Shoaib Sayyed
Posted on November 18, 2022
Quickly create your own NFT using Clarity with Stacks.
Hello Developers 👋
Welcome to today’s post, where we’ll break down a blockchain topic into bite-sized pieces to help you learn and apply your new skills in the real world.
Today’s topic is Creating an NFT with Stacks.
Here’s a list of what we’ll cover 🗒
- ✅ Step 1: Set up your project using Clarinet
- ✅ Step 2: Configure dependencies and define traits
- ✅ Step 3: Create the NFT smart contract
- ✅ Step 4: Check the contracts for errors
- ✅ Step 5: Testing the contract on local chain
- ✅ Step 6: Deploy the NFT smart contracts
By the end of this post, you’ll be able to create and deploy your own NFT using the Stacks.
Let’s go! 🚀
Prerequisites
There are some prerequisite that you should have setup before going into this tutorial ⚒️
- You should have Clarinet installed on your system. Follow the installation guide.
- Setup your Hiro wallet. Download the wallet.
✅ Step 1: Set up your project using Clarinet
We will create a project called music-bird
using Clarinet. Open up your terminal and type the following in the command:
clarinet new music-bird
Once the project is created Open the folder in your favorite IDE (example: VS Code) and open the built-in terminal emulator by going to the Terminal menu and choosing New Terminal.
Now, Inside our project folder, we will create two new Clarity contracts using the following commands:
clarinet contract new nft-trait
clarinet contract new music-bird
Clarinet will create a boilerplate Clarity and test file, and add the contracts to the configuration file.
Creating file contracts/nft-trait.clar
Creating file tests/nft-trait_test.ts
Creating file contracts/music-bird.clar
Creating file tests/music-bird_test.ts
As we are not going to write any test cases in this post, so we can delete the tests
folder.
✅ Step 2: Configure dependencies and define traits
Now that we have the project setup out of the way, lets configure the dependencies and define the traits.
For creating the NFT on stacks we need to follow the Sip 009 NFT standard and its pre-defined traits. We will be writing our NFT contract based on this traits.
To assert that the music-bird
NFT contract implements the trait of SIP-009, we can use the impl-trait
function, as the NFT trait is already deployed on Stacks Mainnet, we can use that deployed contract address when deploying our NFT on mainnet but in this post we are deploying on testnet, so we will configure a NFT traits contract on our local.
To do so, open up the nft-trait.clar
file inside the contracts
directory and add the following traits in it.
(define-trait nft-trait
(
;; Last token ID, limited to uint range
(get-last-token-id () (response uint uint))
;; URI for metadata associated with the token
(get-token-uri (uint) (response (optional (string-ascii 256)) uint))
;; Owner of a given token identifier
(get-owner (uint) (response (optional principal) uint))
;; Transfer from the sender to a new principal
(transfer (uint principal principal) (response bool uint))
)
)
Now we have define the traits, lets configure the dependencies for our NFT smart contract. Open Clarinet.toml
and edit the [contracts.music-bird]
section, we need to add the dependency of nft-trait
contract.
[contracts.music-bird]
path = "contracts/music-bird.clar"
depends_on = ["nft-trait"]
This will ensure that the nft-traits
contract should deployed before our NFT smart contract as it depends on it.
✅ Step 3: Create the NFT smart contract
Now everything is setup, let us jump into writing our NFT contract. Open the music-bird.clar
contracts file and remove all the boilerplate code.
;; Use this when deploying on mainnet
;; (impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
;; Use this when deploying on testnet
;; Implementing traits from the `nft-trait.clar` contract
(impl-trait .nft-trait.nft-trait)
By adding this expression, the analyzer will check if the contract implements the trait specified when the contract is deployed. It will reject the transaction if the contract is not a full implementation. It is therefore recommended to always use impl-trait because it prevent accidental non-conformity.
Now let's define a non fungible token which will take the name of the NFT and its identifier type, Since the SIP requires the asset identifier type to be an unsigned integer, we add our NFT definition next.
(define-non-fungible-token music-bird uint)
The asset identifier for the NFT should be an incrementing unsigned integer. The easiest way to implement it is to increment a counter variable each time a new NFT is minted. Let us define a data variable for it.
(define-data-var last-token-id uint u0)
Now, we will add a constant for the contract deployer and two error codes. Here is everything put together:
;; Implementing traits from the `nft-trait.clar` contract
(impl-trait .nft-trait.nft-trait)
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-token-owner (err u101))
(define-non-fungible-token music-bird uint)
(define-data-var last-token-id uint u0)
To get the token id of last minted NFT, we will define a read only function which will read the chain state and return us the value of last-token-id
variable.
(define-read-only (get-last-token-id)
(ok (var-get last-token-id))
)
After that, we will define one more read only function get-token-uri
which is used to get metadata for the NFT. But in this post we are not going to look that, so we will keep it as none.
(define-read-only (get-token-uri (token-id uint))
(ok none)
)
Now its time to define a public function to mint our NFTs which will take the recipient
address and mint the NFT to that address.
(define-public (mint (recipient principal))
(let
(
(token-id (+ (var-get last-token-id) u1))
)
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(try! (nft-mint? music-bird token-id recipient))
(var-set last-token-id token-id)
(ok token-id)
)
)
In the above mint
function,
- First, we have defined the local variable
token-id
and set its value by incrementing thelast-token-id
value with 1. - After that, we have an
asserts!
function which will validate, is the transaction sender who is calling the mint functiontx-sender
is equal to thecontract-owner
who has deployed the contract or not. - With this check, the
mint
function can only be called by the contract owner, and not by anyone else. - Then we are minting our
music-bird
NFT to the recipient. - Once the NFT is successfully minted, we are updating the
last-token-id
variable with the updated value oftoken-id
and returning the same.
Now let's define a read only function get-owner
which will take the NFT's token-id
and return the owner of the NFT.
(define-read-only (get-owner (token-id uint))
(ok (nft-get-owner? music-bird token-id))
)
And, the final function in our contract will be a public function using which the NFT owner can transfer their NFT to some other address.
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
(begin
(asserts! (is-eq tx-sender sender) err-not-token-owner)
(nft-transfer? music-bird token-id sender recipient)
)
)
In the above transfer
function,
- It will take the NFT's token id, and the sender and recipient addresses.
- Then first we will check transaction sender address and the sender address received in parameter is same or not.
- If it is not same then we will throw an error that the transaction sender is not the owner of the NFT.
- And If the condition satisfies, we will begin to transfer the NFT of token id from the sender address to the recipient address.
✅ Step 4: Check the contracts for errors
Now we have written our NFT contract and it's time to check for errors. Run the command on the terminal:
clarinet check
✅ Step 5: Testing the contract on local chain
Once our code have been checked for errors we can now test our function by running them on local chain, which can easily be possible with clarinet. To do so we just need to run the following command on terminal
clarinet console
Now our console session is running, lets call our mint
function and try to mint a token for ourselves.
The syntax to call a contract from console in Clarinet is:
>> (contract-call? contract-identifier function-name param-1 param-2 ...)
So calling our mint function:
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.music-bird mint 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM)
You can see the NFT mint event and the resulting ok response. We can transfer the newly minted token with ID u1 to a different principal.
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.music-bird transfer u1 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG)
Here as well you can see the NFT transfer event and the resulting ok response. We can confirms that the token is now owned by the specified principal by calling the get-owner
function.
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.music-bird get-owner u1)
✅ Step 5: Deploy the NFT smart contracts
To deploy our NFT smart contract on testnet, open up the Sandbox - Stacks Explorer
- Connect your wallet and Switch to testnet.
- First we need to deploy our NFT trait smart contract
- Paste your NFT trait smart contract code in IDE and Update the contract name.
- Click on Deploy and Confirm the transaction.
- You can view the transaction details in your wallet activity. Click on the transaction in you wallet activity and you will be redirected to Stacks block explorer page.
Now we will repeat the same steps for deploying our NFT smart contract, so first
- Copy and Paste your NFT smart contract code in IDE.
- Copy the contract name of
nft-trait
smart contract from the block explorer page and replace the path inimpl-trait
function in our NFT smart contract.
(impl-trait 'ST2K0T4HFQKJMMCHBYFPRNVBG0B1NZFZTV94RD9WE.nft-trait.nft-trait)
- Update the contract name, click on Deploy and Confirm the transaction.
- Similarly, you can view the transaction details in your wallet activity
- To view more details about the deployment, click on the transaction in you wallet activity.
That is all there is to it! NFTs in Clarity are really quite easy to do. The full source code of the project can be found here: https://github.com/0xShoaib/music-bird-nft
Posted on November 18, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.