Skip to content

Non-Fungible Tokens (NFTs)

This page lists common examples of working with NFTs.

Accepting NFT ownership assignment

Notification message of assigned NFT ownership has the following structure:

message(0x05138d91) NFTOwnershipAssigned {
previousOwner: Address;
forwardPayload: Slice as remaining;
}
▶️ Open in Web IDE

Use receiver function to accept notification message.

Validation can be done in two ways:

  1. Directly store the NFT item address and validate against it.
import "@stdlib/deploy";
message(0x05138d91) NFTOwnershipAssigned {
previousOwner: Address;
forwardPayload: Slice as remaining;
}
contract SingleNft with Deployable {
nftItemAddress: Address;
init(nftItemAddress: Address) {
self.nftItemAddress = nftItemAddress;
}
receive(msg: NFTOwnershipAssigned) {
require(self.nftItemAddress == sender(), "NFT contract is not the sender");
// your logic of processing nft ownership assign notification
}
}
▶️ Open in Web IDE
  1. Use StateInit and derived address of the NFT item.
import "@stdlib/deploy";
message(0x05138d91) NFTOwnershipAssigned {
previousOwner: Address;
forwardPayload: Slice as remaining;
}
struct NFTItemInitData {
index: Int as uint64;
collectionAddress: Address;
}
inline fun calculateNFTAddress(index: Int, collectionAddress: Address, nftCode: Cell): Address {
let initData = NFTItemInitData{
index,
collectionAddress,
};
return contractAddress(StateInit{code: nftCode, data: initData.toCell()});
}
contract NftInCollection with Deployable {
nftCollectionAddress: Address;
nftItemIndex: Int as uint64;
nftCode: Cell;
init(nftCollectionAddress: Address, nftItemIndex: Int, nftCode: Cell) {
self.nftCollectionAddress = nftCollectionAddress;
self.nftItemIndex = nftItemIndex;
self.nftCode = nftCode;
}
receive(msg: NFTOwnershipAssigned) {
let expectedNftAddress = calculateNFTAddress(self.nftItemIndex, self.nftCollectionAddress, self.nftCode); // or you can even store expectedNftAddress
require(expectedNftAddress == sender(), "NFT contract is not the sender");
// your logic of processing nft ownership assign notification
}
}
▶️ Open in Web IDE

Since the initial data layout of the NFT item can vary, the first approach is often more suitable.

Transferring NFT item

To send NFT item transfer use send() function.

import "@stdlib/deploy";
message(0x5fcc3d14) NFTTransfer {
queryId: Int as uint64;
newOwner: Address; // address of the new owner of the NFT item.
responseDestination: Address; // address where to send a response with confirmation of a successful transfer and the rest of the incoming message coins.
customPayload: Cell? = null; // optional custom data. In most cases should be null
forwardAmount: Int as coins; // the amount of nanotons to be sent to the new owner.
forwardPayload: Slice as remaining; // optional custom data that should be sent to the new owner.
}
contract Example {
nftItemAddress: Address;
init(nftItemAddress: Address) {
self.nftItemAddress = nftItemAddress;
}
// ... add more code from previous examples ...
receive("transfer") {
send(SendParameters{
to: self.nftItemAddress,
value: ton("0.1"),
body: NFTTransfer{
queryId: 42,
// FIXME: Change this according to your needs
newOwner: sender(),
responseDestination: myAddress(),
customPayload: null,
forwardAmount: 1,
forwardPayload: rawSlice("F"), // precomputed beginCell().storeUint(0xF, 4).endCell().beginParse()
}.toCell(),
});
}
}
▶️ Open in Web IDE

Get NFT static info

Note, that TON Blockchain does not allow contracts to call each other getters. In order to receive data from another contract, you must exchange messages.

message(0x2fcb26a2) NFTGetStaticData {
queryId: Int as uint64;
}
message(0x8b771735) NFTReportStaticData {
queryId: Int as uint64;
index: Int as uint256;
collection: Address;
}
struct NFTItemInitData {
index: Int as uint64;
collectionAddress: Address;
}
inline fun calculateNFTAddress(index: Int, collectionAddress: Address, nftCode: Cell): Address {
let initData = NFTItemInitData{
index,
collectionAddress,
};
return contractAddress(StateInit{code: nftCode, data: initData.toCell()});
}
contract Example {
nftCollectionAddress: Address;
nftItemIndex: Int as uint64;
nftCode: Cell;
init(nftCollectionAddress: Address, nftItemIndex: Int, nftCode: Cell) {
self.nftCollectionAddress = nftCollectionAddress;
self.nftItemIndex = nftItemIndex;
self.nftCode = nftCode;
}
// ... add more code from previous examples ...
receive("get static data") {
// FIXME: Put proper address("[NFT_ADDRESS]") here
let nftAddress = sender();
send(SendParameters{
to: nftAddress,
value: ton("0.1"),
body: NFTGetStaticData{
queryId: 42,
}.toCell(),
});
}
receive(msg: NFTReportStaticData) {
let expectedNftAddress = calculateNFTAddress(msg.index, msg.collection, self.nftCode);
require(expectedNftAddress == sender(), "NFT contract is not the sender");
// Save nft static data or do something
}
}
▶️ Open in Web IDE

Get NFT royalty params

NFT royalty params are described here.

message(0x693d3950) NFTGetRoyaltyParams {
queryId: Int as uint64;
}
message(0xa8cb00ad) NFTReportRoyaltyParams {
queryId: Int as uint64;
numerator: Int as uint16;
denominator: Int as uint16;
destination: Address;
}
contract Example {
nftCollectionAddress: Address;
init(nftCollectionAddress: Address) {
self.nftCollectionAddress = nftCollectionAddress;
}
// ... add more code from previous examples ...
receive("get royalty params") {
send(SendParameters{
to: self.nftCollectionAddress,
value: ton("0.1"),
body: NFTGetRoyaltyParams{
queryId: 42,
}.toCell(),
});
}
receive(msg: NFTReportRoyaltyParams) {
require(self.nftCollectionAddress == sender(), "NFT collection contract is not the sender");
// Do something with msg
}
}
▶️ Open in Web IDE

NFT Collection methods

Note that only NFT owners are allowed to use these methods.

Deploy NFT

message(0x1) NFTDeploy {
queryId: Int as uint64;
itemIndex: Int as uint64;
amount: Int as coins; // amount to sent when deploying nft
nftContent: Cell;
}
contract Example {
nftCollectionAddress: Address;
init(nftCollectionAddress: Address) {
self.nftCollectionAddress = nftCollectionAddress;
}
// ... add more code from previous examples ...
receive("deploy") {
send(SendParameters{
to: self.nftCollectionAddress,
value: ton("0.14"),
body: NFTDeploy{
queryId: 42,
itemIndex: 42,
amount: ton("0.1"),
nftContent: beginCell().endCell() // FIXME: Should be your content, mostly generated offchain
}.toCell(),
});
}
}
▶️ Open in Web IDE

Change owner

message(0x3) NFTChangeOwner {
queryId: Int as uint64;
newOwner: Address;
}
contract Example {
nftCollectionAddress: Address;
init(nftCollectionAddress: Address) {
self.nftCollectionAddress = nftCollectionAddress;
}
// ... add more code from previous examples ...
receive("change owner") {
send(SendParameters{
to: self.nftCollectionAddress,
value: ton("0.05"),
body: NFTChangeOwner{
queryId: 42,
// FIXME: Put proper address("NEW_OWNER_ADDRESS") here
newOwner: sender(),
}.toCell(),
});
}
}
▶️ Open in Web IDE