April 12, 2022

How to Create and Deploy a Factory ERC-1155 Contract on Polygon using Truffle

Overview

With the increasing popularity of NFTs, Polygon, a fast-growing blockchain, has become the go-to choice for many users and developers. Compared to Ethereum, Polygon allows users and developers to interact with blockchains at a more affordable level. For these reasons, this guide will be covering how to create and deploy a Factory ERC-1155 smart contract on Polygon using Truffle. To make things fun, the ERC-1155 tokens we mint will represent a Solar System NFT collection (containing eight different planets). To create this collection, we gathered eight images (our planets) and eight metadata files (which includes a name, description, and link fields) which we uploaded to NFT.Storage. At the end of this guide, we will be able to view our minted NFT on OpenSea:

Mercury ERC-1155 NFT on OpenSea

We will also add in some additional knowledge bombs at the end if you stick around!

What we will do:

- Define the ERC-1155 Standard
- Set up a Polygon node
- Set up Truffle Project
- Deploy an ERC-1155 factory smart contract
- Mint from the ERC-1155 factory smart contract

What you will need:

- Your own image assets and metadata
- Node.js
- Terminal
- Text Editor
- MetaMask and some MATIC (main-net).
- Polygonscan API Key

We will now give an overview of the ERC-1155 Standard, Factory Patterns, Polygon and Truffle. If you're familiar with these, feel free to skip to the "Set up a QuickNode Endpoint" section.

ERC-1155 Standard

The ERC-1155 standard was proposed back in 2018 and continues to be used by many NFT projects. Why? It solved some of the problems the ERC-721 standard was running into. Let us dive a bit more into what it can do:

Use Cases

- Represent fungible, non-fungible, and partially fungible tokens
- Send multiple tokens in a single transaction
- Allows for atomic swaps (enabling swaps across two different chains)

What is the Factory Pattern?

A factory contract refers to a smart contract that can create, track and modify the state of its created contracts. There are several uses cases of why you may want to implement a factory contract. For example, if your deployment pipeline involves re-deploying the same contract, you can utilize the factory pattern to keep track of these contracts and save on gas fees. 

The factory contract we are deploying in this guide allows us to create and track multiple ERC-1155 tokens in one contract. This gives us the ability to make different NFT collections and manage them from one place. 

What is Polygon?

Polygon is a scaling solution for public blockchains. Based on an adapted implementation of Plasma framework (PoS) - with an account-based performance, Polygon supports all the existing Ethereum tooling and faster and cheaper transactions. 

To put its growth into context, Polygon's total value locked (TVL) increased from 100 million to over 3 billion in under a year (source). We can also see growth when looking at the number of unique addresses on Polygon:

A line chart of Polygon (PoS) daily transactions

What is Truffle?

Truffle is a development environment, testing framework, and asset pipeline for blockchains using the Ethereum Virtual Machine (EVM). It is a suite of products that will help developers quickly build and ship more awesome products in web3. Check out the Truffle homepage to learn more.

Set up a QuickNode Endpoint

To deploy our contract to Polygon main-net, we will need to run a Polygon node. We could run our own Polygon node, but since that can be too involved for just deploying a contract, we will use a free endpoint from QuickNode to make this easy. To get a Polygon node, navigate QuickNode and signup! After you have created your free Polygon endpoint, copy your HTTP Provider endpoint:

Screenshot of Quicknode endpoint

Set up Project Directory

Navigate to a directory in your terminal where you'd like to set up this project and run the following commands: 

mkdir nft_project && cd nft_project

Then, install the required dependencies (i.e., Truffle & OpenZeppelin, plugins) run: 

npm install -g truffle

To initialize the truffle project, run:

npx truffle init

Installing necessary dependencies:

npm install @openzeppelin/contracts @truffle/hdwallet-provider truffle-plugin-verify

After installation, you can also run the commands `node --version` or `truffle version` to see the installed version.  If you see an error, ensure that your npm modules are added to your path.

