Comment on page
How to deploy an NFT token
Create a unique ERC721-equivalent token (ie: CryptoKitties) on TomoChain!
This article will explain:
- What is a Non-Fungible Token (NFT)
- Use-cases of NFT
- How-to step-by-step deploy a NFT token on TomoChain
What is a Non-Fungible Token (NFT)?
Fungible tokens are all equal and interchangeable. For instance, dollars or Bitcoins or 1 kilogram of pure gold or ERC20 tokens. All TOMO are equivalent too, they are the same and have the same value. They are interchangeable 1:1. This is a fungible token.
Non-fungible tokens (NFTs) are all distinct and special. Every token is rare, with unique atributes and different value. For instance: CryptoKitty tokens, collectible cards, airplane tickets or real art paintings. Every item has its own characteristics and specifics and is clearly differentiable to another one. They are not interchangeable 1:1. They are distinguishable.
Think of Non-Fungible Tokens (NFT) as a rare collectible on the TomoChain network. Every token has unique characteristics, its own metadata and special attributes
Non-Fungible Tokens (NFT) are used to create verifiable digital scarcity. NFTs are unique and distinctive tokens that you can mainly find on EVM blockchains.

The ERC-721 is the standard interface for Non-Fungible Tokens (but there are also other NFTs, like ERC1155). ERC721 is a set of rules to make your NFT easy for other people / apps / contracts to interface with.
ERC721 is a free, open standard that describes how to build non-fungible or unique tokens on EVM compatible blockchains. While most tokens are fungible (not distinguishable), ERC721 tokens are all unique, with individual identities and properties. Think of them like rare, one-of-a-kind collectables — each unit is a unique item with its own serial number.
ERC20: identical tokens. ERC721: unique tokens
Some high demand non-fungible tokens are applications like CryptoKitties, Decentraland, CryptoPunks, and many others.
At the end of 2017, NFTs made a remarkable entrance in the blockchain world with the success of CryptoKitties. Each one is a unique collectible item, with its own serial number, which can be compared to its DNA card. This unleashed an unprecedented interest for NFTs, that went so far as to clog the Ethereum network. The CryptoKitties market alone generated $12 million dollars in two weeks after its launch, and over $25 million in total. Some rare cryptokitties were even sold for 600 ETH ($170,000).
The strength of NFTs resides in the fact that each token is unique and cannot be mistaken for another one– unlike bitcoins, for example, which are interchangeable with one another.

One step further in the non-fungible token space is the ERC-1155 Standard proposed by the Enjin team, also known as the “Crypto Item Standard”. This is an improved version of ERC-721 which will actually be suitable for platforms where there are tens of thousands of digital items and goods.
Online games can have up to 100,000 different digital items. The current problem with ERC-721 is that if we would like to tokenize all those 100,000 items, then we would need to deploy 100,000 separate smart contracts.
ERC-1155 standard combines ERC-20 and ERC-721 tokens in its smart contract. Each token is saved in the contract with a minimal set of data that distinguishes it from others. This allows for the creation of bigger collections which contain multiple different items.
Most of the time when people think about ERC-721 or NFT, they refer to the most notably successful CryptoKitties. But there are many other usability applications for NFT contracts:
- Software titles or software licences to guarantee anti-piracy, privacy and transferability — like Collabs.io
- Betting in real time on the outcome of a video game being live-streamed
- Gaming in general is an important field of experimentation and development for the uses of NFT in the future.
- Concert tickets and sports match tickets can be tokenized and name-bound, preventing fraud and at the same time offering fans an option to have a single place where to collect all their past event experiences
- Digital Art (or physical art!) has already entered the game and showed an important usage of ERC721. Digital art auctions were the first application and still are the first thought of non-fungible token standards. The auctions organized by Christie’s revealed the appeal of the public for crypto-collectibles. Several digital art assets were sold during this event, the high point being the sale of the ‘Celestial Cyber Dimension’, an ERC721 CryptoKitty piece of art, for $140,000
- Real Estate assets, to carry out transfers of houses, land and other ‘tokenized’ properties through smart contracts
- Financial instruments like loans, burdens and other responsibilities, or a futures contract to buy 1,000 barrels of oil for $60k on May 1
- KYC compliance check to verify users. Receiving a specific NFT token in your wallet similar to the blue checkmark ☑️ on Twitter — like Wyre
- and more…
Crypto-Collectibles are more than a passing craze. It is easy to see the reason why, especially when you look at the potential of the crypto-collectible technology, including: securing digital ownership, protecting intellectual property, tracking digital assets and overall creating real world value.

