FNCY Chain
FNCY 2.0.2 (ENG)
FNCY 2.0.2 (ENG)
  • Introduction
  • Getting Started
  • Design
    • Structure
    • Three Main Sets of Participants
    • PoSA Consensus
    • Governance
    • Staking
    • Mining Reward
    • Gas Fee
    • Runtime Upgrade
    • Cross-Chain Bridge
    • Security
  • Build On FNCY Chain
    • RPC Endpoints
    • Testnet Faucet
    • Block Explorer
      • Token Listing
    • BEP20 Token
    • NFT
    • Validator Requirements
    • NFT Market
      • FNCY Marketplace
  • For Developers
    • JSON-RPC
    • FNCY Chain API
      • Wallet API
      • Transaction API
      • Blockchain API
    • Wallet
      • FNCY Wallet
      • Use MetaMask for FNCY Chain
      • SDK
        • Android
          • Install
          • Guide
            • Initialize
            • Create/Restore Wallet
            • Wallet Search
            • Execute SendCoin/Contract
            • Transaction Search
            • Other Features
          • Methods
          • Domain
          • ETC
        • iOS
          • Install
          • Guide
            • Initialize
            • Create/Restore Wallet
            • Wallet Search
            • Execute SendCoin/Contract
            • Transaction Search
            • Other Features
          • Methods
          • Domain
          • ETC
    • Smart Contract
      • Deployment
        • Truffle
        • Hardhat
        • Remix IDE
      • Verify Your Contract on FncyScan
    • Gasless Transaction
    • with FNCY
      • FNCY Login
      • GAME AUTH Login
  • Tokenomics
  • Ecosystem Partner
  • FNCY Governance Partner
Powered by GitBook
On this page
  • Implementing Token URI
  • Metadata Structure
  • Attributes
  • Deploy NFT on Application Chain
  • Setting up the Environment
  • Getting the Contract Artifacts
  • Deploy the contract to a local blockchain
  • Interact with Our Token
  • MetaData
  • Deploy to a Public Testnet
  • Deploy to FNCY Testnet
  • Mint
  1. Build On FNCY Chain

NFT

PreviousBEP20 TokenNextValidator Requirements

Implementing Token URI

To facilitate a marketplace on the FNCY blockchain to pull in off-chain metadata for ERC721 assets, the NFT contract will need to return a URI where the metadata can be found. To find this URI, the tokenURI method in ERC721 and the uri method in ERC1155 is used to track your NFT. You should implement the function in the Contract:

/**
 * @dev Returns an URI for a given token ID
 */
function tokenURI(uint256 _tokenId) public view returns (string) {
  return Strings.strConcat(
      baseTokenURI(),
      Strings.uint2str(_tokenId)
  );
}

The tokenURI function in your Contract should return an HTTP or IPFS URL. When queried, this URL should in turn return a JSON blob of data with the metadata for your token.

Metadata Structure

Marketplaces on FNCY Blockchain support metadata that is structured according to . Additionally, several properties for your items are supported, giving you all the sorting and filtering capabilities on FNCY Blockchain Marketplaces. The below metadata structure, allows the FNCY Blockchain Marketplace to read and display the details about the assets which your NFTs represent.

{
    "name":"NFT Name",
    "description":"NFT Description",
    "image":"https://somedomain.com/pic/xxxx.jpg",
    "external_url":"https://originalsite.io/2",
    "attributes":[...]
}

Here's how each of these properties works:

Property
Description

name

Name of the item. Max 200 characters.

description

A human-readable description of the item. Markdown is supported. Max 500 characters.

image

This is the URL to the image of the item. Can be just about any type of image. A 350 x 350 image is recommended.

animation_url

This is the URL to a multi-media attachment for the item. The file extensions GLTF, GLB, WEBM, MP4, M4V, OGV, and OGG are supported, along with the audio-only extensions MP3, WAV, and OGA.

animation_type

This is the file format of the multi-media attachment provided from animation_url.

external_url

This is the URL that will appear below the asset's image on the marketplace and will allow users to leave the marketplace and view the item on your site.

attributes

