diff --git a/x/programs/rust/examples/nft/src/lib.rs b/x/programs/rust/examples/nft/src/lib.rs index 15d266c3ec..d8aa0b2c19 100644 --- a/x/programs/rust/examples/nft/src/lib.rs +++ b/x/programs/rust/examples/nft/src/lib.rs @@ -1,28 +1,83 @@ -use wasmlanche_sdk::{public, store::State}; - +//! A basic ERC-721 compatible contract. +//! Serves as a non-fungible token with the ability to mint, burn, and pause. +//! Only supports whole units with no decimal places. +//! +//! NOTE: The NFT must support the common NFT metadata format. +//! This JSON encoded file provides all the necessary metadata about the NFT. +use metadata::{Properties, TypeDescription, NFT}; +use wasmlanche_sdk::{program::Program, public, state::State, state_keys, types::Address}; pub mod metadata; -/// A basic ERC-721 compatible contract. -/// Serves as a non-fungible token with the ability to mint, burn, and pause. -/// Only supports whole units with no decimal places. -/// -/// NOTE: The NFT must support the common NFT metadata format. -/// This JSON encoded file provides all the necessary metadata about the NFT. +const NFT_NAME: &str = "My NFT"; +const NFT_SYMBOL: &str = "MNFT"; /// Initializes the program with a name, symbol, and total supply /// Returns whether the initialization was successful. -// TODO: Parametrize string inputs #[public] -pub fn init(state: State) -> bool { - state - .store_value("total_supply", &10000_i64) - .store_value("name", "NFToken") - .store_value("symbol", "NFT") - .is_ok() +pub fn init(program: Program) -> bool { + // set token name + program + .state() + .store(StateKey::Name.to_vec(), &NFT_NAME.as_bytes()) + .expect("failed to store coin name"); + + // set token symbol + program + .state() + .store(StateKey::Symbol.to_vec(), &NFT_SYMBOL.as_bytes()) + .expect("failed to store symbol"); + + let nft_metadata = metadata::NFT::new() + .with_title("My NFT".to_string()) + .with_type("object".to_string()) + .with_properties(Properties::new() + .with_name(TypeDescription::new() + .with_type(NFT_SYMBOL.to_string()) + .with_description("Identifies the asset to which this NFT represents".to_string()) + ) + .with_description(TypeDescription::new() + .with_type(NFT_NAME.to_string()) + .with_description("Describes the asset to which this NFT represents".to_string()) + ) + .with_image(TypeDescription::new() + .with_type("ipfs://bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam/nft-image.png".to_string()) + .with_description("A URI pointing to a resource with mime type image".to_string()) + )); + + // set total supply + program + .state() + .store(StateKey::TotalSupply.to_vec(), &20) + .expect("failed to store total supply"); + + // TODO: convert nft_metadata struct into bytes to persist to storage. + // program + // .state() + // .store(StateKey::Metadata.to_vec(), nft_metadata) + // .expect("failed to store total supply"); + + true } /// Returns the total supply of the token. #[public] -pub fn get_total_supply(state: State) -> i64 { - state.get_value("total_supply").unwrap_or(0) +pub fn get_total_supply(program: Program) -> i64 { + 0 +} + +/// The program state keys. +#[state_keys] +enum StateKey { + /// The total supply of the token. Key prefix 0x0. + TotalSupply, + /// The name of the token. Key prefix 0x1. + Name, + /// The symbol of the token. Key prefix 0x2. + Symbol, + /// Metadata of the token + Metadata, + /// Owner address + Owner, + /// Edition (for example, edition 1 of 10) + Edition, } diff --git a/x/programs/rust/examples/nft/src/metadata.rs b/x/programs/rust/examples/nft/src/metadata.rs index 3dfba37189..b4b6650fa5 100644 --- a/x/programs/rust/examples/nft/src/metadata.rs +++ b/x/programs/rust/examples/nft/src/metadata.rs @@ -1,32 +1,125 @@ //! NFT schema +//! See https://nftschool.dev/reference/metadata-schemas/#ethereum-and-evm-compatible-chains for more information +//! on the ERC-721 NFT metadata schema. use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] -pub struct Metadata { +pub struct NFT { pub title: String, pub r#type: String, - pub properties: AssetMetadata, + pub properties: Properties, +} + +impl NFT { + pub fn new() -> Self { + Self { + title: "".to_string(), + r#type: "".to_string(), + properties: Properties { + name: TypeDescription { + r#type: "".to_string(), + description: "".to_string(), + }, + description: TypeDescription { + r#type: "".to_string(), + description: "".to_string(), + }, + image: TypeDescription { + r#type: "".to_string(), + description: "".to_string(), + }, + }, + } + } + + pub fn with_title(mut self, title: String) -> Self { + self.title = title; + self + } + + pub fn with_type(mut self, r#type: String) -> Self { + self.r#type = r#type; + self + } + + pub fn with_properties(mut self, properties: Properties) -> Self { + self.properties = properties; + self + } } #[derive(Serialize, Deserialize)] -pub struct AssetMetadata { +pub struct Properties { pub name: TypeDescription, pub description: TypeDescription, pub image: TypeDescription, } +impl Properties { + pub fn new() -> Self { + Self { + name: TypeDescription { + r#type: "".to_string(), + description: "".to_string(), + }, + description: TypeDescription { + r#type: "".to_string(), + description: "".to_string(), + }, + image: TypeDescription { + r#type: "".to_string(), + description: "".to_string(), + }, + } + } + + pub fn with_name(mut self, name: TypeDescription) -> Self { + self.name = name; + self + } + + pub fn with_description(mut self, description: TypeDescription) -> Self { + self.description = description; + self + } + + pub fn with_image(mut self, image: TypeDescription) -> Self { + self.image = image; + self + } +} + #[derive(Serialize, Deserialize)] pub struct TypeDescription { pub r#type: String, pub description: String, } +impl TypeDescription { + pub fn new() -> Self { + Self { + r#type: "".to_string(), + description: "".to_string(), + } + } + + pub fn with_type(mut self, r#type: String) -> Self { + self.r#type = r#type; + self + } + + pub fn with_description(mut self, description: String) -> Self { + self.description = description; + self + } +} + #[test] fn test_schema() { - let metadata = Metadata { + let metadata = NFT { title: "My NFT Metadata".to_string(), r#type: "object".to_string(), - properties: AssetMetadata { + properties: Properties { name: TypeDescription { r#type: "MNFT".to_string(), description: "Identifies the asset to which this NFT represents".to_string(),