How to deploy NFT tokens on TomoChain

Create your unique ERC721 tokens (ie: CryptoKitties) on TomoChain!

Karl D. Brillant
Mar 25 · 14 min read

This article will explain:


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 coins 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.

Comparison: fungible and non-fungible tokens

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.

CryptoKitties

At the end of 2017, NFTs made a remarkable entrance in the blockchain world with the sucess 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.

CryptoKitties

Crypto Item Standard (ERC-1155)

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.


Use-cases of Non-Fungible Tokens (NFT)

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:

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.

Resultado de imagen de axie nft
Axie Infinity

How to deploy a NFT token on TomoChain

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.

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.

Let’s Start the NFT Tutorial

We will now implement a 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:

Gradient Tokens

0. Prerequisites

The first prerequisites you should have:

npm install -g truffle

1. Creating a new project

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

2. Preparing your TOMO wallet

Create a TOMO wallet. Then grab a few tokens:

TomoWallet

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.


3. Writing the Smart Contract

3.1 GradientToken.sol

We’ll be extending now the OpenZeppelin ERC721 token contracts to create our Gradient Token.

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 color
contract 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 deployer (when the contract is deployed) and adds modifier onlyOwner that allows you to restrict certain methods only to contract owner. Also, you can transfer ownership. You can approve a third party 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.

You can see the functions to use in OpenZeppelin ERC721 here and here.

You can find another ERC721 smart contract example by OpenZeppelin here.


4. Config Migrations

4.1 Create the migration scripts

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.

4.2 Configure truffle.js

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). You can edit here the migration settings: 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 the mnemonic in another secret file (loaded from environment variables or a secure secret management system)...

4.3 Ganache

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.

Ganache

5. Adding Tests

We will add now tests to check our smart contracts.

When you deploy contracts your 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 that Ganache is running (on a different console).

Run the test:

truffle test
Testing

The test should pass. This means that the smart contract works correctly and it did successfully what it was expected to do.

Adding more tests

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.

Testing

6. Deploying

6.1 Start the migration

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 enough TOMO tokens in your wallet!! I recommend at least 60 TOMO to deploy this smart contract

Back in our terminal, migrate the contract to TomoChain testnet network:

truffle migrate --network tomotestnet

To deploy to TomoChain mainnet is very similar:

truffle migrate --network tomomainnet

The migrations start…

Starting migrations...
======================
> Network name: 'tomotestnet'
> Network id: 89
> Block gas limit: 84000000
1_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 ETH
2_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 ETH
Summary
=======
> 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.

Read the output text on the screen. The NFT token contract address is (yours will be different):

0x8B830F38b798B7b39808A059179f2c228209514C

⚠️ Note: TomoChain’s smart contract creation fee: gas price 10000 Gwei, gas limit >= 1000000

*** Troubleshooting ***


7. Interacting with the smart contract

7.1 Minting new 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.

MyEtherWallet

In MyEtherWallet, under menu Contract > Interact with Contract two things are required:

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.

MEW: Interact with contract

Here is our contract address, and the new mint transaction:

TomoScan

You can use MEW to Write and to Read functions, like getGradient! This way you can check if values are correct, totalSupply, transfer tokens...

Note: 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.

Ethereum (Ropsten): Before and After “mint()”
Ropsten: Gradient Token

What’s next?

A few suggestions to continue from here:

Gradient Tokens

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

Now we are looking forward to see your awesome ideas implemented!


Source Code

The source code for this tutorial is available on Github.

TomoChain

Welcome to TomoChain blog. Follow us to get the official news and good reads from TOMO team. Clap along if you feel like a room without a roof!

Thanks to Van Cam PHAM.

Karl D. Brillant

Written by

You do not talk about Fisht Clu…

TomoChain

TomoChain

Welcome to TomoChain blog. Follow us to get the official news and good reads from TOMO team. Clap along if you feel like a room without a roof!