The Truffle boilerplate template is as follows:

Folders:
    - Contracts - which stores solidity code that we'll deploy
    - Tests - stores tests that we will use to test our code
    - Config - truffles settings file
    - Migrations -  directory that will help us deploy our contracts
Files:
    - contracts/Migrations.sol 
    - migrations/1_initial_migration.js

To double-check the project is initialized, run the ls command in your terminal. You should see the following files and folders:

displaying directory contents of truffle project

Once you confirm that it's created, you are one step closer to deploying and minting an NFT on Polygon!

Create and Compile Core Contracts

Now that we verified our truffle project is created, we can start making the necessary files to deploy our NFT smart contract.

Navigate into the contracts directory and run the command `touch ERC1155Token.sol`. Then open up the file in your text editor and input the following code:

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract ERC1155Token is ERC1155, Ownable {

    string[] public names; //string array of names
    uint[] public ids; //uint array of ids
    string public baseMetadataURI; //the token metadata URI
    string public name; //the token mame
    uint public mintFee = 0 wei; //mintfee, 0 by default. only used in mint function, not batch.
    
    mapping(string => uint) public nameToId; //name to id mapping
    mapping(uint => string) public idToName; //id to name mapping

    /*
    constructor is executed when the factory contract calls its own deployERC1155 method
    */
    constructor(string memory _contractName, string memory _uri, string[] memory _names, uint[] memory _ids) ERC1155(_uri) {
        names = _names;
        ids = _ids;
        createMapping();
        setURI(_uri);
        baseMetadataURI = _uri;
        name = _contractName;
        transferOwnership(tx.origin);
    }   

    /*
    creates a mapping of strings to ids (i.e ["one","two"], [1,2] - "one" maps to 1, vice versa.)
    */
    function createMapping() private {
        for (uint id = 0; id < ids.length; id++) {
            nameToId[names[id]] = ids[id];
            idToName[ids[id]] = names[id];
        }
    }
    /*
    sets our URI and makes the ERC1155 OpenSea compatible
    */
    function uri(uint256 _tokenid) override public view returns (string memory) {
        return string(
            abi.encodePacked(
                baseMetadataURI,
                Strings.toString(_tokenid),".json"
            )
        );
    }

    function getNames() public view returns(string[] memory) {
        return names;
    }

    /*
    used to change metadata, only owner access
    */
    function setURI(string memory newuri) public onlyOwner {
        _setURI(newuri);
    }

    /*
    set a mint fee. only used for mint, not batch.
    */
    function setFee(uint _fee) public onlyOwner {
        mintFee = _fee;
    }

    /*
    mint(address account, uint _id, uint256 amount)

    account - address to mint the token to
    _id - the ID being minted
    amount - amount of tokens to mint
    */
    function mint(address account, uint _id, uint256 amount) 
        public payable returns (uint)
    {
        require(msg.value == mintFee);
        _mint(account, _id, amount, "");
        return _id;
    }

    /*
    mintBatch(address to, uint256[] memory _ids, uint256[] memory amounts, bytes memory data)

    to - address to mint the token to
    _ids - the IDs being minted
    amounts - amount of tokens to mint given ID
    bytes - additional field to pass data to function
    */
    function mintBatch(address to, uint256[] memory _ids, uint256[] memory amounts, bytes memory data)
        public
    {
        _mintBatch(to, _ids, amounts, data);
    }
}

We will also need to create a file that contains the Factory code. Inside the contracts directory, run the command `touch FactoryERC1155.sol`, then open the file and input the following code:

pragma solidity ^0.8.4;

import "./ERC1155Token.sol";

