diff --git a/contracts/mocks/ERC721PausableTokenMock.sol b/contracts/mocks/ERC721PausableTokenMock.sol new file mode 100644 index 00000000000..b48608114b4 --- /dev/null +++ b/contracts/mocks/ERC721PausableTokenMock.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.4.24; + +import "../token/ERC721/ERC721PausableToken.sol"; + + +/** + * @title ERC721PausableTokenMock + * This mock just provides a public mint, burn and exists functions for testing purposes + */ +contract ERC721PausableTokenMock is ERC721PausableToken { + function mint(address _to, uint256 _tokenId) public { + super._mint(_to, _tokenId); + } + + function burn(uint256 _tokenId) public { + super._burn(ownerOf(_tokenId), _tokenId); + } + + function exists(uint256 _tokenId) public view returns (bool) { + return super._exists(_tokenId); + } +} diff --git a/contracts/token/ERC721/ERC721PausableToken.sol b/contracts/token/ERC721/ERC721PausableToken.sol new file mode 100644 index 00000000000..2da188a7edc --- /dev/null +++ b/contracts/token/ERC721/ERC721PausableToken.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.4.24; + +import "./ERC721BasicToken.sol"; +import "../../lifecycle/Pausable.sol"; + + +/** + * @title ERC721 Non-Fungible Pausable token + * @dev ERC721BasicToken modified with pausable transfers. + **/ +contract ERC721PausableToken is ERC721BasicToken, Pausable { + function approve( + address _to, + uint256 _tokenId + ) + public + whenNotPaused + { + super.approve(_to, _tokenId); + } + + function setApprovalForAll( + address _to, + bool _approved + ) + public + whenNotPaused + { + super.setApprovalForAll(_to, _approved); + } + + function transferFrom( + address _from, + address _to, + uint256 _tokenId + ) + public + whenNotPaused + { + super.transferFrom(_from, _to, _tokenId); + } +} diff --git a/test/token/ERC721/ERC721PausableToken.test.js b/test/token/ERC721/ERC721PausableToken.test.js new file mode 100644 index 00000000000..03274ffb2b1 --- /dev/null +++ b/test/token/ERC721/ERC721PausableToken.test.js @@ -0,0 +1,36 @@ +const { shouldBehaveLikeERC721PausedToken } = require('./ERC721PausedToken.behavior'); +const { shouldBehaveLikeERC721BasicToken } = require('./ERC721BasicToken.behavior'); + +const BigNumber = web3.BigNumber; +const ERC721PausableToken = artifacts.require('ERC721PausableTokenMock.sol'); + +require('chai') + .use(require('chai-bignumber')(BigNumber)) + .should(); + +contract('ERC721PausableToken', function ([_, owner, recipient, operator, ...otherAccounts]) { + beforeEach(async function () { + this.token = await ERC721PausableToken.new({ from: owner }); + }); + + context('when token is paused', function () { + beforeEach(async function () { + await this.token.pause({ from: owner }); + }); + + shouldBehaveLikeERC721PausedToken(owner, [...otherAccounts]); + }); + + context('when token is not paused yet', function () { + shouldBehaveLikeERC721BasicToken([owner, ...otherAccounts]); + }); + + context('when token is paused and then unpaused', function () { + beforeEach(async function () { + await this.token.pause({ from: owner }); + await this.token.unpause({ from: owner }); + }); + + shouldBehaveLikeERC721BasicToken([owner, ...otherAccounts]); + }); +}); diff --git a/test/token/ERC721/ERC721PausedToken.behavior.js b/test/token/ERC721/ERC721PausedToken.behavior.js new file mode 100644 index 00000000000..2ef21ec0790 --- /dev/null +++ b/test/token/ERC721/ERC721PausedToken.behavior.js @@ -0,0 +1,88 @@ +const { assertRevert } = require('../../helpers/assertRevert'); +const { sendTransaction } = require('../../helpers/sendTransaction'); + +const BigNumber = web3.BigNumber; + +require('chai') + .use(require('chai-bignumber')(BigNumber)) + .should(); + +function shouldBehaveLikeERC721PausedToken (owner, [recipient, operator]) { + const firstTokenId = 1; + const mintedTokens = 1; + const mockData = '0x42'; + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; + + describe('like a paused ERC721Token', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId, { from: owner }); + }); + + it('reverts when trying to approve', async function () { + await assertRevert(this.token.approve(recipient, firstTokenId, { from: owner })); + }); + + it('reverts when trying to setApprovalForAll', async function () { + await assertRevert(this.token.setApprovalForAll(operator, true, { from: owner })); + }); + + it('reverts when trying to transferFrom', async function () { + await assertRevert(this.token.transferFrom(owner, recipient, firstTokenId, { from: owner })); + }); + + it('reverts when trying to safeTransferFrom', async function () { + await assertRevert(this.token.safeTransferFrom(owner, recipient, firstTokenId, { from: owner })); + }); + + it('reverts when trying to safeTransferFrom with data', async function () { + await assertRevert( + sendTransaction( + this.token, + 'safeTransferFrom', + 'address,address,uint256,bytes', + [owner, recipient, firstTokenId, mockData], + { from: owner } + ) + ); + }); + + describe('getApproved', function () { + it('returns approved address', async function () { + const approvedAccount = await this.token.getApproved(firstTokenId); + approvedAccount.should.be.equal(ZERO_ADDRESS); + }); + }); + + describe('balanceOf', function () { + it('returns the amount of tokens owned by the given address', async function () { + const balance = await this.token.balanceOf(owner); + balance.should.be.bignumber.equal(mintedTokens); + }); + }); + + describe('ownerOf', function () { + it('returns the amount of tokens owned by the given address', async function () { + const ownerOfToken = await this.token.ownerOf(firstTokenId); + ownerOfToken.should.be.equal(owner); + }); + }); + + describe('exists', function () { + it('should return token existance', async function () { + const result = await this.token.exists(firstTokenId); + result.should.eq(true); + }); + }); + + describe('isApprovedForAll', function () { + it('returns the approval of the operator', async function () { + const isApproved = await this.token.isApprovedForAll(owner, operator); + isApproved.should.eq(false); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC721PausedToken, +};