A Simple Guide to ERC-1155 Tokens on Ethereum
Explore the versatility of Ethereum's ERC-1155 token standard, learn how it streamlines digital asset management, and follow a step-by-step guide to creating and deploying your own tokens.
Ethereum's blockchain has significantly transformed the creation and management of digital assets. Among these advancements, the ERC-1155 standard emerges as a notable innovation. Introduced by the team at Enjin, ERC-1155 is designed to streamline and enhance the efficiency of digital assets across the blockchain by enabling the creation of fungible, non-fungible, and semi-fungible tokens—all within a single smart contract.
ERC-1155 was developed to address inefficiencies in previous token standards such as ERC-20 and ERC-721. While ERC-20 is suited for fungible tokens (where each token is identical and interchangeable), and ERC-721 for non-fungible tokens (where each token is unique), these standards have limitations in scope and efficiency when handling multiple token types.
Some of the key benefits of ERC-1155 are:
- Batch Transfers: ERC-1155 supports batch transfers, allowing multiple token types to be moved in a single transaction. This significantly reduces gas costs and increases transaction speed.
- Single Contract for Multiple Tokens: This standard enables a single contract to manage both fungible and non-fungible tokens, simplifying deployment and reducing costs.
- Safe Transfer Checks: ERC-1155 ensures that tokens are sent to compatible addresses, enhancing transaction safety and preventing losses due to errors.
Here are some practical applications of ERC-1155:
- Gaming: Allows for an entire economy of in-game items, from consumables to rare gear, to be managed within one contract.
- Digital Art: Enables artists to issue both limited and open-edition pieces seamlessly.
- Tokenized Assets: Real-world assets can be tokenized and managed using ERC-1155.
Writing and deploying an ERC-1155 token
In this tutorial, we will create a basic ERC-1155 token called RAD Token. This example will demonstrate how different assets can be managed within a single contract framework.
- Set up your development environment: Before you begin, ensure you have Node.js and npm installed. Then, set up your Hardhat project:
mkdir rad-token
cd rad-token
npm init -y
- Install Hardhat and OpenZeppelin contracts
npm install --save-dev hardhat
npm install @openzeppelin/contracts
- Initialize Hardhat
npx hardhat
Choose "Create an empty hardhat.config.js" when prompted.
- Write the RADToken contract: Create a new file contracts/RADToken.sol and paste the contract code below.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract RADToken is ERC1155, Ownable {
constructor(address initialOwner, string memory baseURI) ERC1155(baseURI) Ownable(initialOwner) {
// Initialize contract with metadata URI
}
function mint(address account, uint256 id, uint256 amount, bytes memory data)
public
onlyOwner
{
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyOwner
{
_mintBatch(to, ids, amounts, data);
}
function setURI(string memory newuri) public onlyOwner {
_setURI(newuri);
}
function uri(uint256 tokenId) public view override returns (string memory) {
return string(abi.encodePacked(super.uri(tokenId), Strings.toString(tokenId), ".json"));
}
function burn(address account, uint256 id, uint256 amount) public onlyOwner {
_burn(account, id, amount);
}
function burnBatch(address account, uint256[] memory ids, uint256[] memory amounts)
public
onlyOwner
{
_burnBatch(account, ids, amounts);
}
}
- Write the deployment script: Create a new file ignition/modules/RadToken.js for the Hardhat Ignition deployment module and add the code below.
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");
module.exports = buildModule("RADTokenModule", (m) => {
const deployer = m.getAccount(0);
const baseURI = m.getParameter(
"baseURI",
"<https://gateway.pinata.cloud/ipfs/Qmar8RRAZ5QoVPTzvrRk9PcFSm9YKbkanxmd951nXk2bgM/>"
);
const radToken = m.contract("RADToken", [deployer, baseURI]);
return { radToken };
});
- Write test scripts: Create a new file test/RadToken.js for testing the contract and add the code below:
const { expect } = require("chai");
const { ethers } = require("hardhat");
const {
loadFixture,
} = require("@nomicfoundation/hardhat-toolbox/network-helpers");
describe("RADToken", function () {
async function deployRADTokenFixture() {
const [owner, addr1, addr2, ...addrs] = await ethers.getSigners();
const RADToken = await ethers.getContractFactory("RADToken");
const baseURI =
"https://gateway.pinata.cloud/ipfs/Qmar8RRAZ5QoVPTzvrRk9PcFSm9YKbkanxmd951nXk2bgM/";
const radToken = await RADToken.deploy(owner.address, baseURI);
return { radToken, owner, addr1, addr2, addrs };
}
describe("Minting", function () {
it("Should mint a token", async function () {
const { radToken, addr1 } = await loadFixture(deployRADTokenFixture);
await radToken.mint(addr1.address, 1, 100, "0x");
expect(await radToken.balanceOf(addr1.address, 1)).to.equal(100);
});
it("Should mint multiple tokens in batch", async function () {
const { radToken, addr1 } = await loadFixture(deployRADTokenFixture);
await radToken.mintBatch(addr1.address, [1, 2], [100, 200], "0x");
expect(await radToken.balanceOf(addr1.address, 1)).to.equal(100);
expect(await radToken.balanceOf(addr1.address, 2)).to.equal(200);
});
});
describe("URI Management", function () {
it("Should return correct token URI", async function () {
const { radToken } = await loadFixture(deployRADTokenFixture);
expect(await radToken.uri(1)).to.equal(
"https://gateway.pinata.cloud/ipfs/Qmar8RRAZ5QoVPTzvrRk9PcFSm9YKbkanxmd951nXk2bgM/1.json"
);
});
it("Should set new URI and return correct token URI", async function () {
const { radToken } = await loadFixture(deployRADTokenFixture);
const newBaseURI = "https://newbase.uri/";
await radToken.setURI(newBaseURI);
expect(await radToken.uri(1)).to.equal("https://newbase.uri/1.json");
});
});
});
- Test the contract code: Run the tests with the following command:
npx hardhat test
- Deploy the RADToken contract: Compile and deploy the contract with the following commands:
npx hardhat compile
npx hardhat ignition --network localhost --show-logs ignition/modules/RadToken.js
What next?
ERC-1155 represents a significant advancement in Ethereum's token standards, offering greater flexibility, efficiency, and utility than its predecessors. The RAD Token example demonstrates the ease with which multiple token types can be managed under a single contract, opening the door for a new generation of decentralized applications.
Useful resources
Keep reading our latest stories
Developers, security news, and more