contract FactoryERC1155 {

    ERC1155Token[] public tokens; //an array that contains different ERC1155 tokens deployed
    mapping(uint256 => address) public indexToContract; //index to contract address mapping
    mapping(uint256 => address) public indexToOwner; //index to ERC1155 owner address

    event ERC1155Created(address owner, address tokenContract); //emitted when ERC1155 token is deployed
    event ERC1155Minted(address owner, address tokenContract, uint amount); //emmited when ERC1155 token is minted

    /*
    deployERC1155 - deploys a ERC1155 token with given parameters - returns deployed address

    _contractName - name of our ERC1155 token
    _uri - URI resolving to our hosted metadata
    _ids - IDs the ERC1155 token should contain
    _name - Names each ID should map to. Case-sensitive.
    */
    function deployERC1155(string memory _contractName, string memory _uri, uint[] memory _ids, string[] memory _names) public returns (address) {
        ERC1155Token t = new ERC1155Token(_contractName, _uri, _names, _ids);
        tokens.push(t);
        indexToContract[tokens.length - 1] = address(t);
        indexToOwner[tokens.length - 1] = tx.origin;
        emit ERC1155Created(msg.sender,address(t));
        return address(t);
    }

    /*
    mintERC1155 - mints a ERC1155 token with given parameters

    _index - index position in our tokens array - represents which ERC1155 you want to interact with
    _name - Case-sensitive. Name of the token (this maps to the ID you created when deploying the token)
    _amount - amount of tokens you wish to mint
    */
    function mintERC1155(uint _index, string memory _name, uint256 amount) public {

        uint id = getIdByName(_index, _name);
        tokens[_index].mint(indexToOwner[_index], id, amount);
        emit ERC1155Minted(tokens[_index].owner(), address(tokens[_index]), amount);
    }

    /*
    Helper functions below retrieve contract data given an ID or name and index in the tokens array.
    */
    function getCountERC1155byIndex(uint256 _index, uint256 _id) public view returns (uint amount) {
        return tokens[_index].balanceOf(indexToOwner[_index], _id);
    }

    function getCountERC1155byName(uint256 _index, string calldata _name) public view returns (uint amount) {
        uint id = getIdByName(_index, _name);
        return tokens[_index].balanceOf(indexToOwner[_index], id);
    }

    function getIdByName(uint _index, string memory _name) public view returns (uint) {
        return tokens[_index].nameToId(_name);
    }

    function getNameById(uint _index, uint _id) public view returns (string memory) {
        return tokens[_index].idToName(_id);
    }

    function getERC1155byIndexAndId(uint _index, uint _id)
        public
        view
        returns (
            address _contract,
            address _owner,
            string memory _uri,
            uint supply
        )
    {
        ERC1155Token token = tokens[_index];
        return (address(token), token.owner(), token.uri(_id), token.balanceOf(indexToOwner[_index], _id));
    }
}

Truffle requires you to have a migration contract in order to use there migrations feature. This migration contract (Migration.sol) can be found in the contracts folder. Our Truffle project also contains a migrations folder which helps us deploy our smart contracts onto the blockchain. We will navigate into the migrations folder and create a file called `2_deploy_migration.js`. Then, input the following code:

var factoryContract = artifacts.require("FactoryERC1155");

module.exports = function(deployer){
  deployer.deploy(factoryContract);
}

Line 1: We tell truffle which contract we want to interact with via an artifact.require() method

Line 3-4: We export a function via module exports that accepts a deployer object as its first parameter and uses that parameter to call a deploy method on our contract.

Now we will run the command `touch .secret` to create a secret file that will contain our account's private key. To find your private key on Metamask, look at the following article: How to Export a Private Key. Once you retrieve your private key, paste the contents into the `.secret` file and save. 

Time to set up our configuration file. Luckily the truffle init command gives us a boilerplate we can edit and configure. Replace the contents of your `truffle-config.js` file with the code below. This file contains the code necessary to create our configuration.