This article will create a basic ERC721 token using the OpenZeppelin implementation of the ERC721 standard. Look at the links in order to familiarize yourself with the requirements as they can sometimes be hidden in the excellent OpenZeppelin ERC721 implementations.
The assets that your ERC721 tokens (NFT) represent will influence some of the design choices for how your contract works, most notably how new tokens are created.
- You can have an initial supply of tokens defined during token creation
- You can have a function, which is only callable by the contract creator (or others — if you allow this) that will issue new tokens when called
For example, in CryptoKitties, players are able to “breed” their Kitties, which creates new Kitties (tokens). However, if your ERC721 token represents something more tangible, like concert tickets, you may not want token holders to be able to create more tokens. In some cases, you may even want token holders to be able to “burn” their tokens, effectively destroying them.
We will now implement an NFT collectible token, like CryptoKitties but with simpler logic.
You’ll learn how to create non fungible tokens, how to write tests for your smart contracts and how to interact with them once deployed.
We’ll build non-fungible collectibles: gradient tokens. Every token will be represented as a unique CSS gradient and will look somewhat like this:
The first prerequisites you should have:
- Install Truffle
npm install -g truffle
Create a new directory and move inside it. Then start a new
Truffle
project:mkdir nft-tutorial
cd nft-tutorial
truffle init
We will use OpenZeppelin ERC721 implementation, which is quick and easy and broadly used. Install
OpenZeppelin
in the current folder.npm install openzeppelin-solidity
TomoChain (mainnet)
: You will need real TOMO from exchanges
Go to Settings menu, select Backup wallet and then Continue. Here you can see your wallet’s private key and the 12-word recovery phrase.
Write down your 12-word recovery phrase.
We’ll be extending the OpenZeppelin ERC721 token contracts to create our Gradient Token.
- 1.Go to
contracts/
folder and create a new file calledGradientToken.sol
- 2.Copy the following code
pragma solidity ^0.5.4;import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-solidity/contracts/ownership/Ownable.sol';// NFT Gradient token
// Stores two values for every token: outer color and inner colorcontract GradientToken is ERC721Full, Ownable {
using Counters for Counters.Counter;
Counters.Counter private tokenId;
struct Gradient {
string outer;
string inner;
}
Gradient[] public gradients; constructor(
string memory name,
string memory symbol
)
ERC721Full(name, symbol)
public
{}
// Returns the outer and inner colors of a token
function getGradient( uint256 gradientTokenId ) public view returns(string memory outer, string memory inner){
Gradient memory _gradient = gradients[gradientTokenId]; outer = _gradient.outer;
inner = _gradient.inner;
} // Create a new Gradient token with params: outer and inner
function mint(string memory _outer, string memory _inner) public payable onlyOwner {
uint256 gradientTokenId = tokenId.current();
Gradient memory _gradient = Gradient({ outer: _outer, inner: _inner });
gradients.push(_gradient);
_mint(msg.sender, gradientTokenId);
tokenId.increment();
}
}
We inherited from two contracts: ERC721Full to make it represent a non-fungible token, and from the Ownable contract.
Every token will have a unique
tokenId
, like a serial number. We also added two attributes: inner
and outer
to save CSS colors.Ownable allows managing authorization. It assigns ownership to the deployer (when the contract is deployed) and adds modifier onlyOwner that allows restrictions to certain methods only to the contract owner. Also, ownership can be transferred. Additionally, third parties can be approved to spend tokens, burn tokens, etc.
Our solidity code is simple and I would recommend a deeper dive into the ERC-721 standard and the OpenZeppelin implementation.
In the
migrations/
directory, create a new file called 2_deploy_contracts.js
and copy the following:const GradientToken = artifacts.require("GradientToken");module.exports = function(deployer) {
const _name = "Gradient Token";
const _symbol = "GRAD";
return deployer
.then(() => deployer.deploy(GradientToken, _name, _symbol));
};
This code will deploy or migrate our contract to TomoChain, with the name
Gradient Token
and the symbol GRAD
.Now we set up the migrations: the blockchain where we want to deploy our smart contract, specify the wallet address to deploy, gas, price, etc.
1. Install Truffle’s
HDWalletProvider
, a separate npm package to find and sign transactions for addresses derived from a 12-word mnemonic
.npm install truffle-hdwallet-provider
2. Open
truffle.js
file (truffle-config.js
on Windows). Here the migration settings can be edited: networks, chain IDs, gas... You have multiple networks to migrate your ICO, you can deploy: locally, ganache
, public Ropsten (ETH)
testnet, TomoChain (testnet)
, TomoChain (Mainnet)
, etc…Both Testnet and Mainnet network configurations are described in the official TomoChain documentation — Networks. We need the
RPC endpoint
, the Chain id
and the HD derivation path
.Replace the
truffle.js
file with this new content:const HDWalletProvider = require('truffle-hdwallet-provider');
const infuraKey = "a93ffc...<PUT YOUR INFURA-KEY HERE>";// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
const mnemonic = '<PUT YOUR WALLET 12-WORD RECOVERY PHRASE HERE>';module.exports = { networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
}, // Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
ropsten: {
//provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/${infuraKey}`),
provider: () => new HDWalletProvider(
mnemonic,
`https://ropsten.infura.io/${infuraKey}`,
0,
1,
true,
"m/44'/889'/0'/0/", // Connect with HDPath same as TOMO
),
network_id: 3, // Ropsten's id
gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
}, // Useful for deploying to TomoChain testnet
tomotestnet: {
provider: () => new HDWalletProvider(
mnemonic,
"https://testnet.tomochain.com",
0,
1,
true,
"m/44'/889'/0'/0/",
),
network_id: "89",
gas: 3000000,
gasPrice: 10000000000000, // TomoChain requires min 10 TOMO to deploy, to fight spamming attacks
}, // Useful for deploying to TomoChain mainnet
tomomainnet: {
provider: () => new HDWalletProvider(
mnemonic,
"https://rpc.tomochain.com",
0,
1,
true,
"m/44'/889'/0'/0/",
),
network_id: "88",
gas: 3000000,
gasPrice: 10000000000000, // TomoChain requires min 10 TOMO to deploy, to fight spamming attacks
}, // Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
}, // Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
}, // Configure your compilers
compilers: {
solc: {
version: "0.5.4", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
}
}
}
3. Remember to update the
truffle.js
file using your own wallet recovery phrase. Copy the 12 words previously obtained from your wallet and paste it as the value of the mnemonic
variable.const mnemonic = '<PUT YOUR WALLET 12-WORD RECOVERY PHRASE HERE>';
⚠️ Warning: In production, it is highly recommend storing themnemonic
in another secret file (loaded from environment variables or a secure secret management system)...
You can use
Ganache
blockchain to test your smart contracts locally, before migrating to a public blockchain like Ethereum (Ropsten)
or Tomochain
.On a separate console window, install
Ganache
and run it:npm install -g ganache-cli
ganache-cli -p 8545
Ganache
will start running, listening on port 8545
. Automatically you will have 10 available wallets with their private keys and 100 ETH
each. You can use them to test your smart contracts.
We will add now tests to check our smart contracts.
When deploying contracts the first contract will usually be the deployer. This test will check that.
Create
GradientTokenTest.js
in /test
directory and write the following test:const GradientToken = artifacts.require("GradientToken");contract("Gradient token", accounts => {
it("Should make first account an owner", async () => {
let instance = await GradientToken.deployed();
let owner = await instance.owner();
assert.equal(owner, accounts[0]);
});
});
Here we run the
contract
block, that deploys our contract. We wait for the contract to be deployed and request owner()
which returns owner’s address. Then we assert that the owner address is the same as account[0]
.Note: Make sure thatGanache
is running (on a different console).
Run the test:
truffle test