These are the attributes for the item to describe the detail of the NFT. (see array below)

Attributes

To present NFT traits, include the following array in the metadata:

{
    "attributes":[
        {
            "trait_type":"Rarity Class",
            "value":"Normal"
        },
        {
            "trait_type":"Type",
            "value":"Male"
        },
        {
            "trait_type":"Face",
            "value":"Mole"
        },
        {
            "trait_type":"Beard",
            "value":"Chinstrap"
        },
        {
            "display_type":"boost_number",
            "trait_type":"Power",
            "value":"Power"
        },
        {
            "display_type":"boost_percentage",
            "trait_type":"Health Increase",
            "value":"20"
        },
        {
            "display_type":"number",
            "trait_type":"Generation",
            "value":"3"
        }
    ]
}

Here, trait_type is the name of the trait, value is the value of the trait, and display_type is a field indicating how you would like a numeric value should be displayed. For string traits, you don't have to worry about display_type. All traits included in the attributes will be displayed in Attribute. If you don't want to have a trait_type for a particular trait, you can include just a value in the trait and it will be set as a generic attribute.

Numeric Traits

There are 3 supported options for display_type: number will show the value in pure number, boost_number allows you to show the number with Plus or Minus symbol, and boost_percentage is similar to boost_number but will show a percent sign behind the number.

Date

Marketplace also supports date traits in date display_type. Pass in a unix timestamp as the value.

   {
      "display_type": "date", 
      "trait_type": "birthday", 
      "value": 1608490000
    }
 

Deploy NFT on Application Chain

In this tutorial, we will create a non-fungible token (NFT) and deploy to a public Testnet.

Setting up the Environment

We begin by creating a new project.

$ mkdir mynft && cd mynft
$ npm init -y
$ npm i --save-dev @openzeppelin/contracts
$ npm i truffle

Getting the Contract Artifacts

We will setup our Solidity project using truffle init to create a contracts directory and configuration to connect to a network.

$ npx truffle init
Starting init...
================

> Copying project files to

Init successful, sweet!

The preset contracts have already been compiled, so we only need to copy the artifacts to the build/contracts directory.

