Back to Sui

Soulbound NFT Example

docs/content/guides/developer/digital-assets/examples-patterns/soulbound-tokens.mdx

latest2.8 KB
Original Source

A soulbound NFT is an NFT that is non-transferable. After an NFT is minted to a Sui account, the NFT is bound to that account and cannot be transferred. This implementation leverages the custom logic of the Sui framework's transfer functions. The <UnsafeLink href="/references/framework/sui-framework/sui_sui/transfer">sui::transfer module</UnsafeLink> contains 2 functions that transfer objects: <UnsafeLink href="/references/framework/sui-framework/sui_sui/transfer#function-transfer">transfer::transfer</UnsafeLink> and <UnsafeLink href="/references/framework/sui-framework/sui_sui/transfer#function-public_transfer">transfer::public_transfer</UnsafeLink>.

Typically, when defining new NFTs or object types on Sui, you don't need to create a transfer function because the Sui Framework offers transfer::public_transfer which anyone can use to transfer objects. However, transfer::public_transfer requires that transferred objects have the key and store ability. Therefore, if you define a new NFT type that has the key ability, meaning it is a Sui object, but not the store ability, the holders cannot use transfer::public_transfer. This results in a soulbound NFT.

You can also create custom transfer logic for NFTs on Sui. The transfer::transfer function has custom rules performed by the Sui Move bytecode verifier that ensures that the transferred objects are defined in the module where transfer is invoked. While removing the store ability from a struct definition makes transfer::public_transfer unusable, transfer::transfer can still be used as long as you use it in the module that defined that object's type. This allows the module owner to provide custom transfer logic for their soulbound NFTs.

Example

The following example creates a basic soulbound NFT on Sui. The TestnetSoulboundNFT struct defines the NFT with id, name, description, and url fields.

<ImportContent source="examples/move/nft-soulbound/sources/testnet_soulbound_nft.move" mode="code" struct="TestnetSoulboundNFT" noComments />

This TestnetSoulboundNFT struct is defined with the key ability but without the store ability. This means you cannot transfer it with transfer::public_transfer. Instead, use transfer::transfer with custom transfer logic implemented in the same module.

This example also shows how to provide custom transfer logic using the transfer::transfer function. This is where you can add additional logic, such as resetting the NFT's stats or requiring a payment. Don't provide this functionality if the NFT is fully soulbound.

<ImportContent source="examples/move/nft-soulbound/sources/testnet_soulbound_nft.move" mode="code" fun="transfer" /> <details> <summary>

testnet_soulbound_nft.move

</summary> <ImportContent source="examples/move/nft-soulbound/sources/testnet_soulbound_nft.move" mode="code" /> </details>