diff --git a/contracts/mocks/ERC20PausableMock.sol b/contracts/mocks/ERC20PausableMock.sol index 639941c7f55..fcf27b6cd29 100644 --- a/contracts/mocks/ERC20PausableMock.sol +++ b/contracts/mocks/ERC20PausableMock.sol @@ -20,4 +20,12 @@ contract ERC20PausableMock is ERC20Pausable { function unpause() external { _unpause(); } + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public { + _burn(from, amount); + } } diff --git a/contracts/token/ERC20/ERC20Pausable.sol b/contracts/token/ERC20/ERC20Pausable.sol index 5aa21a6038a..8860c9ebfe9 100644 --- a/contracts/token/ERC20/ERC20Pausable.sol +++ b/contracts/token/ERC20/ERC20Pausable.sol @@ -4,8 +4,7 @@ import "./ERC20.sol"; import "../../utils/Pausable.sol"; /** - * @title Pausable token - * @dev ERC20 with pausable transfers and allowances. + * @dev ERC20 token with pausable token transfers, minting and burning. * * Useful for scenarios such as preventing trades until the end of an evaluation * period, or having an emergency switch for freezing all token transfers in the diff --git a/contracts/token/ERC721/ERC721Pausable.sol b/contracts/token/ERC721/ERC721Pausable.sol index 50da11c375b..ff77a24025b 100644 --- a/contracts/token/ERC721/ERC721Pausable.sol +++ b/contracts/token/ERC721/ERC721Pausable.sol @@ -4,10 +4,20 @@ import "./ERC721.sol"; import "../../utils/Pausable.sol"; /** - * @title ERC721 Non-Fungible Pausable token - * @dev ERC721 modified with pausable transfers. + * @dev ERC721 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. */ abstract contract ERC721Pausable is ERC721, Pausable { + /** + * @dev See {ERC721-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override { super._beforeTokenTransfer(from, to, tokenId); diff --git a/contracts/token/ERC777/ERC777.sol b/contracts/token/ERC777/ERC777.sol index 7f130ee595b..bd7880b6528 100644 --- a/contracts/token/ERC777/ERC777.sol +++ b/contracts/token/ERC777/ERC777.sol @@ -322,6 +322,8 @@ contract ERC777 is Context, IERC777, IERC20 { address operator = _msgSender(); + _beforeTokenTransfer(operator, address(0), account, amount); + // Update state variables _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); @@ -382,6 +384,8 @@ contract ERC777 is Context, IERC777, IERC20 { address operator = _msgSender(); + _beforeTokenTransfer(operator, from, address(0), amount); + _callTokensToSend(operator, from, address(0), amount, data, operatorData); // Update state variables diff --git a/test/token/ERC20/ERC20Pausable.test.js b/test/token/ERC20/ERC20Pausable.test.js index b43e820628f..dd175269e93 100644 --- a/test/token/ERC20/ERC20Pausable.test.js +++ b/test/token/ERC20/ERC20Pausable.test.js @@ -78,5 +78,59 @@ describe('ERC20Pausable', function () { ); }); }); + + describe('mint', function () { + const amount = new BN('42'); + + it('allows to mint when unpaused', async function () { + await this.token.mint(recipient, amount); + + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); + }); + + it('allows to mint when paused and then unpaused', async function () { + await this.token.pause(); + await this.token.unpause(); + + await this.token.mint(recipient, amount); + + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); + }); + + it('reverts when trying to mint when paused', async function () { + await this.token.pause(); + + await expectRevert(this.token.mint(recipient, amount), + 'ERC20Pausable: token transfer while paused' + ); + }); + }); + + describe('burn', function () { + const amount = new BN('42'); + + it('allows to burn when unpaused', async function () { + await this.token.burn(holder, amount); + + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(amount)); + }); + + it('allows to burn when paused and then unpaused', async function () { + await this.token.pause(); + await this.token.unpause(); + + await this.token.burn(holder, amount); + + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(amount)); + }); + + it('reverts when trying to burn when paused', async function () { + await this.token.pause(); + + await expectRevert(this.token.burn(holder, amount), + 'ERC20Pausable: token transfer while paused' + ); + }); + }); }); }); diff --git a/test/token/ERC721/ERC721Pausable.test.js b/test/token/ERC721/ERC721Pausable.test.js index a1586284740..659757ee7d9 100644 --- a/test/token/ERC721/ERC721Pausable.test.js +++ b/test/token/ERC721/ERC721Pausable.test.js @@ -19,7 +19,8 @@ describe('ERC721Pausable', function () { context('when token is paused', function () { const firstTokenId = new BN(1); - const mintedTokens = new BN(1); + const secondTokenId = new BN(1337); + const mockData = '0x42'; beforeEach(async function () { @@ -49,6 +50,20 @@ describe('ERC721Pausable', function () { ); }); + it('reverts when trying to mint', async function () { + await expectRevert( + this.token.mint(receiver, secondTokenId), + 'ERC721Pausable: token transfer while paused' + ); + }); + + it('reverts when trying to burn', async function () { + await expectRevert( + this.token.burn(firstTokenId), + 'ERC721Pausable: token transfer while paused' + ); + }); + describe('getApproved', function () { it('returns approved address', async function () { const approvedAccount = await this.token.getApproved(firstTokenId); @@ -59,7 +74,7 @@ describe('ERC721Pausable', function () { describe('balanceOf', function () { it('returns the amount of tokens owned by the given address', async function () { const balance = await this.token.balanceOf(owner); - expect(balance).to.be.bignumber.equal(mintedTokens); + expect(balance).to.be.bignumber.equal('1'); }); });