$ mkdir -p build/contracts/
$ cp node_modules/@openzeppelin/contracts/build/contracts/* build/contracts/

Use your favorite editor to create 2_deploy.js in the migrations directory with the following contents:

// migrations/2_deploy.js
// SPDX-License-Identifier: MIT
const ERC721PresetMinterPauserAutoId = artifacts.require("ERC721PresetMinterPauserAutoId");

module.exports = function(deployer) {
  deployer.deploy(ERC721PresetMinterPauserAutoId, "My NFT","NFT", "http://my-json-server.typicode.com/huangsuyu/nft/tokens");
};

Deploy the contract to a local blockchain

$ npx truffle develop
Truffle Develop started at http://127.0.0.1:8545/

Accounts:
(0) 0xc7e4bbc4269fdc62f879834e363173aee7895f45

Private Keys:
(0) ef424b4dc91a9c9d6c1fc4ae0a50ce80668f3a955a7e982584b45577e2c70e27

Mnemonic: mechanic cannon setup general indicate people notable frown poet friend credit true

⚠️  Important ⚠️  : This mnemonic was created for you by Truffle. It is not secure.
Ensure you do not use it on production blockchains, or else you risk losing funds.

truffle(develop)> migrate

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Artifacts written to /Users/Documents/work/mynft/build/contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

Starting migrations...
======================
> Network name:    'develop'
> Network id:      5777
> Block gas limit: 67211175 (0x6691b7)

1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x9a17a50e6efd52ba3e55245c76c52b065d20587add45aee732c233987033e567
   > Blocks: 0            Seconds: 0
   > contract address:    0x77409B688eA5461078a31450F3138EA8324F72C9
   > block number:        1
   > block timestamp:     1604387655
   > account:             0xc7e4bBc4269fdC62F879834E363173aeE7895F45
   > balance:             99.99616114
   > gas used:            191943 (0x2edc7)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00383886 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00383886 ETH


2_deploy.js
===========

   Deploying 'ERC721PresetMinterPauserAutoId'
   ------------------------------------------
   > transaction hash:    0xc1a3994c2ad2ba706ac49934b4f480c7b3d9b94241f526afa2dfe91545145a4e
   > Blocks: 0            Seconds: 0
   > contract address:    0xEaB17D581552123695f03F12b09e378EE9463b44
   > block number:        3
   > block timestamp:     1604387655
   > account:             0xc7e4bBc4269fdC62F879834E363173aeE7895F45
   > balance:             99.92142266
   > gas used:            3694586 (0x385ffa)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.07389172 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.07389172 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.07773058 ETH

truffle(develop)>

We can deploy our new NFT to our development blockchain using migrate.

truffle(develop)> migrate

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Starting migrations...
======================
> Network name:    'develop'
> Network id:      5777
> Block gas limit: 67211175 (0x6691b7)


1_initial_migration.js
======================

   Replacing 'Migrations'
   ----------------------
   > transaction hash:    0x5d71b0a45a0fe20e2ca645393bb44b83fbd47351c009c48be0b8b84b610fb3b7
   > Blocks: 0            Seconds: 0
   > contract address:    0x37117c825cAC4a1FA765F6D8cd7787fB195849555
   > block number:        1
   > block timestamp:     1590736865
   > account:             0x0445c33BdCe670D57189158b88c0034B579f37cE
   > balance:             99.99671674
   > gas used:            164163 (0x28143)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00328326 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00328326 ETH


2_deploy.js
===========

   Replacing 'ERC721PresetMinterPauserAutoId'
   ------------------------------------------
   > transaction hash:    0x166d7b28f4afb949585b5a0e5b4151daa54acbcb70566b202fd62ab380a6650c
   > Blocks: 0            Seconds: 0
   > contract address:    0xDEE9411430c7Dd9b67fC6DA723DE729AdAB50AD7
   > block number:        3
   > block timestamp:     1590736866
   > account:             0x0445c33BdCe670D57189158b88c0034B579f37cE
   > balance:             99.92191642
   > gas used:            36117675 (0x386c0b)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.0739535 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0739535 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.07723676 ETH

We can then use our deployed contract.

truffle(develop)> nft = await ERC721PresetMinterPauserAutoId.deployed()
undefined

Interact with Our Token

The accounts that we can use were displayed when we started truffle develop

Token Metadata

We can call the contract to read token metadata such as name, symbol and baseURI

truffle(develop)> await nft.name()
'My NFT'
truffle(develop)> await nft.symbol()
'NFT'
truffle(develop)> await nft.baseURI()

Mint

We can send a transaction to mint tokens to a given account, from an account with the minter role. In our case, we are minting from the account which deployed the token, which is given the minter role.

We will mint 1 NFT with token ID 0.

truffle(develop)> await nft.mint("0x0445c33bdce670d57189158b88c0034b579f37ce")
{ tx:
   '0xd301a60dbb8ac187687f6639f200d4e6f2cfa065923092b3940330e35a26421d',
  receipt:
   { transactionHash:
      '0xd301a60dbb8ac187687f6639f200d4e6f2cfa065923092b3940330e35a26421d',
     transactionIndex: 0,
     blockHash:
      '0x3ad3cfcb26da0c969e9d5a5414a5e90a91a3a862c9e9082afc38a0ec0f1a5d00',
     blockNumber: 5,
     from: '0x0445c33bdce670d57189158b88c0034b579f37ce',
     to: '0xdee9411430c7dd9b67fc6da723de729adab50ad7',
     gasUsed: 156470,
...

We can check the owner of the token and the token URI for the metadata

truffle(develop)> await nft.ownerOf(1)
'0x0445c33BdCe670D57189158b88c0034B579f37cE'
truffle(develop)> await nft.tokenURI(1)

MetaData

Warning: For production, we need to store our metadata in a permanent location that can exist for the life of the token.

Deploy to a Public Testnet

Next, we will deploy to FNCY Testnet .

You will need the following:

  • @truffle/hdwallet-provider installed

  • Configure truffle-config.js for FNCY testnet network

  • A funded testnet account and mnemonic

  • A secrets.json or another secret-management solution. Make sure you don’t commit this to GitHub!

My truffle-config.js has the following testnet configuration:

   testnet: {
      provider: () => new HDWalletProvider(mnemonic, `https://fncy-testnet-seed.fncy.world`),
      network_id: 923018,
      confirmations: 10,
      timeoutBlocks: 200,
      skipDryRun: true
    }

Deploy to FNCY Testnet

$ npx truffle migrate --network testnet

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.

Starting migrations...
======================
> Network name:    'testnet'
> Network id:      923018
> Block gas limit: 30000000 (0x1c9c380)


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x9a17a50e6efd52ba3e55245c76c52b065d20587add45aee732c233987033e567
   > Blocks: 0            Seconds: 0
   > contract address:    0x77409B688eA5461078a31450F3138EA8324F72C9
   > block number:        1
   > block timestamp:     1604387655
   > account:             0xc7e4bBc4269fdC62F879834E363173aeE7895F45
   > balance:             99.99616114
   > gas used:            191943 (0x2edc7)
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.000191943 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.000191943 ETH


2_deploy.js
===========

   Deploying 'ERC721PresetMinterPauserAutoId'
   ------------------------------------------
   > transaction hash:    0xc1a3994c2ad2ba706ac49934b4f480c7b3d9b94241f526afa2dfe91545145a4e
   > Blocks: 0            Seconds: 0
   > contract address:    0xEaB17D581552123695f03F12b09e378EE9463b44
   > block number:        3
   > block timestamp:     1604387655
   > account:             0xc7e4bBc4269fdC62F879834E363173aeE7895F45
   > balance:             99.995969197
   > gas used:            3694586 (0x385ffa)
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.003694586 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.003694586 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.003886529 ETH

Mint

We can send a transaction to mint tokens to a given account, from an account with the minter role.

truffle(testnet)> nft = await ERC721PresetMinterPauserAutoId.deployed()
undefined

In our case we are minting from the account which deployed the token, which is given the minter role.

To see configured accounts run the command accounts.

truffle(testnet)> accounts
[ '0x133d144f52705ceb3f5801b63b9ebccf4102f5ed',

We will mint 1 NFT with token ID 1. Specify the address that you want to be the token holder (0xc7e4bBc4269fdC62F879834E363173aeE7895F45 is a test account)

truffle(testnet)> await nft.mint("0x133d144f52705ceb3f5801b63b9ebccf4102f5ed")
{ tx:
   '0x0d90d4a2a4ac3f33d5220deb11e8f65adf14a6669afd18abd4cce8ca7ab58e04',
  receipt:
   { blockHash: '0x724ba66bc1d799820c05a93ae67991b21bb769fd1e9dddd5db9f725f5f633331',
     blockNumber: 3333746,
     contractAddress: null,
     cumulativeGasUsed: 164785,
     from: '0x77737a65c296012c67f8c7f656d1df81827c9541',
     gasUsed: 164785,
...

This work is inspired by

ERC721 is a standard for representing ownership of , that is, where each token is unique, such as in real estate or collectibles.

We will use contracts in to create an ERC721 and deploy using Truffle.

Then we install which has an implementation of ERC721.

Next, we install a development tool for deployment. For this tutorial, we will use but we could use any other tools, such as Buidler, Remix, or .

We are going to use Preset which is an ERC721, which is a preset that can be minted (with auto token ID and metadata URI), paused, and burned.

We will use to open a Truffle console with a development blockchain.

For this tutorial, we will use where we can store a single JSON file in a GitHub repository that we can access via a fake JSON server.

A sample JSON for tokenID 1 is:

To deploy, we will use the instructions for

Head over to and request test FNCY

the official ERC721 metadata standard
this blog
non-fungible tokens
Presets
OpenZeppelin Contracts
OpenZeppelin Contracts
Truffle,
OpenZeppelin CLI
ERC721PresetMinterPauserAutoId
truffle develop
My JSON Server
http://my-json-server.typicode.com/huangsuyu/nft/tokens/1
Connecting to Public Test Networks with Truffle
Faucet