Marketplace has launched, further enabling blockchain developers! Learn more

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

October 19, 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: 

set up project directory

Copy
mkdir nft_project && cd nft_project

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

set up project directory

Copy
npm install -g truffle

To initialize the truffle project, run:

set up project directory

Copy
npx truffle init

Installing necessary dependencies:

set up project directory

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

Create a file called `ERC1155Token.sol` in the contracts directory.

create and compile core contracts

Copy
touch contracts/ERC1155Token.sol

Then open up the file in your text editor and input the following code:

create and compile core contracts

Copy
// contracts/ERC1155Token.sol

// SPDX-License-Identifier: MIT
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. Create a file called `FactoryERC1155.sol` in the contracts directory.

create and compile core contracts

Copy
touch contracts/FactoryERC1155.sol

Then open the file and input the following code:

create and compile core contracts

Copy
// contracts/FactoryERC1155.sol

// SPDX-License-Identifier: MIT
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 create a file called `2_deploy_migration.js` in the migrations folder.

create and compile core contracts

Copy
touch migrations/2_deploy_migration.js

Then, input the following code:

create and compile core contracts

Copy
// migrations/2_deploy_migration.js

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.

create and compile core contracts

Copy
touch .secret
Create a `.gitignore` file so you don't commit your private key.

create and compile core contracts

Copy
echo '.secret' >> .gitignore

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
// truffle-config.js

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: {
    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 )
    },
  },
  compilers: {             // Configure your compilers
    solc: {
      version: "0.8.4",    // Fetch exact version from solc-bin (default: truffle's version)
      settings: {          // See the solidity docs for advice about optimization and evmVersion
        optimizer: {
          enabled: true,
          runs: 200
        },
      }
    }
  },
  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 `npx truffle compile`.

create and compile core contracts

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

deploy and mint nft

Copy
npx truffle migrate --network polygon --reset

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: `npx 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.

deploy and mint nft

Copy
npx truffle run verify FactoryERC1155  --network polygon

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 26

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

With the introduction to smart contracts on the Ethereum blockchain, it was only a matter of time until a language other than Solidity was made to write smart contract code. Vyper is one such...

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

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

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

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
How to create and deploy an ERC20 token
Published: Feb 3, 2021
Updated: Sep 23, 2022

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

Continue reading
How to Create and Deploy an ERC-1155 NFT
Published: Mar 14, 2022
Updated: Sep 23, 2022

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

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

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

Continue reading