cw-nfts CosmWasm NFT Standards

repository·main·Indexed 41 days ago

https://github.com/public-awesome/cw-nfts

A collection of CosmWasm NFT standards and implementation examples. Includes cw721-base for core NFT functionality, cw2981-royalties for token-level royalty payments, cw721-expiration for time-limited NFTs, cw721-fixed-price for CW20-based minting, and cw721-metadata-onchain for storing OpenSea-standard metadata directly on-chain.

Tokens
27.9K
Snippets
79
Records
125
Agent score
50%

What's inside cw-nfts

  1. Overview of cw721-base

    main

    The cw721-base contract is a basic implementation of the CW721 NFT standard. It is designed to be deployed as a standalone contract or imported into other contracts to build custom CW721-compatible NFTs.

    It implements:

    • CW721 Base: The core NFT functionality.
    • Metadata extension: Support for token metadata.
    • Enumerable extension: Support for iterating over tokens.

    Key features include a Minter role, which is a specific address authorized to mint new tokens via ExecuteMsg::Mint but lacks permission to modify existing ones.

  2. Understand the CW721 specification and package structure

    main

    CW721 is a CosmWasm specification for Non-Fungible Tokens (NFTs) inspired by Ethereum's ERC721. The specification is modular: a contract may implement only parts of it, but must implement the base functionality.

    The cw721 package provides two core traits with default implementations (utilities):

    • Cw721Execute (via Cw721ExecuteMsg): Handles minting, burning, sending, approving, and transferring NFTs, as well as managing collection info and ownership.
    • Cw721Query (via Cw721QueryMsg): Handles queries for NFTs, tokens, approvals, and ownership (creator and minter).

    cw721-base is a lightweight implementation that handles ownership, transfers, and allowances. It supports both off-chain metadata (via token_uri) and on-chain metadata (via NftInfo's extension).

  3. Implement CW-2981 Token-level Royalties

    main
    The cw2981-royalties contract implements the EIP-2981 standard for token-level royalties in CosmWasm. It builds on the metadata pattern found in cw721-metadata-onchain. Unlike global royalty settings, this contract allows royalty information to be attached to individual tokens at the time of minting. This is useful for marketplaces that need to determine if a specific NFT sale requires a royalty payment and to whom.
  4. How to implement on-chain metadata using CW721-Base extensions

    main

    To store NFT metadata directly on-chain (rather than via a URI), you can leverage the extension: T generic field provided by cw721-base. This allows you to define a custom data structure that is included during the minting process and made available in all subsequent queries.

    In the cw721-metadata-onchain implementation, the metadata follows the OpenSea Metadata Standard. The custom data is encapsulated in a Metadata struct which is then used as the Extension type.

    #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
    pub struct Trait {
        pub display_type: Option<String>,
        pub trait_type: String,
        pub value: String,
    }
    
    #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
    pub struct Metadata {
        pub image: Option<String>,
        pub image_data: Option<String>,
        pub external_url: Option<String>,
        pub description: Option<String>,
        pub name: Option<String>,
        pub attributes: Option<Vec<Trait>>,
        pub background_color: Option<String>,
        pub animation_url: Option<String>,
        pub youtube_url: Option<String>,
    }
    
    pub type Extension = Option<Metadata>;
  5. Understand the `cw721` extension model

    main

    The cw721 package provides a generic framework for building NFT contracts using different extension models. Extensions allow you to define how metadata and custom logic are handled. These extensions implement the Cw721Query and Cw721Execute traits, which provide default implementations for standard operations that can be customized.

    Available extension models:

    • Cw721OnchainExtensions: For storing metadata onchain.
    • Cw721BaseExtensions: For offchain NFT extensions and optional onchain collection extensions.
    • Cw721EmptyExtensions: For no extensions.
    • Cw721Extensions: For custom metadata and logic.
  6. Customizing minting with the Minter role

    main

    In cw721-base, minting is controlled by a designated Minter address set during instantiation.

    • InstantiateMsg: Requires name, symbol, and a minter address.
    • ExecuteMsg::Mint{token_id, owner, token_uri}: Creates a new token. This message can only be called by the address defined as the minter in the InstantiateMsg.
    • QueryMsg::Minter{}: Returns the current minter address.

    The Minter can be an external actor (like a web server) or another smart contract. If you want to customize minting logic while keeping standard NFT functionality, you can either extend this contract or have a custom contract act as the Minter.

  7. How CW721 Expiration works

    main

    The cw721-expiration contract allows for the creation of NFTs that expire after a predetermined number of days. The expiration timestamp is calculated as: mint timestamp + expiration days.

    This contract is useful for services with limited access, such as access cards, SLAs, or cloud services. The expiration_days value is set during the contract instantiation.

  8. Implement the CW721Receiver interface

    main

    To manage tokens sent via SendNft, a contract must implement the ReceiveNft interface. This is the counterpart to the SendNft message.

    ReceiveNft{sender, token_id, msg}:

    • sender: The original account that requested the token move.
    • token_id: The ID of the NFT.
    • msg: A Binary payload that can be decoded into contract-specific instructions (e.g., a ReceiveMsg variant specifying a listing price for an exchange).

    Security Note: The recipient contract should verify that the sender matches the expected token contract and does not allow arbitrary addresses to trigger logic.

  9. Import cw721-base into custom contracts

    main

    You can import cw721-base to build domain-specific NFTs (e.g., real estate or gaming assets) while maintaining CW721 compliance.

    Instead of rewriting the standard logic, you implement your own ExecuteMsg and QueryMsg and dispatch standard CW721 calls to the underlying implementation. For example, you would import and call cw721_base::contract::handle_transfer within your own handle function. This allows you to combine custom logic with standard NFT behaviors like transfers and ownership queries.

  10. Compile and test the CW721 Expiration contract

    main

    Use the following commands for development tasks:

    Build a development build:

    cargo build

    Build an optimized build (using Docker):

    docker run --rm -v "$(pwd)":/code \
      --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
      --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
      cosmwasm/rust-optimizer:0.16.0

    Run unit tests:

    cargo test

    Format code:

    cargo fmt
  11. Compile and build cw721-base

    main

    To build the contract, you need Rust 1.81+ and the wasm32-unknown-unknown target installed.

    To compile the contract to WebAssembly (WASM) for deployment:

    RUSTFLAGS='-C link-arg=-s' cargo wasm
    cp ../../target/wasm32-unknown-unknown/release/cw721_base.wasm .

    For production-ready optimized builds, use the build commands provided by the cw-plus repository.