/**
 * Use this file to configure your truffle project. It's seeded with some
 * standard settings for different networks and features like migrations,
 * compilation and testing. Uncomment the ones you need or modify
 * them to suit your project as necessary.
 *
 * More information about configuration can be found at:
 *
 * trufflesuite.com/docs/advanced/configuration
 *
 */

 const HDWalletProvider = require('@truffle/hdwallet-provider');
 const fs = require('fs');
 const privateKey = fs.readFileSync(".secret").toString().trim();
 const QUICKNODE_PROVIDER = "YOUR_POLYGON_HTTP_ENDPOINT"

 module.exports = {
   /**
    * Networks define how you connect to your Ethereum client and let you set the
    * defaults web3 uses to send transactions. If you don't specify one truffle
    * will spin up a development blockchain for you on port 9545 when you
    * run `develop` or `test`. You can ask a truffle command to use a specific
    * network from the command line, e.g
    *
    * $ truffle test --network <network-name>
    */
 
   networks: {
     // Useful for testing. The `development` name is special - truffle uses it by default
     // if it's defined here and no other network is specified at the command line.
     // You should run a client (like ganache-cli, geth or parity) in a separate terminal
     // tab if you use this network and you must also set the `host`, `port` and `network_id`
     // options below to some value.
     //
     //development: {
     //host: "127.0.0.1",     // Localhost (default: none)
     //port: 8545,            // Standard Ethereum port (default: none)
     //network_id: "*",       // Any network (default: none)
    //},
     // Another network with more advanced options...
     // advanced: {
     // port: 8777,             // Custom port
     // network_id: 1342,       // Custom network
     // gas: 8500000,           // Gas sent with each transaction (default: ~6700000)
     // gasPrice: 20000000000,  // 20 gwei (in wei) (default: 100 gwei)
     // from: <address>,        // Account to send txs from (default: accounts[0])
     // websocket: true        // Enable EventEmitter interface for web3 (default: false)
     // },
     // Useful for deploying to a public network.
     // NB: It's important to wrap the provider as a function.
      polygon: {
      provider: () => new HDWalletProvider(privateKey, QUICKNODE_PROVIDER),
      network_id: 137,
      gasPrice: 40000000000,
      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 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.8.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: true,
           runs: 200
         },
        
       //  evmVersion: "byzantium"
        }
     }
   },
 
   // Truffle DB is currently disabled by default; to enable it, change enabled:
   // false to enabled: true. The default storage location can also be
   // overridden by specifying the adapter settings, as shown in the commented code below.
   //
   // NOTE: It is not possible to migrate your contracts to truffle DB and you should
   // make a backup of your artifacts to a safe location before enabling this feature.
   //
   // After you backed up your artifacts you can utilize db by running migrate as follows: 
   // $ truffle migrate --reset --compile-all
   //
   // db: {
     // enabled: false,
     // host: "127.0.0.1",
     // adapter: {
     //   name: "sqlite",
     //   settings: {
     //     directory: ".db"
     //   }
     // }
   // }
   plugins: [
    'truffle-plugin-verify'
  ],
  api_keys: {
    polygonscan: 'YOUR_POLYGONSCAN_APY_KEYS'
  }
 };

Before moving onto the next step, make sure the following information is filled in: 

1. Remember to assign the QuickNode HTTP Endpoint you retrieved in the previous step to the variable `YOUR_POLYGON_MAINNET_HTTP_ENDPOINT`. 
2. Navigate to Polygonscan's signup page and create an account. Once you are logged in, go to API Keys, and click "Add" to create API Keys. Once created, assign these keys to the variable `YOUR_POLYGONSCAN_API_KEY` in the truffle.config.js file under `api_keys`.

polygonscan api keys page

Before you compile, confirm that your project directory is setup correctly:

screenshot of project directory

Save the file and then run the command `truffle compile`. Unless otherwise specified, this command will compile contracts that have changed since the last compilation.

Truffle compilation details

Deploy and Mint NFT

