Tezos NFT Standard

The following standard outlines a smart contract interface required to enable Non Fungible Tokens on the Tezos blockchain - along with integration examples.

Proposed Interface

type nftId is nat;

// @remarks Meta data will be added to this record.
type nft is record [
owner : address;
]

type nfts is map(nftId, nft);

type storageType is nfts;

type actionMint is record [
nftToMintId : nftId;
nftToMint : nft;
]

type actionTransfer is record [
nftToTransfer : nftId;
destination : address;
]

type actionBurn is record [
nftToBurnId : nftId;
]

type action is
| Mint of actionMint
| Transfer of actionTransfer
| Burn of actionBurn

// Mints a new NFT by creating a new entry in the contract.
// @param nftToMintId - ID of the NFT
// @param nftToMint - The NFT data structure
function mint(const action : actionMint ; const s : storageType) : (list(operation) * storageType) is
block { skip } with ((nil: list(operation)) , s)

// Transfers the ownership of an NFT by replacing the owner address.
// @param nftToTransfer - ID of the NFT
// @param destination - Address of the recipient
function transfer(const action : actionTransfer ; const s : storageType) : (list(operation) * storageType) is
block { skip } with ((nil: list(operation)) , s)

// Burns an NFT by removing its entry from the contract.
// @param nftToBurnId - ID of the NFT
function burn(const action : actionBurn ; const s : storageType) : (list(operation) * storageType) is
block { skip } with ((nil: list(operation)) , s)

// @remarks In v004 Athens, Michelson does not support multiple entrypoints. This is solved 
// in Ligo through variants and pattern matching.
// @param Any of the action types defined above.
function main(const action : action; const s : storageType) : (list(operation) * storageType) is 
block {skip} with 
case action of
| Mint (mt) -> mint (mt, s)
| Transfer (tx) -> transfer (tx, s)
| Burn (bn) -> burn (bn, s)
end
                    

Usage examples

Contract deployment

# Compile the LIGO contract
$ ligo compile-contract nft.ligo main > nft.tz

# Compile the LIGO storage initial values into a Michelson representation
$ ligo compile-storage nft.ligo main 'map 1n -> record owner = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); end end'
{ Elt 1 "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" }

# Originate the compiled contract using the tezos-client
$ tezos-client originate contract nft for alice transferring 0 from alice running nft.tz --init '"{ Elt 1 \"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx\" }"'

                    

Transfering ownership of an existing token

# Compile the LIGO transaction parameter into it's Michelson representation
$ ligo compile-parameter nft.ligo main 'Transfer(record nftToTransfer = 1n ; destination = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) end)'
(Right (Pair "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" 1))

# Invoke the contract by a parametrized transaction
$ tezos-client transfer 0 from alice to nft --arg '(Right (Pair \"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx\" 1))'
                    

Minting of a new token

# Compile the LIGO transaction parameter into it's Michelson representation
$ ligo compile-parameter nft.ligo main 'Mint(record nftToMintId = 2n ; nftToMint = record owner = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); end; end)'
(Left (Right (Pair "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" 2)))

# Invoke the contract by a parametrized transaction
$ tezos-client transfer 0 from alice to nft --arg '(Left (Right (Pair \"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx\" 2)))'
                    

Burning of an existing token

# Compile the LIGO transaction parameter into it's Michelson representation
$ ligo compile-parameter nft.ligo main 'Burn(record nftToBurnId = 1n end)'
(Left (Left 1))

# Invoke the contract by a parametrized transaction
$ tezos-client transfer 0 from alice to nft --arg '(Left (Left 1))'
                    

SDK

You can find more SDK usage examples here.

Installation

# Currently the SDK is shipped as a Typescript package
npm i --save @stove-labs/tezos-nft-sdk@pre-alpha
                    

Getting all tokens

// Setup the SDK & retrieve all existing tokens
import { TezosNFT, Config } from "@stove-labs/tezos-nft-sdk";

const config: Config = {
    serverAddress: "http://localhost:8732",
    contractAddress: "KT1J4g5sm71ynvr5SevBCU5udzE759SonDvY"
};

const nft = new TezosNFT(config);

nft.getAllTokens()
    .then(tokens => console.log("tokens", tokens));
                    

FAQ


Can we have more metadata?

One of the trickiest challenges when designing NFT contracts is, how you deal with metadata. Data that does not need to be accesed on-chain within other contracts, could be serialized, or stored externally via decentralized data distribution solutions such as IPFS.

Will the standard implementation support proto005 Michelson entrypoints?

Yes, native Michelson entrypoints will be supported, as soon as they're available in the reference implementation language (LIGO).

Is this the final feature set?

No, because you can combine features as needed and deviate from the reference contract implementation as seen above. Further examples will follow.

How can wallets integrate the NFT standard?

Apart from the usage examples with tezos-client, there'll be an SDK for wallet integrations - once the implementation of the contract is done. This will allow wallet developers to integrate NFT support.

Are there any projects using the standard?

Yes there are, check out the Partners & Projects section below.

Can a 3rd party smart contract, interact with the NFT contract?

Yes, especially when you delegate asset ownership to a smart contract.

What is LIGO?

LIGO is a smart contract programing language for the Tezos blockchain, you can learn more about it here.

Partners & Projects