Marketplace has launched, further enabling blockchain developers! Learn more

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

September 23, 2022

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:

create and compile core contracts

Copy
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:

create and compile core contracts

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

create and compile core contracts

Copy
/**
 * 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 :)

Related articles 18

Solidity vs Vyper
Originally Published On: Aug 18, 2021
Updated On: Sep 23, 2022

With the introduction to smart... When we think of blockchains,... Introduction IntroductionVyper is... Now, the million dollar question...

Continue reading
How to write an Ethereum smart contract using Solidity
Originally Published On: Apr 5, 2020
Updated On: Sep 23, 2022

This article is intended for... Before getting started with... The smart contract term was... As we saw that smart contracts... Now, let's write a simple smart... After writing a smart contract... So we have successfully created...

Continue reading
How to Setup Local Development Environment for Solidity
Originally Published On: Jul 10, 2021
Updated On: Sep 23, 2022

The success story of blockchain... According to the Now that you are geared up with... Installation Now that our contract is ready... Now, let's write some test... The last thing, we need to do is... For deploying using hardhat, you... Here we saw how to work with...

Continue reading
How to Deploy a Smart Contract on MATIC/Polygon
Originally Published On: Oct 27, 2021
Updated On: Sep 23, 2022

Ethereum is a very lovely... With the rapid adoption of... We'll deploy our contract on... After creating your QuickNode... We will need some test MATIC to... Time to write our smart... Now click on the Solidity logo... A big thank you and kudos if you...

Continue reading
How to Create a BEP20 Token
Originally Published On: Jul 3, 2021
Updated On: Sep 23, 2022

BEP20 is the Binance Smart Chain... The first thing you will want to... The Next thing we will need to... Because BSC uses the EVM, the... With the initial BEP20.sol... In order to view this in your... Congratulations! You made it to...

Continue reading
What is an ABI?
Originally Published On: Mar 20, 2021
Updated On: Sep 23, 2022

While interacting with a smart... ABI (Application Binary... The JSON format of a contract’s... One of the most common ways is... Now that you know what ABI is,...

Continue reading
How to call another smart contract from your solidity code
Originally Published On: Jan 30, 2021
Updated On: Sep 23, 2022

Smart contracts are the most... Smart contracts are computer... We’ll deploy our contracts on... Now to achieve our goal we'll... Now you will see two contracts... Now click on count button under... Now let's update the value of... Let's check if the INTERFACE... Here we successfully saw how we...

Continue reading
How to create and deploy an ERC-721 (NFT)
Originally Published On: Mar 6, 2021
Updated On: Sep 23, 2022

Digital collectibles compatible... Fungible means to be the same or... ERC stands for Ethereum Request... Digital art (or... Now that we know what ERC-721... Before writing our NFT contract,... For ease and security, we’ll use... Congratulations on creating your...

Continue reading
How to deploy a smart contract with Brownie
Originally Published On: Jul 23, 2021
Updated On: Sep 23, 2022

Python is one of the most... Smart contract development is... Brownie is built on top of... First of all, lets cd into the... We will deploy our contract on... We need to set up our QuickNode... As our contract will be deployed... Before deploying the contract,... So, today we learned brownies...

Continue reading
How to Write a Secure Smart Contract in Vyper
Originally Published On: Feb 2, 2022
Updated On: Sep 23, 2022

Hey readers! If you are familiar... In this project we will be... First things first, if we are... We can begin by writing our... Here we will write the code... Congrats on making it to the...

Continue reading
Como crear un Token BEP20
Originally Published On: Jan 10, 2022
Updated On: Sep 23, 2022

BEP20 es en Binance Smart Chain... La primera cosa que hay que... El próximo paso que deberás... Como BSC usa la EVM, el código... Con el contrato BEP20.sol... Para poder ver esto en la... ¡Felicidades! Acabas de terminar...

Continue reading
How to Create a Dutch Auction Smart Contract
Originally Published On: Feb 11, 2022
Updated On: Sep 23, 2022

Often to perform any transaction... Auctions are platforms for... A Dutch auction, also known as... We will deploy our contract on... Before moving further, it is... Now that our Dutch auction... If you made it here,...

Continue reading