Note, before we run the last command to deploy our smart contract, we want to make sure we have at least a couple MATIC in the wallet we will be deploying from.

It's time! Run the command `truffle migrate --network polygon` to deploy your Factory ERC-1155 contract to Polygon main-net.

You can also add the tag `--reset` to the command above to run migrations from the beginning. This will be useful when we have edited our migrations content and will need to re-compile from the start.

After the command is executed, you should see something similar to the following output:

log of migrations deployment

log of migrations deployment


If you see output similar to the above, congrats! You can copy the transaction hash from the `ERC1155Factory.sol` migration process and paste it into Polygonscan to see the created contract. 

In order to make calls via Polygonscan and allow others to view our source code, run the following command in your terminal: `truffle run verify FactoryERC1155  --network polygon`. This handy command will verify your contracts so everyone can see the source code and can use the call/write functions on your contract.

log of verification


Once your contract is verified, navigate to your deployed contract address on Polygonscan, click the "Write Contract" button on the Contract tab and connect your wallet.

Contract tab on Polygonscan


After you connect your wallet, navigate to the `deployERC1155` function. This function takes four arguments:

- the name of the ERC-1155 token
- the URI for metadata collection.
- IDs of your NFT collection.
- names of your NFTs.

Take a moment to upload your image and metadata assets to NFT.Storage. If you need to learn how, take a look at the "Creating Metadata URI" section in this QuickNode guide. If you want to create the same collection as this guide, use the following inputs:

- SolarSystem
- https://bafybeigrfsyjsgjcapbehtpfttm3z5arfs6amwo2ni4nz2pgcs65fb65di.ipfs.nftstorage.link/
- ["Mercury","Venus","Earth", "Mars", "Neptune","Uranus","Saturn","Jupiter"]
- [1,2,3,4,5,6,7,8] 

Interact with the deployERC1155 function on Polygonscan

We can look at the transaction hash for more details:

image of transaction receipt for function call to deployERC1155
Note that you can click the logs tab to see the events emitted.

The transaction receipt above displays our factory contract deployed but we have not minted any NFTs yet. To mint NFTs from our Solar System collection, we need to call the  `mintERC1155` function. This function takes three parameters: 

 1. the index of the ERC-1155 you want to interact with. 
 2. The name of the token you wish to mint. 
 3. The number of tokens you want to mint.
 
In this example, we will be minting an NFT of the planet Mercury. Here's how the transaction call and receipt would look:

Interact with the mintERC1155 function on Polygonscan

Transaction receipt on Polygonscan for the function call mintERC1155

Now that we have minted an ERC-1155 token, you can use the function `getERC1155byIndexAndId` to get more information:

Contract read functions displayed on polygonscan

And that's a wrap! If you followed along with us, you should now have your newly minted NFTs in your wallet. Here's what ours looks like:

Wallet activity on polygonscan showing ERC-1155 tokens

We can also go to our profile on OpenSea and see the minted NFT there: 

OpenSea page containing minted NFT of Mercury

Conclusion

If you made it this far, congrats! We know this guide was quite extensive, but hopefully, you learned a few things! A brief recap of what we went over in this guide: 

1. How to deploy a factory ERC-1155 smart contract on the Polygon blockchain using Truffle 
2. How to mint ERC-1155 tokens from our factory contract. 
3. How to verify our contract code to be visible on Polygonscan. 
4. Making our ERC-1155 contract OpenSea compatible.

Subscribe to our newsletter for more articles and guides on Ethereum. Feel free to reach out to us via Twitter if you have any feedback. You can always chat with us on our Discord community server, featuring some of the coolest developers you'll ever meet :)
Ready to try QuickNode? Start your 7-day free trial

Related articles 14

Apr 12, 2022 How to Deploy a Smart Contract on MATIC/Polygon

Ethereum is a very lovely blockchain to work with, but recently, heavy traffic and many people building on it have resulted in the chain being a bit congested. Layer 2 solutions solve this issue by extending Ethereum's scalability. Polygon (FKA MATIC) is one such...

