Introduce ERC1155 totalSupply() and exists() functions (#2593)
Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
This commit is contained in:
@ -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
|
||||||
|
|
||||||
|
|||||||
26
contracts/mocks/ERC1155SupplyMock.sol
Normal file
26
contracts/mocks/ERC1155SupplyMock.sol
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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
|
|||||||
|
|
||||||
{{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.
|
||||||
|
|||||||
67
contracts/token/ERC1155/extensions/ERC1155Supply.sol
Normal file
67
contracts/token/ERC1155/extensions/ERC1155Supply.sol
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
test/token/ERC1155/extensions/ERC1155Supply.test.js
Normal file
111
test/token/ERC1155/extensions/ERC1155Supply.test.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user