diff --git a/CHANGELOG.md b/CHANGELOG.md index ce747e9ae..bf376f6e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ ### Breaking changes * `ECDSA`: when receiving an invalid signature, `recover` now reverts instead of returning the zero address. ([#2114](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2114)) + * `Pausable`: moved to the `utils` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `Strings`: moved to the `utils` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `Counters`: moved to the `utils` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `SignedSafeMath`: moved to the `math` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `ERC20Snapshot`: moved to the `token/ERC20` directory. `snapshot` was changed into an `internal` function. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) * `Ownable`: moved to the `access` directory. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) * `Ownable`: removed `isOwner`. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) * `Secondary`: removed from the library, use `Ownable` instead. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) diff --git a/contracts/drafts/ERC1046/ERC20Metadata.sol b/contracts/drafts/ERC1046/ERC20Metadata.sol deleted file mode 100644 index 1a768cfc1..000000000 --- a/contracts/drafts/ERC1046/ERC20Metadata.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma solidity ^0.6.0; - -import "../../token/ERC20/IERC20.sol"; - -/** - * @title ERC-1047 Token Metadata - * @dev See https://eips.ethereum.org/EIPS/eip-1046 - * @dev {tokenURI} must respond with a URI that implements https://eips.ethereum.org/EIPS/eip-1047 - */ -contract ERC20Metadata { - string private _tokenURI; - - constructor (string memory tokenURI_) public { - _setTokenURI(tokenURI_); - } - - function tokenURI() external view returns (string memory) { - return _tokenURI; - } - - function _setTokenURI(string memory tokenURI_) internal virtual { - _tokenURI = tokenURI_; - } -} diff --git a/contracts/drafts/README.adoc b/contracts/drafts/README.adoc deleted file mode 100644 index 19e981ae4..000000000 --- a/contracts/drafts/README.adoc +++ /dev/null @@ -1,23 +0,0 @@ -= Drafts - -Contracts in this category should be considered unstable. They are as thoroughly reviewed as everything else in OpenZeppelin Contracts, but we have doubts about their API so we don't commit to backwards compatibility. This means these contracts can receive breaking changes in a minor version, so you should pay special attention to the changelog when upgrading. For anything that is outside of this category you can read more about xref:ROOT:api-stability.adoc[API Stability]. - -NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned! - -== ERC 20 - -{{ERC20Migrator}} - -{{ERC20Snapshot}} - -{{TokenVesting}} - -== Miscellaneous - -{{Counters}} - -{{SignedSafeMath}} - -== ERC 1046 - -{{ERC1046}} diff --git a/contracts/lifecycle/README.adoc b/contracts/lifecycle/README.adoc deleted file mode 100644 index 523207707..000000000 --- a/contracts/lifecycle/README.adoc +++ /dev/null @@ -1,5 +0,0 @@ -= Lifecycle - -== Pausable - -{{Pausable}} diff --git a/contracts/math/README.adoc b/contracts/math/README.adoc index 99789b94b..38d1b9724 100644 --- a/contracts/math/README.adoc +++ b/contracts/math/README.adoc @@ -6,4 +6,6 @@ These are math-related utilities. {{SafeMath}} +{{SignedSafeMath}} + {{Math}} diff --git a/contracts/drafts/SignedSafeMath.sol b/contracts/math/SignedSafeMath.sol similarity index 100% rename from contracts/drafts/SignedSafeMath.sol rename to contracts/math/SignedSafeMath.sol diff --git a/contracts/mocks/CountersImpl.sol b/contracts/mocks/CountersImpl.sol index 2f3b008a1..19e9eb8de 100644 --- a/contracts/mocks/CountersImpl.sol +++ b/contracts/mocks/CountersImpl.sol @@ -1,6 +1,6 @@ pragma solidity ^0.6.0; -import "../drafts/Counters.sol"; +import "../utils/Counters.sol"; contract CountersImpl { using Counters for Counters.Counter; diff --git a/contracts/mocks/ERC20MetadataMock.sol b/contracts/mocks/ERC20MetadataMock.sol deleted file mode 100644 index f18845e11..000000000 --- a/contracts/mocks/ERC20MetadataMock.sol +++ /dev/null @@ -1,12 +0,0 @@ -pragma solidity ^0.6.0; - -import "../token/ERC20/ERC20.sol"; -import "../drafts/ERC1046/ERC20Metadata.sol"; - -contract ERC20MetadataMock is ERC20, ERC20Metadata { - constructor (string memory tokenURI) public ERC20Metadata(tokenURI) { } - - function setTokenURI(string memory tokenURI) public { - _setTokenURI(tokenURI); - } -} diff --git a/contracts/mocks/ERC20SnapshotMock.sol b/contracts/mocks/ERC20SnapshotMock.sol index 5333a964f..9d5c091ca 100644 --- a/contracts/mocks/ERC20SnapshotMock.sol +++ b/contracts/mocks/ERC20SnapshotMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.6.0; -import "../drafts/ERC20Snapshot.sol"; +import "../token/ERC20/ERC20Snapshot.sol"; contract ERC20SnapshotMock is ERC20Snapshot { @@ -8,6 +8,10 @@ contract ERC20SnapshotMock is ERC20Snapshot { _mint(initialAccount, initialBalance); } + function snapshot() public { + _snapshot(); + } + function mint(address account, uint256 amount) public { _mint(account, amount); } diff --git a/contracts/mocks/PausableMock.sol b/contracts/mocks/PausableMock.sol index 2f48a1ad1..548b06428 100644 --- a/contracts/mocks/PausableMock.sol +++ b/contracts/mocks/PausableMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.6.0; -import "../lifecycle/Pausable.sol"; +import "../utils/Pausable.sol"; contract PausableMock is Pausable { bool public drasticMeasureTaken; diff --git a/contracts/mocks/SignedSafeMathMock.sol b/contracts/mocks/SignedSafeMathMock.sol index 33f705f0d..a66fa2b6b 100644 --- a/contracts/mocks/SignedSafeMathMock.sol +++ b/contracts/mocks/SignedSafeMathMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.6.0; -import "../drafts/SignedSafeMath.sol"; +import "../math/SignedSafeMath.sol"; contract SignedSafeMathMock { function mul(int256 a, int256 b) public pure returns (int256) { diff --git a/contracts/mocks/StringsMock.sol b/contracts/mocks/StringsMock.sol index bc19b40c9..383278070 100644 --- a/contracts/mocks/StringsMock.sol +++ b/contracts/mocks/StringsMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.6.0; -import "../drafts/Strings.sol"; +import "../utils/Strings.sol"; contract StringsMock { function fromUint256(uint256 value) public pure returns (string memory) { diff --git a/contracts/token/ERC20/ERC20Pausable.sol b/contracts/token/ERC20/ERC20Pausable.sol index 61d57b805..a29554a86 100644 --- a/contracts/token/ERC20/ERC20Pausable.sol +++ b/contracts/token/ERC20/ERC20Pausable.sol @@ -1,7 +1,7 @@ pragma solidity ^0.6.0; import "./ERC20.sol"; -import "../../lifecycle/Pausable.sol"; +import "../../utils/Pausable.sol"; /** * @title Pausable token diff --git a/contracts/drafts/ERC20Snapshot.sol b/contracts/token/ERC20/ERC20Snapshot.sol similarity index 70% rename from contracts/drafts/ERC20Snapshot.sol rename to contracts/token/ERC20/ERC20Snapshot.sol index 9e2b1558a..0783308d8 100644 --- a/contracts/drafts/ERC20Snapshot.sol +++ b/contracts/token/ERC20/ERC20Snapshot.sol @@ -1,15 +1,12 @@ pragma solidity ^0.6.0; -import "../math/SafeMath.sol"; -import "../utils/Arrays.sol"; -import "../drafts/Counters.sol"; -import "../token/ERC20/ERC20.sol"; +import "../../math/SafeMath.sol"; +import "../../utils/Arrays.sol"; +import "../../utils/Counters.sol"; +import "./ERC20.sol"; /** - * @title ERC20 token with snapshots. - * @dev Inspired by Jordi Baylina's - * https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol[MiniMeToken] - * to record historical balances. + * @dev ERC20 token with snapshots. * * When a snapshot is made, the balances and total supply at the time of the snapshot are recorded for later * access. @@ -21,6 +18,9 @@ import "../token/ERC20/ERC20.sol"; * @author Validity Labs AG */ contract ERC20Snapshot is ERC20 { + // Inspired by Jordi Baylina's MiniMeToken to record historical balances: + // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol + using SafeMath for uint256; using Arrays for uint256[]; using Counters for Counters.Counter; @@ -40,10 +40,12 @@ contract ERC20Snapshot is ERC20 { event Snapshot(uint256 id); - // Creates a new snapshot id. Balances are only stored in snapshots on demand: unless a snapshot was taken, a - // balance change will not be recorded. This means the extra added cost of storing snapshotted balances is only paid - // when required, but is also flexible enough that it allows for e.g. daily snapshots. - function snapshot() public virtual returns (uint256) { + /** + * @dev Creates a new snapshot id. Balances are only stored in snapshots on demand: unless a snapshot was taken, a + * balance change will not be recorded. This means the extra added cost of storing snapshotted balances is only paid + * when required, but is also flexible enough that it allows for e.g. daily snapshots. + */ + function _snapshot() internal virtual returns (uint256) { _currentSnapshotId.increment(); uint256 currentId = _currentSnapshotId.current(); @@ -87,19 +89,6 @@ contract ERC20Snapshot is ERC20 { super._burn(account, value); } - // When a valid snapshot is queried, there are three possibilities: - // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never - // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds - // to this id is the current one. - // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the - // requested id, and its value is the one to return. - // c) More snapshots were created after the requested one, and the queried value was later modified. There will be - // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is - // larger than the requested one. - // - // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if - // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does - // exactly this. function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { @@ -107,6 +96,20 @@ contract ERC20Snapshot is ERC20 { // solhint-disable-next-line max-line-length require(snapshotId <= _currentSnapshotId.current(), "ERC20Snapshot: nonexistent id"); + // When a valid snapshot is queried, there are three possibilities: + // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never + // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds + // to this id is the current one. + // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the + // requested id, and its value is the one to return. + // c) More snapshots were created after the requested one, and the queried value was later modified. There will be + // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is + // larger than the requested one. + // + // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if + // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does + // exactly this. + uint256 index = snapshots.ids.findUpperBound(snapshotId); if (index == snapshots.ids.length) { diff --git a/contracts/token/ERC20/README.adoc b/contracts/token/ERC20/README.adoc index 4524a2d08..9b46537d7 100644 --- a/contracts/token/ERC20/README.adoc +++ b/contracts/token/ERC20/README.adoc @@ -43,6 +43,8 @@ NOTE: This page is incomplete. We're working to improve it for the next release. {{ERC20Capped}} +{{ERC20Snapshot}} + == Utilities {{SafeERC20}} diff --git a/contracts/token/ERC721/ERC721.sol b/contracts/token/ERC721/ERC721.sol index b8470fa9e..e22699486 100644 --- a/contracts/token/ERC721/ERC721.sol +++ b/contracts/token/ERC721/ERC721.sol @@ -5,7 +5,7 @@ import "./IERC721.sol"; import "./IERC721Receiver.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; -import "../../drafts/Counters.sol"; +import "../../utils/Counters.sol"; import "../../introspection/ERC165.sol"; /** diff --git a/contracts/token/ERC721/ERC721Pausable.sol b/contracts/token/ERC721/ERC721Pausable.sol index efe9309be..3d422f89d 100644 --- a/contracts/token/ERC721/ERC721Pausable.sol +++ b/contracts/token/ERC721/ERC721Pausable.sol @@ -1,7 +1,7 @@ pragma solidity ^0.6.0; import "./ERC721.sol"; -import "../../lifecycle/Pausable.sol"; +import "../../utils/Pausable.sol"; /** * @title ERC721 Non-Fungible Pausable token diff --git a/contracts/drafts/Counters.sol b/contracts/utils/Counters.sol similarity index 100% rename from contracts/drafts/Counters.sol rename to contracts/utils/Counters.sol diff --git a/contracts/lifecycle/Pausable.sol b/contracts/utils/Pausable.sol similarity index 100% rename from contracts/lifecycle/Pausable.sol rename to contracts/utils/Pausable.sol diff --git a/contracts/utils/README.adoc b/contracts/utils/README.adoc index e994449ae..9e56e5b07 100644 --- a/contracts/utils/README.adoc +++ b/contracts/utils/README.adoc @@ -10,8 +10,14 @@ Miscellaneous contracts containing utility functions, often related to working w {{Arrays}} +{{Counters}} + +{{Strings}} + {{EnumerableSet}} {{Create2}} {{ReentrancyGuard}} + +{{Pausable}} diff --git a/contracts/drafts/Strings.sol b/contracts/utils/Strings.sol similarity index 71% rename from contracts/drafts/Strings.sol rename to contracts/utils/Strings.sol index a441eaa60..6977c809f 100644 --- a/contracts/drafts/Strings.sol +++ b/contracts/utils/Strings.sol @@ -6,11 +6,12 @@ pragma solidity ^0.6.0; */ library Strings { /** - * @dev Converts a `uint256` to a `string`. - * via OraclizeAPI - MIT licence - * https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + * @dev Converts a `uint256` to its ASCII `string` representation. */ function fromUint256(uint256 value) internal pure returns (string memory) { + // Inspired by OraclizeAPI's implementation - MIT licence + // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + if (value == 0) { return "0"; } diff --git a/docs/modules/ROOT/pages/drafts.adoc b/docs/modules/ROOT/pages/drafts.adoc new file mode 100644 index 000000000..d1793449b --- /dev/null +++ b/docs/modules/ROOT/pages/drafts.adoc @@ -0,0 +1,19 @@ += Drafts + +All draft contracts were either moved into a different directory or removed from the OpenZeppelin Contracts library on the https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256[v3.0.0 release]. + +* `ERC20Migrator`: removed. +* xref:api:token/ERC20.adoc#ERC20Snapshot[`ERC20Snapshot`]: moved to `token/ERC20`. +* `ERC20Detailed` and `ERC1046`: removed. +* `TokenVesting`: removed. Pending a replacement that is being discussed in https://github.com/OpenZeppelin/openzeppelin-contracts/issues/1214[`#1214`]. +* xref:api:utils.adoc#Counters[`Counters`]: moved to xref:api:utils.adoc[`utils`]. +* xref:api:utils.adoc#Strings[`Strings`]: moved to xref:api:utils.adoc[`utils`]. +* xref:api:utils.adoc#SignedSafeMath[`SignedSafeMath`]: moved to xref:api:utils.adoc[`utils`]. + +Removed contracts are still available on the v2.5 release of OpenZeppelin Contracts, which you can install by running: + +```console +$ npm install @openzeppelin/contracts@v2.5 +``` + +Refer to the xref:2.x@contracts:api:utils.adoc[v2.x documentation] when working with them. diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 9708710fd..c88a248af 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -15,7 +15,7 @@ Here's what a contract for tokenized items might look like: pragma solidity ^0.5.0; import "@openzeppelin/contracts/token/ERC721/ERC721Full.sol"; -import "@openzeppelin/contracts/drafts/Counters.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; contract GameItem is ERC721Full { using Counters for Counters.Counter; diff --git a/test/drafts/ERC1046/ERC20Metadata.test.js b/test/drafts/ERC1046/ERC20Metadata.test.js deleted file mode 100644 index 625bc6587..000000000 --- a/test/drafts/ERC1046/ERC20Metadata.test.js +++ /dev/null @@ -1,26 +0,0 @@ -const { contract } = require('@openzeppelin/test-environment'); -require('@openzeppelin/test-helpers'); - -const ERC20MetadataMock = contract.fromArtifact('ERC20MetadataMock'); - -const { expect } = require('chai'); - -const metadataURI = 'https://example.com'; - -describe('ERC20Metadata', function () { - beforeEach(async function () { - this.token = await ERC20MetadataMock.new(metadataURI); - }); - - it('responds with the metadata', async function () { - expect(await this.token.tokenURI()).to.equal(metadataURI); - }); - - describe('setTokenURI', function () { - it('changes the original URI', async function () { - const newMetadataURI = 'https://betterexample.com'; - await this.token.setTokenURI(newMetadataURI); - expect(await this.token.tokenURI()).to.equal(newMetadataURI); - }); - }); -}); diff --git a/test/drafts/TokenVesting.test.js b/test/drafts/TokenVesting.test.js deleted file mode 100644 index 3eb2d7c51..000000000 --- a/test/drafts/TokenVesting.test.js +++ /dev/null @@ -1,172 +0,0 @@ -const { accounts, contract } = require('@openzeppelin/test-environment'); - -const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); -const { ZERO_ADDRESS } = constants; - -const { expect } = require('chai'); - -const ERC20Mock = contract.fromArtifact('ERC20Mock'); -const TokenVesting = contract.fromArtifact('TokenVesting'); - -describe('TokenVesting', function () { - const [ owner, beneficiary ] = accounts; - - const amount = new BN('1000'); - - beforeEach(async function () { - // +1 minute so it starts after contract instantiation - this.start = (await time.latest()).add(time.duration.minutes(1)); - this.cliffDuration = time.duration.years(1); - this.duration = time.duration.years(2); - }); - - it('reverts with a duration shorter than the cliff', async function () { - const cliffDuration = this.duration; - const duration = this.cliffDuration; - - expect(cliffDuration).to.be.bignumber.that.is.at.least(duration); - - await expectRevert( - TokenVesting.new(beneficiary, this.start, cliffDuration, duration, true, { from: owner }), - 'TokenVesting: cliff is longer than duration' - ); - }); - - it('reverts with a null beneficiary', async function () { - await expectRevert( - TokenVesting.new(ZERO_ADDRESS, this.start, this.cliffDuration, this.duration, true, { from: owner }), - 'TokenVesting: beneficiary is the zero address' - ); - }); - - it('reverts with a null duration', async function () { - // cliffDuration should also be 0, since the duration must be larger than the cliff - await expectRevert( - TokenVesting.new(beneficiary, this.start, 0, 0, true, { from: owner }), 'TokenVesting: duration is 0' - ); - }); - - it('reverts if the end time is in the past', async function () { - const now = await time.latest(); - - this.start = now.sub(this.duration).sub(time.duration.minutes(1)); - await expectRevert( - TokenVesting.new(beneficiary, this.start, this.cliffDuration, this.duration, true, { from: owner }), - 'TokenVesting: final time is before current time' - ); - }); - - context('once deployed', function () { - beforeEach(async function () { - this.vesting = await TokenVesting.new( - beneficiary, this.start, this.cliffDuration, this.duration, true, { from: owner }); - - this.token = await ERC20Mock.new(this.vesting.address, amount); - }); - - it('can get state', async function () { - expect(await this.vesting.beneficiary()).to.equal(beneficiary); - expect(await this.vesting.cliff()).to.be.bignumber.equal(this.start.add(this.cliffDuration)); - expect(await this.vesting.start()).to.be.bignumber.equal(this.start); - expect(await this.vesting.duration()).to.be.bignumber.equal(this.duration); - expect(await this.vesting.revocable()).to.be.equal(true); - }); - - it('cannot be released before cliff', async function () { - await expectRevert(this.vesting.release(this.token.address), - 'TokenVesting: no tokens are due' - ); - }); - - it('can be released after cliff', async function () { - await time.increaseTo(this.start.add(this.cliffDuration).add(time.duration.weeks(1))); - const { logs } = await this.vesting.release(this.token.address); - expectEvent.inLogs(logs, 'TokensReleased', { - token: this.token.address, - amount: await this.token.balanceOf(beneficiary), - }); - }); - - it('should release proper amount after cliff', async function () { - await time.increaseTo(this.start.add(this.cliffDuration)); - - await this.vesting.release(this.token.address); - const releaseTime = await time.latest(); - - const releasedAmount = amount.mul(releaseTime.sub(this.start)).div(this.duration); - expect(await this.token.balanceOf(beneficiary)).to.be.bignumber.equal(releasedAmount); - expect(await this.vesting.released(this.token.address)).to.be.bignumber.equal(releasedAmount); - }); - - it('should linearly release tokens during vesting period', async function () { - const vestingPeriod = this.duration.sub(this.cliffDuration); - const checkpoints = 4; - - for (let i = 1; i <= checkpoints; i++) { - const now = this.start.add(this.cliffDuration).add((vestingPeriod.muln(i).divn(checkpoints))); - await time.increaseTo(now); - - await this.vesting.release(this.token.address); - const expectedVesting = amount.mul(now.sub(this.start)).div(this.duration); - expect(await this.token.balanceOf(beneficiary)).to.be.bignumber.equal(expectedVesting); - expect(await this.vesting.released(this.token.address)).to.be.bignumber.equal(expectedVesting); - } - }); - - it('should have released all after end', async function () { - await time.increaseTo(this.start.add(this.duration)); - await this.vesting.release(this.token.address); - expect(await this.token.balanceOf(beneficiary)).to.be.bignumber.equal(amount); - expect(await this.vesting.released(this.token.address)).to.be.bignumber.equal(amount); - }); - - it('should be revoked by owner if revocable is set', async function () { - const { logs } = await this.vesting.revoke(this.token.address, { from: owner }); - expectEvent.inLogs(logs, 'TokenVestingRevoked', { token: this.token.address }); - expect(await this.vesting.revoked(this.token.address)).to.equal(true); - }); - - it('should fail to be revoked by owner if revocable not set', async function () { - const vesting = await TokenVesting.new( - beneficiary, this.start, this.cliffDuration, this.duration, false, { from: owner } - ); - - await expectRevert(vesting.revoke(this.token.address, { from: owner }), - 'TokenVesting: cannot revoke' - ); - }); - - it('should return the non-vested tokens when revoked by owner', async function () { - await time.increaseTo(this.start.add(this.cliffDuration).add(time.duration.weeks(12))); - - const vested = vestedAmount(amount, await time.latest(), this.start, this.cliffDuration, this.duration); - - await this.vesting.revoke(this.token.address, { from: owner }); - - expect(await this.token.balanceOf(owner)).to.be.bignumber.equal(amount.sub(vested)); - }); - - it('should keep the vested tokens when revoked by owner', async function () { - await time.increaseTo(this.start.add(this.cliffDuration).add(time.duration.weeks(12))); - - const vestedPre = vestedAmount(amount, await time.latest(), this.start, this.cliffDuration, this.duration); - - await this.vesting.revoke(this.token.address, { from: owner }); - - const vestedPost = vestedAmount(amount, await time.latest(), this.start, this.cliffDuration, this.duration); - - expect(vestedPre).to.be.bignumber.equal(vestedPost); - }); - - it('should fail to be revoked a second time', async function () { - await this.vesting.revoke(this.token.address, { from: owner }); - await expectRevert(this.vesting.revoke(this.token.address, { from: owner }), - 'TokenVesting: token already revoked' - ); - }); - - function vestedAmount (total, now, start, cliffDuration, duration) { - return (now.lt(start.add(cliffDuration))) ? new BN(0) : total.mul((now.sub(start))).div(duration); - } - }); -}); diff --git a/test/drafts/SignedSafeMath.test.js b/test/math/SignedSafeMath.test.js similarity index 100% rename from test/drafts/SignedSafeMath.test.js rename to test/math/SignedSafeMath.test.js diff --git a/test/drafts/ERC20Snapshot.test.js b/test/token/ERC20/ERC20Snapshot.test.js similarity index 100% rename from test/drafts/ERC20Snapshot.test.js rename to test/token/ERC20/ERC20Snapshot.test.js diff --git a/test/drafts/Counters.test.js b/test/utils/Counters.test.js similarity index 100% rename from test/drafts/Counters.test.js rename to test/utils/Counters.test.js diff --git a/test/lifecycle/Pausable.test.js b/test/utils/Pausable.test.js similarity index 100% rename from test/lifecycle/Pausable.test.js rename to test/utils/Pausable.test.js diff --git a/test/drafts/Strings.test.js b/test/utils/Strings.test.js similarity index 100% rename from test/drafts/Strings.test.js rename to test/utils/Strings.test.js