Continue reading
Apr 12, 2022 How to build an Ethereum URL shortener dApp

dApps (decentralized applications) are an integral part of the Ethereum development ecosystem. There are thousands of dApps already residing on the Ethereum blockchain. In this guide, we will learn how to build an Ethereum dApp that...

Continue reading
Jan 31, 2022 How to Create a BEP20 Token

BEP20 is the Binance Smart Chain equivalent to the popular ERC20 specification for tokens on the Ethereum network. The difference is the BEP20 Token isn't run on the Ethereum Network, but the Binance Smart Chain network. Despite this difference, because BSC (Binance Smart...

Continue reading
Dec 27, 2021 An overview of how smart contracts work on Ethereum

Smart contracts are the first-class citizens in the Ethereum blockchain; they are a critical part of Ethereum development. In this guide, we’ll see an overview of how smart contracts work on the Ethereum blockchain. This will include how to set up an Ethereum IDE and then...

Continue reading
Mar 7, 2022 How to create and deploy an ERC20 token

Ethereum network’s launch in 2015 created a lot of buzz in the developer community and sprouted a lot of tokens on the network. Initially there weren’t any templates or guidelines for token development. This resulted in a variety of tokens quite different from each other....

Continue reading
Dec 27, 2021 How to create your own Oracle with an Ethereum smart contract

Ethereum smart contracts are the most valuable features of the Ethereum blockchain network; The development of applications driven by smart contracts on the Ethereum blockchain has skyrocketed recently. These smart-contracts are sandboxed and can’t access the data...

Continue reading
Dec 27, 2021 What is an ABI?

While interacting with a smart contract ABI is one of the essential components. In this guide, let us understand what the ABI of smart contracts is.

Continue reading
Dec 27, 2021 How to call another smart contract from your solidity code

Smart contracts are the most popular feature of the Ethereum network. Smart contracts have functions that sometimes need to be accessed by other smart contracts. Before understanding smart contracts' interactions, let's brush up on our basics about smart...

Continue reading
Dec 27, 2021 How to create and deploy an ERC-721 (NFT)

Digital collectibles compatible with the ERC-721 standard have become very popular since the launch of Cryptokitties and have moved forward towards mass adoption in recent months. This guide will cover creating and deploying our...

Continue reading
Dec 27, 2021 How to write an Ethereum smart contract using Solidity

This article is intended for developers new to Ethereum development. In this article, we will talk about Solidity and smart contracts, What they are and what role they actually play in the ethereum development with the end goal of writing a smart contract using...

Continue reading
Mar 14, 2022 How to Create and Deploy an ERC-1155 NFT

ERC1155 has emerged as a gold standard to create NFTs; every major marketplace lists new tokens as an ERC1155 standard. In this guide, we will learn about the ERC1155 token standard and how to create an ERC1155 token.What we will...

Continue reading
Jan 31, 2022 Como crear un Token BEP20

BEP20 es en Binance Smart Chain el equivalente al popular token ERC20 de la red de Ethereum. La diferencia es que el token BEP20 no corre en la red de Ethereum sino en la red de Binance Smart Chain. Más allá de esta diferencia, como BSC (Binance Smart Chain) implementa la...

Continue reading
Feb 12, 2022 How to Create a Smart Contract Factory in Solidity using Hardhat

Hello reader! Today we are going to be leveling up your Solidity skills. We are going to walk you through how to implement the factory contract pattern. Do not worry if you have not heard this term before; all will be explained to you by the end of the...

Continue reading
Apr 12, 2022 How to Create a Dutch Auction Smart Contract

Often to perform any transaction between two untrusted parties, there is a need for a trusted middle man; smart contracts eliminate this need for a middle man altogether. In this guide, we will learn how to create a dutch auction smart contract which will make the entire...

Continue reading