Skip to main content

ERC-1155D1

The standard is a gas efficient modification of the EVM standard ERC-1155 but mainly aimed to replace ERC-721 both accepted in 2018 and not changed since then. Only supports NFTs. By contrast with ERC-721 it allows batch minting, burning & transferring.

TL;DR2

ParameterPROS\color{green}PROSCONS\color{red}CONS
Minting Gas Price<51K\color{green}<51K
Transferring Gas Price<35K\color{green}<35K
AuditingNot Audited\color{red}Not~Audited
tokenId enumerationNot Implemented\color{red}Not~Implemented
FT supportNot Implemented\color{red}Not~Implemented
SFT supportNot Implemented\color{red}Not~Implemented
Max Supply5,000\color{red}5,000

Gas Saving Architecture

The updated standard authors followed the blue ocean strategy dropping all the unnecessary but costly functionality leaving only the bear minimum. Unfortunately, a lot of useful functionality was lost as well, such as FT & SFT support, or the maximum number of tokens is restricted to 5,000 due to the implementation limitations.

Gas saving is achieved by fewer storage modifications.

1. Drop of ownerOf function (by contrast with ERC-721)

Becasuse the standard is mainly aimed at replacing ERC-721 one of the gas saving features was the removal of the function that returns the number of owned tokens for a user. By contrast with ERC-721, in ERC-1155D 2*32 bytes of the (address + uint256) are not added to the contract (i.e. blockchain) storage.

Here's the removed part of the ERC-721 contract code3:

// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
...
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _ownerOf(tokenId);
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
...
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
...
function _mint(address to, uint256 tokenId) internal virtual {
...
_owners[tokenId] = to;
...
}

Removal of the above code was motivated by the presence the function balanceOf in ERC-1155 contract, but the way it operates has been modified. For details see the next point.

2. Modification of the balanceOf function

In the original ERC-1155 implementation the function looked like this:

// ORIGINAL ERC-1155
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
require(account != address(0), "ERC1155: address zero is not a valid owner");
return _balances[id][account];
}

In ERC-1155D the function has been modified like so:

function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
require(account != address(0), "ERC1155: balance query for the zero address");
require(id < MAX_SUPPLY, "ERC1155D: id exceeds maximum");

return _owners[id] == account ? 1 : 0;
}

This leads us to the next point, the contract storage.

3. Contract Storage

ERC-721 storage consists of the following slots:

#Slot nameSlot typesStorage Change (bytes)5
1_namestring\color{blue}string0
2_symbolstring\color{blue}string0
3_owners(uint256, address)\color{blue}(uint256,~address)64\color{red}64
4_balances(address, uint256)\color{blue}(address,~uint256)64\color{red}64
5_tokenApprovals(uint256, address)\color{blue}(uint256,~address)04
6_operatorApprovals(address, (address, uint256))\color{blue}(address,~(address,~uint256))05

ERC-1155 storage consists of the following slots:

#Slot nameSlot typesStorage Change (bytes)
1_uristring\color{blue}string0
2_balances(address, uint256)\color{blue}(address,~uint256)64\color{red}64
3_operatorApprovals(address, (address, uint256))\color{blue}(address,~(address,~uint256))05

ERC-1155D storage consists of the following slots:

#Slot nameSlot typesStorage Change (bytes)
1_uristring\color{blue}string0
2_owners(address)\color{blue}(address)32\color{green}32
3_operatorApprovals(address, (address, bool))\color{blue}(address,~(address,~bool))05
4_currentIndexuint256\color{blue}uint25632\color{green}326

Key Takeaways:

  1. The name of the standard is misleading since it replaces ERC721 while it is called ERC-1155D.
  2. The standard is not as universal as it might seem. It only supports NFTs like ERC-721 and does not support FTs and SFTs like the original ERC-1155 would.
  3. The maximum number of tokens is limited to 5,000 while regular contracts are free from this limitation.
  4. The contract has not been audited, so the team planning to use it should invest in going through the decent auditing procedure before using it in production.
  5. The contract consumes 32-32 bytes of the blockchain storage depending on whether the tokenId is handled on- or off-chain. It saves up to 50-75% of the gas fees if compared with ERC-721 and 0-50% if compared with the classical ERC-1155.

Footnotes


  1. For studying the standard ERC-1155 implementation we referred to the folowing repository: https://github.com/DonkeVerse/ERC1155D↩
  2. The pros & cons are compared with the industry standard ERC-1155 and ERC-721 implementations by OpenZeppelin.↩
  3. ... dots in the sourse code indicate presense of other code irrelevant for the current topic. ↩
  4. _tokenApprovals is called independently of minting, burning & transferring and is present in all 3 standards.↩
  5. Here, we only mean slot increase/change during every mint, burn, transfer or related operations. Static variables set during the contract deployment are not taken into account as irrelevant to the topic. Therefore, storage change of the last is considered to be zero for the purposes of this paper. ↩
  6. Since the tokenId tracking is not natively implemented by the contract on GitHub, we used an arbitrary implementation offered in a medium article: https://medium.com/donkeverse/introducing-erc1155d-the-most-efficient-non-fungible-token-contract-in-existence-c1d0a62e30f1#2c37↩