Unverified Commit 406c8364 by Hadrien Croubois Committed by GitHub

Introduce ERC1155 totalSupply() and exists() functions (#2593)

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
parent 5f50b9f6
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* `Counter`: add a reset method. ([#2678](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2678)) * `Counter`: add a reset method. ([#2678](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2678))
* Tokens: Wrap definitely safe subtractions in `unchecked` blocks. * Tokens: Wrap definitely safe subtractions in `unchecked` blocks.
* `Math`: Add a `ceilDiv` method for performing ceiling division. * `Math`: Add a `ceilDiv` method for performing ceiling division.
* `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593))
### Breaking Changes ### Breaking Changes
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ERC1155Mock.sol";
import "../token/ERC1155/extensions/ERC1155Supply.sol";
contract ERC1155SupplyMock is ERC1155Mock, ERC1155Supply {
constructor(string memory uri) ERC1155Mock(uri) { }
function _mint(address account, uint256 id, uint256 amount, bytes memory data) internal virtual override(ERC1155, ERC1155Supply) {
super._mint(account, id, amount, data);
}
function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual override(ERC1155, ERC1155Supply) {
super._mintBatch(to, ids, amounts, data);
}
function _burn(address account, uint256 id, uint256 amount) internal virtual override(ERC1155, ERC1155Supply) {
super._burn(account, id, amount);
}
function _burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) internal virtual override(ERC1155, ERC1155Supply) {
super._burnBatch(account, ids, amounts);
}
}
...@@ -32,6 +32,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel ...@@ -32,6 +32,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC1155Burnable}} {{ERC1155Burnable}}
{{ERC1155Supply}}
== Presets == Presets
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC1155.sol";
/**
* @dev Extension of ERC1155 that adds tracking of total supply per id.
*
* Useful for scenarios where Fungible and Non-fungible tokens have to be
* clearly identified. Note: While a totalSupply of 1 might mean the
* corresponding is an NFT, there is no guarantees that no other token with the
* same id are not going to be minted.
*/
abstract contract ERC1155Supply is ERC1155 {
mapping (uint256 => uint256) private _totalSupply;
/**
* @dev Total amount of tokens in with a given id.
*/
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}
/**
* @dev Indicates weither any token exist with a given id, or not.
*/
function exists(uint256 id) public view virtual returns(bool) {
return ERC1155Supply.totalSupply(id) > 0;
}
/**
* @dev See {ERC1155-_mint}.
*/
function _mint(address account, uint256 id, uint256 amount, bytes memory data) internal virtual override {
super._mint(account, id, amount, data);
_totalSupply[id] += amount;
}
/**
* @dev See {ERC1155-_mintBatch}.
*/
function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual override {
super._mintBatch(to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] += amounts[i];
}
}
/**
* @dev See {ERC1155-_burn}.
*/
function _burn(address account, uint256 id, uint256 amount) internal virtual override {
super._burn(account, id, amount);
_totalSupply[id] -= amount;
}
/**
* @dev See {ERC1155-_burnBatch}.
*/
function _burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) internal virtual override {
super._burnBatch(account, ids, amounts);
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] -= amounts[i];
}
}
}
const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC1155SupplyMock = artifacts.require('ERC1155SupplyMock');
contract('ERC1155Supply', function (accounts) {
const [ holder ] = accounts;
const uri = 'https://token.com';
const firstTokenId = new BN('37');
const firstTokenAmount = new BN('42');
const secondTokenId = new BN('19842');
const secondTokenAmount = new BN('23');
beforeEach(async function () {
this.token = await ERC1155SupplyMock.new(uri);
});
context('before mint', function () {
it('exist', async function () {
expect(await this.token.exists(firstTokenId)).to.be.equal(false);
});
it('totalSupply', async function () {
expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal('0');
});
});
context('after mint', function () {
context('single', function () {
beforeEach(async function () {
await this.token.mint(holder, firstTokenId, firstTokenAmount, '0x');
});
it('exist', async function () {
expect(await this.token.exists(firstTokenId)).to.be.equal(true);
});
it('totalSupply', async function () {
expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal(firstTokenAmount);
});
});
context('batch', function () {
beforeEach(async function () {
await this.token.mintBatch(
holder,
[ firstTokenId, secondTokenId ],
[ firstTokenAmount, secondTokenAmount ],
'0x',
);
});
it('exist', async function () {
expect(await this.token.exists(firstTokenId)).to.be.equal(true);
expect(await this.token.exists(secondTokenId)).to.be.equal(true);
});
it('totalSupply', async function () {
expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal(firstTokenAmount);
expect(await this.token.totalSupply(secondTokenId)).to.be.bignumber.equal(secondTokenAmount);
});
});
});
context('after burn', function () {
context('single', function () {
beforeEach(async function () {
await this.token.mint(holder, firstTokenId, firstTokenAmount, '0x');
await this.token.burn(holder, firstTokenId, firstTokenAmount);
});
it('exist', async function () {
expect(await this.token.exists(firstTokenId)).to.be.equal(false);
});
it('totalSupply', async function () {
expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal('0');
});
});
context('batch', function () {
beforeEach(async function () {
await this.token.mintBatch(
holder,
[ firstTokenId, secondTokenId ],
[ firstTokenAmount, secondTokenAmount ],
'0x',
);
await this.token.burnBatch(
holder,
[ firstTokenId, secondTokenId ],
[ firstTokenAmount, secondTokenAmount ],
);
});
it('exist', async function () {
expect(await this.token.exists(firstTokenId)).to.be.equal(false);
expect(await this.token.exists(secondTokenId)).to.be.equal(false);
});
it('totalSupply', async function () {
expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal('0');
expect(await this.token.totalSupply(secondTokenId)).to.be.bignumber.equal('0');
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment