mirror of
https://github.com/OpenZeppelin/openzeppelin-contracts.git
synced 2021-05-29 14:48:28 +03:00
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))
|
||||
* Tokens: Wrap definitely safe subtractions in `unchecked` blocks.
|
||||
* `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
|
||||
|
||||
|
||||
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}}
|
||||
|
||||
{{ERC1155Supply}}
|
||||
|
||||
== 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.
|
||||
|
||||
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