Getting Started & Prerequisites
This article assumes you understand Sui Move and have already installed your development environment with an IDE and Sui CLI. If you don’t, check out this article to get set up Once you’re all set up, create a new project with this command:[Terminal]
[sui.move]
Building an NFT on Sui
Before defining your structs, you need to understand your aim and your NFT collection’s traits, habits, and features. In this tutorial, we want an NFT collection with life, so we’d give it an account. The NFT collection should be able to hold assets. This is great if you want to create an NFT collection with revenue or airdrops to holders.[sui.move]
GOODYLILI_NFT
struct represents the NFT collection with all the necessary fields, including a balance
field. Notice that the struct has the key
and store
abilities.
The NFTMinted
struct is for emitting events whenever a new NFT is minted.
Now, you’ll need an init
function that will execute once to initialize traits and claim the publisher capability. The publisher capability allows you admin controls over the NFT collection.
[sui.move]
publisher
variable has this capability, and once the init function runs, it will be sent to the address that deploys the contract.
You’ll also need a mint function to mint new NFTs into circulation. Here’s how to declare one.
[sui.move]
[sui.move]
coin_balance
is the balance of the Coin<T>
passed in by the caller, stored in the object. Then, the paid
is a split off the amount from the balance. The balance::join
function joins the NFT’s balance with the amount paid.
You’ll also need a function for holders to withdraw funds from the NFT.
[sui.move]
balance::split
function to split the amount from the NFT’s balance before sending the funds to the transaction sender.
Launching NFTs with Kiosk
Sui provides kiosks that are more ergonomic than on-chain assets. It’s like opening a brand for items, and then you get to specify and enforce policies over the items. Many of your favourite NFT collections, including Prime Machin and Rootlets, use Kiosk. First, you need to add all these imports to your package.[sui.move]
transfer_policy
, which is the real star of this section.
The transfer_policy
module defines how assets can be transferred and enforces rules on those transfers.
Here’s a table of the transfer policy features:
Feature | What it means |
---|---|
TransferPolicy<T> | A shared object that defines the rules for transferring type T |
TransferPolicyCap<T> | A capability (object) that lets you modify the policy — only the holder can change or add rules |
TransferRequest<T> | An object created whenever someone tries to transfer type T . They must fulfill the policy rules before the transfer is finalized |
add_rule(...) | Adds a custom rule (like “pay 1 SUI”) to the policy |
add_to_balance(...) | Lets you collect fees or payments tied to transfers |
add_receipt(...) | Marks a rule as fulfilled for a given transfer |
confirm_request(...) | Finalizes a transfer if all rules are met |
[sui.move]
[sui.move]
Rule
, and an empty Config
. We’ll plug this into the policy later.
Here’s a basic function to mint a new Art
NFT:
[sui.move]
[sui.move]
transfer_policy::new
creates the policy and its capability (cap
)- The policy is made shared so everyone can access it
- The cap is transferred to the caller so only they can manage the policy
[sui.move]
Rule
) and its config. This function can only be called by whoever holds the TransferPolicyCap
.
Here’s the critical part: enforcing a 1 SUI transfer fee:
[sui.move]
- It checks the coin’s value is exactly 1 SUI (in Mist)
- Adds that payment to the policy’s internal balance
- Marks the
TransferRequest
as passed for this rule
[sui.move]
confirm_request()
, the item is stuck in limbo. That’s why TradePort asks recipients to claim from Kiosks.