> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fantom: ERC-721 collection with Truffle & OpenZeppelin

> Deploy an ERC-721 NFT collection contract on Fantom using Truffle, OpenZeppelin, and a Chainstack node endpoint for testnet contract verification.

**TLDR:**

* This tutorial shows how to build and deploy a straightforward ERC-721 contract on the Fantom testnet.
* You’ll leverage Truffle and OpenZeppelin to set up and compile the NFT collection, which can be minted by anyone.
* Once deployed, you can verify the contract on FTMScan for easy interactions like minting or querying balances.
* You can eventually take the same approach for Fantom mainnet and list your collection on an NFT marketplace such as Artion.

## Main article

ERC-721 is the non-fungible token (NFT) standard for smart contracts.

In this tutorial, you will:

* Create a simple ERC-721 collection contract that allows anyone to mint new non-fungible tokens in the collection.
* Deploy the contract on the Fantom testnet through a node deployed with Chainstack.
* Interact with the deployed contract.
* See that you can register your collection on an NFT market.

## Prerequisites

* [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Fantom RPC endpoint](https://chainstack.com/build-better-with-fantom/).
* [Truffle Suite](https://trufflesuite.com/) to create and deploy contracts.
* [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/) to use the audited [ERC-721 libraries](https://docs.openzeppelin.com/contracts/4.x/erc721) to create your ERC-721 collection contract.

## Overview

To get from zero to a deployed ERC-721 contract on the Fantom testnet, do the following:

1. With Chainstack, create a <Tooltip tip="A public chain project- a project to join public networks">public chain project</Tooltip>.
2. With Chainstack, join the Fantom testnet.
3. With Chainstack, access your Fantom node credentials.
4. With OpenZeppelin, create an ERC-721 contract.
5. With Truffle, compile and deploy the contract through your Fantom node.
6. With FTMScan, verify the deployed contract.

## Step-by-step

### Create a public chain project

See [Create a project](/docs/manage-your-project#create-a-project).

### Join the Fantom testnet

See [Join a public network](/docs/manage-your-networks).

### Get your Fantom node access and credentials

See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials).

### Install OpenZeppelin Contracts

See [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/).

### Install Truffle Suite

See [Truffle Suite: Installation](https://trufflesuite.com/docs/truffle/how-to/install/).

### Create the contract

1. On your machine, in the contract directory, initialize Truffle:

   <CodeGroup>
     ```bash Shell theme={"system"}
     truffle init
     ```
   </CodeGroup>

   This will generate the Truffle boilerplate structure:

   ```
   .
   ├── contracts
   │   └── .gitkeep
   ├── migrations
   │   └── .gitkeep
   ├── test
   │   └── .gitkeep
   └── truffle-config.js
   ```

2. Go to the `contracts` directory. In the directory, create your ERC-721 contract `Fantom721Collection.sol`.

   <CodeGroup>
     ```solidity solidity theme={"system"}
     //SPDX-License-Identifier: MIT
     pragma solidity ^0.8;

     import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

     contract Fantom721Collection is ERC721URIStorage {
         uint256 public tokenCounter;
         constructor () public ERC721 ("COLLECTION_NAME", "COLLECTION_TICKER"){
             tokenCounter = 0;
         }

         function createCollectible(string memory tokenURI) public returns (uint256) {
             uint256 newItemId = tokenCounter;
             _safeMint(msg.sender, newItemId);
             _setTokenURI(newItemId, tokenURI);
             tokenCounter = tokenCounter + 1;
             return newItemId;
         }

     }
     ```
   </CodeGroup>

   The contract implementation is the following:

   * The contract uses OpenZeppelin audited [ERC-721 contract templates](https://docs.openzeppelin.com/contracts/4.x/erc721).
   * The contract is a mintable collection. Anyone can add a token to the collection through `createCollectible`.
   * COLLECTION\_NAME — any name to give to your collection
   * COLLECTION\_TICKER — any ticker for your collection

3. Create `2_deploy_contracts.js` in the `migrations` directory.

   <CodeGroup>
     ```js JavaScript theme={"system"}
     module.exports = function(deployer) {
     var Fantom721Collection = artifacts.require("./Fantom721Collection.sol");
       deployer.deploy(Fantom721Collection);
     };
     ```
   </CodeGroup>

   This will create the contract deployment instructions for Truffle.

### Compile and deploy the contract

1. Install `HDWalletProvider`.

   [HDWalletProvider](https://github.com/trufflesuite/truffle/tree/develop/packages/hdwallet-provider) is Truffle's separate npm package used to sign transactions.

   Run:

   <CodeGroup>
     ```bash Shell theme={"system"}
     npm install @truffle/hdwallet-provider
     ```
   </CodeGroup>

2. Edit `truffle-config.js` to add:

   * `HDWalletProvider`

   * Your Fantom node access and credentials

   * Your Fantom account that you will use to deploy the contract.

     <CodeGroup>
       ```js JavaScript theme={"system"}
       const HDWalletProvider = require("@truffle/hdwallet-provider");
       const private_key = 'YOUR_PRIVATE_KEY';

       module.exports = {
        networks: {
           testnet: {
               provider: () => new HDWalletProvider(private_key, "YOUR_CHAINSTACK_ENDPOINT"),
               network_id: 4002
           }
          },

        compilers: {
           solc: {
           version: "0.8.9",
           }
         }
       };
       ```
     </CodeGroup>

   where

   * `testnet` — any network name that you will pass to the `truffle migrate --network` command.
   * `HDWalletProvider` — Truffle's custom provider to sign transactions.
   * YOUR\_PRIVATE\_KEY — the private key of your Fantom account that will deploy the contract. The account must have enough FTM funds to run the deployment. See also [Fantom testnet faucet](https://faucet.fantom.network/).
   * YOUR\_CHAINSTACK\_ENDPOINT — your Fantom node HTTPS endpoint deployed with Chainstack. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) and [Tools](/docs/fantom-tooling).
   * `network_id` — the network ID of the Fantom network: mainnet is `250`, testnet is `4002`.
   * `solc` — the Solidity compiler version that Truffle must use.

3. Run:

   <CodeGroup>
     ```bash Shell theme={"system"}
     truffle migrate --network testnet
     ```
   </CodeGroup>

   This will engage `2_deploy_contracts.js` and deploy the contract to the Fantom testnet as specified in `truffle-config.js`.

### Interact with the contract

Once your contract is deployed, you can view it online at [FTMScan testnet](https://testnet.ftmscan.com/).

For an easy way to interact with your deployed contract, verify it on FTMScan.

### Flatten your contract code

Since your ERC-721 contract uses imported OpenZeppelin libraries, you must put all the imports into one `.sol` file to make FTMScan be able to verify it.

1. Install Truffle Flattener.

   Run:

   <CodeGroup>
     ```bash Shell theme={"system"}
     npm install truffle-flattener
     ```
   </CodeGroup>

2. Flatten the contract.

   In the `contracts` directory, run:

   <CodeGroup>
     ```bash Shell theme={"system"}
     npx truffle-flattener Fantom721Collection.sol > FlatFantom721Collection.sol
     ```
   </CodeGroup>

3. Clean up the licensing information.

   The flattened contract will have the same licensing note imported from each of the files. Multiple licensing notes in one file break the FTMScan verification, so you have to leave one licensing note for the entirety of the flattened contract.

   The easiest way to clean up is to search for the `SPDX` mentions in the file and remove all of them except for the very first one.

### Verify the deployed contract on FTMScan

At this point, you have your flattened and cleaned-up contract ready for the FTMScan verification.

1. Go to [FTMScan testnet](https://testnet.ftmscan.com/).
2. Find your deployed contract. The address of your contract should have been printed by Truffle at the end of the deployment in the `contract address` field.
3. On the contract page on FTMScan, click **Contract** > **Verify and Publish**.
4. In **Compiler Type**, select **Solidity (Single file)**.
5. In **Compiler Version**, select **v0.8.9**. This is the version this tutorial used to compile the contract.
6. In **Open Source License Type**, select **MIT License (MIT)**.
7. Click **Continue**.
8. Keep the **Optimization** option set to **No** as Truffle does not use optimization by default.
9. Paste the entirety of your flattened `.sol` contract in the **Enter the Solidity Contract Code below** field.
10. Click **Verify and Publish**.

FTMScan will take a few seconds to compile your contract, verify, and publish it.

### Interact with the contract

Now that your ERC-721 contract is verified, FTMScan is effectively a front-end instance for your contract.

### Mint an NFT in the collection

You can use any account to call the `createCollectible` function.

Make sure you have:

* MetaMask installed and unlocked as you will need it to call the contract. See [Fantom tooling: MetaMask](/docs/fantom-tooling#metamask).
* Testnet FTM on the account to pay for the transaction. See [Fantom testnet faucet](https://faucet.fantom.network/).

1. On FTMScan, on your contract, click **Contract**.
2. Click **Write Contract**.
3. Click **Connect to Web3**.
4. Under **createCollectible**, in the **tokenURI** field, provide any string to serve as metadata for this specific NFT. See also [OpenZeppelin ERC-721](https://docs.openzeppelin.com/contracts/4.x/erc721) for a metadata example.
5. Click **Write**.

This will send a transaction to mint in NFT in your contract collection and distribute the token to the account called `createCollectible`.

### Check the balances

Check the NFT balance of an address:

1. On FTMScan, on your contract, click **Contract**.
2. Click **Read Contract**.
3. Scroll to the **balanceOf** field.
4. In the **owner (address)** field, provide the address of the account you used to deploy the contract.
5. Click **Query**.

Check the number of minted NFTs

1. On FTMScan, on your contract, click **Contract**.
2. Click **Read Contract**.
3. Check the **tokenCounter** field.

### Listing on an NFT market

Having gone through the tutorial to understand the basics of creating an NFT collection, you can amend the contract to your needs and deploy it on the Fantom mainnet.

Once deployed, you can list the collection at an NFT marketplace—[Artion](https://artion.io/).

See [Artion: Register Collection](https://artion.io/collection/register).

## Conclusion

This tutorial guided you through the basics of creating and deploying a contract in the ERC-721 non-fungible token standard.

The contract that you created is a collection that anyone on the Fantom network can interact with to add their tokens to the collection.

When you are ready, you can also deploy your own ERC-721 contract on the Fantom mainnet and list the collection on a Fantom NFT marketplace.

This tutorial uses testnet, however, the exact same instructions and sequence work on the mainnet.

<CardGroup>
  <Card title="Ake">
    <img src="https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max&auto=format&n=UN3rP7zhB69idvnC&q=85&s=792a24ab1b4682406fa589c0ecd88e5d" alt="Ake" style={{width: '80px', height: '80px', borderRadius: '50%', objectFit: 'cover', display: 'block', margin: '0 auto'}} noZoom width="400" height="400" data-path="images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg" />

    <Icon icon="code" iconType="solid" /> Director of Developer Experience @ Chainstack
    <br /><Icon icon="screwdriver-wrench" iconType="solid" /> Talk to me all things Web3
    <br />20 years in technology | 8+ years in Web3 full time years experience

    <div style={{display: "flex", justifyContent: "center", gap: "12px"}}>
      <a href="https://github.com/akegaviar/" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="github" iconType="brands" />
      </a>

      <a href="https://twitter.com/akegaviar" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="twitter" iconType="brands" />
      </a>

      <a href="https://www.linkedin.com/in/ake/" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="linkedin" iconType="brands" />
      </a>
    </div>
  </Card>
</CardGroup>