The test should pass. This means that the smart contract works correctly and it did successfully what it was expected to do.
Every NFT token will have a unique ID. The first minted token has
ID: 0
, the second one has ID: 1
, and on and on…Now we’ll test the
mint
function. Add the following test:describe("mint", () => {
it("creates token with specified outer and inner colors", async () => {
let instance = await GradientToken.deployed();
let owner = await instance.owner(); let token0 = await instance.mint("#ff00dd", "#ddddff");
let token1 = await instance.mint("#111111", "#ffff22");
let token2 = await instance.mint("#00ff00", "#ffff00"); let gradients1 = await instance.getGradient( 1 );
assert.equal(gradients1.outer, "#111111");
assert.equal(gradients1.inner, "#ffff22");
});
});
This test is simple. First we check that we can mint new tokens. We mint 3 tokens. Then we expect that the unique attributes
outer
and inner
of token with tokenId = 1
are saved correctly and we assert it by using the getGradient
function that we created before.The test passed.

You should have your smart contract already compiled. Otherwise, now it’s a good time to do it with
truffle compile
.Note: Check that you have enoughTOMO
tokens in your wallet!! I recommend at least60 TOMO
to deploy this smart contract
Back in our terminal, migrate the contract to TomoChain testnet network:
truffle migrate --network tomotestnet
To deploy to the TomoChain mainnet is very similar:
truffle migrate --network tomomainnet
The migrations start…
Starting migrations...
======================
> Network name: 'tomotestnet'
> Network id: 89
> Block gas limit: 840000001_initial_migration.js
======================Deploying 'Migrations'
----------------------
> transaction hash: 0x67c0f12247d0bb0add43e81e8ad534df9cd7d3473ef76f5b60cee3e3d34bae1a
> Blocks: 2 Seconds: 5
> contract address: 0x6056dC38715C7d2703a8aA94ee68A964eaE86fdc
> account: 0x169397F515Af9E93539e0F483f8A6FC115de660C
> balance: 90.05683
> gas used: 273162
> gas price: 10000 gwei
> value sent: 0 ETH
> total cost: 2.73162 ETH> Saving artifacts
-------------------------------------
> Total cost: 2.73162 ETH2_deploy_contracts.js
=====================Deploying 'GradientToken'
-------------------------
> transaction hash: 0xca09a87ad8f834644dcb85f8ea89beff74b818eff11d355e0774e6b60c51718c
> Blocks: 2 Seconds: 5
> contract address: 0x8B830F38b798B7b39808A059179f2c228209514C
> account: 0x169397F515Af9E93539e0F483f8A6FC115de660C
> balance: 60.64511
> gas used: 2941172
> gas price: 10000 gwei
> value sent: 0 ETH
> total cost: 29.41172 ETH> Saving artifacts
-------------------------------------
> Total cost: 29.41172 ETHSummary
=======
> Total deployments: 2
> Final cost: 32.14334 ETH
Congratulations! You have already deployed your non-fungible token (NFT) to TomoChain! The deployment fees were
32.14 TOMO
.0x8B830F38b798B7b39808A059179f2c228209514C
- Error:
smart contract creation cost is under allowance
. Why?Increasing transaction fees for smart contract creation is one of the ways TomoChain offers to defend against spamming attacks. Solution: edittruffle.js
and add more gas/gasPrice to deploy. - Error:
insufficient funds for gas * price + value
. Why? You don’t have enough tokens in your wallet for gas fees. Solution: you need more funds in your wallet to deploy, go to faucet and get more tokens.
Now to create a new Gradient Token you can call:
GradientToken(gradientTokenAddress).mint("#001111", "#002222")
You can call this function via MyEtherWallet/Metamask or Web3... In a Dapp or game this would probably be called from a button click in a UI.
Let’s use MyEtherWallet (MEW) to interact with the contract. We use MetaMask to connect to the GradientToken owner wallet in TomoChain (testnet), then we will call function
mint()
to mint the first token.
In MyEtherWallet, under menu
Contract
> Interact with Contract
two things are required:- Contract Address: you got this address when you deployed
- ABI: file
build/contracts/GradientToken.json
, search"abi": [
…]
and copy everything inside brackets, including brackets. Then paste on MEW
On the right you will see a dropdown list with the functions. Select
mint
. MEW will show two fields: outer
and inner
. Input two colors, like #ff0000
or #0000ff
and click the button Write. Confirm with MetaMask.

You can use MEW to Write and to Read functions, like
getGradient
! This way you can check if values are correct, totalSupply, transfer tokens...In
Ethereum (Ropsten)
, the Etherscan page with our migrated contract will change after the first token is minted. A new link will be displayed now to track the ERC721 token GRAD
.

A few suggestions to continue from here:
- You could add a lot of different attributes to your unique NFT tokens
- You can have some buttons to interact with the tokens (buy, sell, change, transfer, change attributes/colors, etc…)
- You can iterate on this basic code and create a new CryptoKitties game :)

Congratulations! You have learned about non-fungible tokens, use-cases of NFTs and how to deploy NFT tokens on TomoChain.