Non-Fungible Token
In the world of digital assets, not all tokens are alike. This becomes important in situations like real estate, voting rights, or collectibles, where some items are valued more than others due to their usefulness, rarity, etc. On Stellar, you can create non-fungible tokens (NFTs), where each token is unique and represents something distinct, with ownership tracked through Soroban smart contracts.
Overview
The non-fungible module provides three different NFT variants that differ in how certain features like ownership tracking, token creation and destruction are handled:
-
Base: Contract variant that implements the base logic for the NonFungibleToken interface. Suitable for most use cases.
-
Consecutive: Contract variant for optimized minting of batches of tokens. Builds on top of the base variant, and overrides the necessary functions from the
Base
variant. -
Enumerable: Contract variant that allows enumerating the tokens on-chain. Builds on top of the base variant, and overrides the necessary functions from the
Base
variant.
These three variants share core functionality and a common interface, exposing identical contract functions as entry-points. However, composing custom flows must be handled with extra caution. That is required because of the incompatible nature between the business logic of the different NFT variants or the need to wrap the base functionality with additional logic.
Usage
We’ll use an NFT to track game items, each having their own unique attributes. Whenever one is to be
awarded to a player, it will be minted and sent to them. Players are free to keep or burn their token or
trade it with other people as they see fit. Please note any account can call award_item
and we might
want to implement access control to restrict who can mint.
Here’s what a contract for tokenized items might look like:
use soroban_sdk::{contract, contractimpl, Address, Env, String};
use stellar_default_impl_macro::default_impl;
use stellar_non_fungible::{
burnable::NonFungibleBurnable,
Base, ContractOverrides, NonFungibleToken,
};
#[contract]
pub struct GameItem;
#[contractimpl]
impl GameItem {
pub fn __constructor(e: &Env) {
Base::set_metadata(
e,
String::from_str(e, "www.mygame.com"),
String::from_str(e, "My Game Items Collection"),
String::from_str(e, "MGMC"),
);
}
pub fn award_item(e: &Env, to: Address) -> u32 {
// access control might be needed
Base::sequential_mint(e, &to)
}
}
#[default_impl]
#[contractimpl]
impl NonFungibleToken for GameItem {
type ContractType = Base;
}
#[default_impl]
#[contractimpl]
impl NonFungibleBurnable for GameItem {}
Extensions
The following optional extensions are provided to enhance capabilities:
- Burnable
The NonFungibleBurnable
trait extends the NonFungibleToken
trait to provide the capability to burn tokens.
- Consecutive
The NonFungibleConsecutive
extension is optimized for batch minting of tokens with consecutive IDs. This approach drastically reduces storage writes during minting by storing ownership only at boundaries and inferring ownership for other tokens. See Non-Fungible Consecutive for detailed documentation.
This extension is build around the contract variant Consecutive
. Here is an example usage:
- Enumerable
The NonFungibleEnumerable
extension enables on-chain enumeration of tokens owned by an address. See Non-Fungible Enumerable for detailed documentation.
This extension is build around the contract variant Enumerable
. Here is an example usage:
- Royalties
The NonFungibleRoyalties
trait extends the NonFungibleToken
trait to provide royalty information for tokens, similar to ERC-2981 standard. This allows marketplaces to query royalty information and pay appropriate fees to creators.
Note: The royalties extension allows both collection-wide default royalties and per-token royalty settings.