diff --git a/contracts/access/SignatureBouncer.sol b/contracts/access/SignatureBouncer.sol index 7741bbdca..644b34374 100644 --- a/contracts/access/SignatureBouncer.sol +++ b/contracts/access/SignatureBouncer.sol @@ -82,7 +82,6 @@ contract SignatureBouncer is Ownable, RBAC { public onlyOwner { - require(_bouncer != address(0)); removeRole(_bouncer, ROLE_BOUNCER); } diff --git a/contracts/token/ERC721/ERC721BasicToken.sol b/contracts/token/ERC721/ERC721BasicToken.sol index f4db7399d..568be300c 100644 --- a/contracts/token/ERC721/ERC721BasicToken.sol +++ b/contracts/token/ERC721/ERC721BasicToken.sol @@ -131,7 +131,6 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic { public { require(isApprovedOrOwner(msg.sender, _tokenId)); - require(_from != address(0)); require(_to != address(0)); clearApproval(_from, _tokenId); diff --git a/test/Bounty.test.js b/test/Bounty.test.js index c9722269e..7438ab022 100644 --- a/test/Bounty.test.js +++ b/test/Bounty.test.js @@ -17,7 +17,7 @@ const sendReward = async (from, to, value) => ethSendTransaction({ const reward = new web3.BigNumber(web3.toWei(1, 'ether')); -contract('Bounty', function ([_, owner, researcher]) { +contract('Bounty', function ([_, owner, researcher, nonTarget]) { context('against secure contract', function () { beforeEach(async function () { this.bounty = await SecureTargetBounty.new({ from: owner }); @@ -82,6 +82,12 @@ contract('Bounty', function ([_, owner, researcher]) { researcherCurrBalance.sub(researcherPrevBalance).should.be.bignumber.equal(reward.sub(gasCost)); }); + it('cannot claim reward from non-target', async function () { + await assertRevert( + this.bounty.claim(nonTarget, { from: researcher }) + ); + }); + context('reward claimed', function () { beforeEach(async function () { await this.bounty.claim(this.targetAddress, { from: researcher }); diff --git a/test/crowdsale/Crowdsale.test.js b/test/crowdsale/Crowdsale.test.js index 7d70ba4f5..5214a7b00 100644 --- a/test/crowdsale/Crowdsale.test.js +++ b/test/crowdsale/Crowdsale.test.js @@ -1,3 +1,4 @@ +const { assertRevert } = require('../helpers/assertRevert'); const { ether } = require('../helpers/ether'); const { ethGetBalance } = require('../helpers/web3'); @@ -15,67 +16,118 @@ contract('Crowdsale', function ([_, investor, wallet, purchaser]) { const value = ether(42); const tokenSupply = new BigNumber('1e22'); const expectedTokenAmount = rate.mul(value); + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; - beforeEach(async function () { - this.token = await SimpleToken.new(); - this.crowdsale = await Crowdsale.new(rate, wallet, this.token.address); - await this.token.transfer(this.crowdsale.address, tokenSupply); + it('requires a non-null token', async function () { + await assertRevert( + Crowdsale.new(rate, wallet, ZERO_ADDRESS) + ); }); - describe('accepting payments', function () { - it('should accept payments', async function () { - await this.crowdsale.send(value); - await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }); - }); - }); - - describe('high-level purchase', function () { - it('should log purchase', async function () { - const { logs } = await this.crowdsale.sendTransaction({ value: value, from: investor }); - const event = logs.find(e => e.event === 'TokenPurchase'); - should.exist(event); - event.args.purchaser.should.eq(investor); - event.args.beneficiary.should.eq(investor); - event.args.value.should.be.bignumber.equal(value); - event.args.amount.should.be.bignumber.equal(expectedTokenAmount); + context('with token', async function () { + beforeEach(async function () { + this.token = await SimpleToken.new(); }); - it('should assign tokens to sender', async function () { - await this.crowdsale.sendTransaction({ value: value, from: investor }); - const balance = await this.token.balanceOf(investor); - balance.should.be.bignumber.equal(expectedTokenAmount); + it('requires a non-zero rate', async function () { + await assertRevert( + Crowdsale.new(0, wallet, this.token.address) + ); }); - it('should forward funds to wallet', async function () { - const pre = await ethGetBalance(wallet); - await this.crowdsale.sendTransaction({ value, from: investor }); - const post = await ethGetBalance(wallet); - post.minus(pre).should.be.bignumber.equal(value); - }); - }); - - describe('low-level purchase', function () { - it('should log purchase', async function () { - const { logs } = await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }); - const event = logs.find(e => e.event === 'TokenPurchase'); - should.exist(event); - event.args.purchaser.should.eq(purchaser); - event.args.beneficiary.should.eq(investor); - event.args.value.should.be.bignumber.equal(value); - event.args.amount.should.be.bignumber.equal(expectedTokenAmount); + it('requires a non-null wallet', async function () { + await assertRevert( + Crowdsale.new(rate, ZERO_ADDRESS, this.token.address) + ); }); - it('should assign tokens to beneficiary', async function () { - await this.crowdsale.buyTokens(investor, { value, from: purchaser }); - const balance = await this.token.balanceOf(investor); - balance.should.be.bignumber.equal(expectedTokenAmount); - }); + context('once deployed', async function () { + beforeEach(async function () { + this.crowdsale = await Crowdsale.new(rate, wallet, this.token.address); + await this.token.transfer(this.crowdsale.address, tokenSupply); + }); - it('should forward funds to wallet', async function () { - const pre = await ethGetBalance(wallet); - await this.crowdsale.buyTokens(investor, { value, from: purchaser }); - const post = await ethGetBalance(wallet); - post.minus(pre).should.be.bignumber.equal(value); + describe('accepting payments', function () { + describe('bare payments', function () { + it('should accept payments', async function () { + await this.crowdsale.send(value, { from: purchaser }); + }); + + it('reverts on zero-valued payments', async function () { + await assertRevert( + this.crowdsale.send(0, { from: purchaser }) + ); + }); + }); + + describe('buyTokens', function () { + it('should accept payments', async function () { + await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }); + }); + + it('reverts on zero-valued payments', async function () { + await assertRevert( + this.crowdsale.buyTokens(investor, { value: 0, from: purchaser }) + ); + }); + + it('requires a non-null beneficiary', async function () { + await assertRevert( + this.crowdsale.buyTokens(ZERO_ADDRESS, { value: value, from: purchaser }) + ); + }); + }); + }); + + describe('high-level purchase', function () { + it('should log purchase', async function () { + const { logs } = await this.crowdsale.sendTransaction({ value: value, from: investor }); + const event = logs.find(e => e.event === 'TokenPurchase'); + should.exist(event); + event.args.purchaser.should.eq(investor); + event.args.beneficiary.should.eq(investor); + event.args.value.should.be.bignumber.equal(value); + event.args.amount.should.be.bignumber.equal(expectedTokenAmount); + }); + + it('should assign tokens to sender', async function () { + await this.crowdsale.sendTransaction({ value: value, from: investor }); + const balance = await this.token.balanceOf(investor); + balance.should.be.bignumber.equal(expectedTokenAmount); + }); + + it('should forward funds to wallet', async function () { + const pre = await ethGetBalance(wallet); + await this.crowdsale.sendTransaction({ value, from: investor }); + const post = await ethGetBalance(wallet); + post.minus(pre).should.be.bignumber.equal(value); + }); + }); + + describe('low-level purchase', function () { + it('should log purchase', async function () { + const { logs } = await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }); + const event = logs.find(e => e.event === 'TokenPurchase'); + should.exist(event); + event.args.purchaser.should.eq(purchaser); + event.args.beneficiary.should.eq(investor); + event.args.value.should.be.bignumber.equal(value); + event.args.amount.should.be.bignumber.equal(expectedTokenAmount); + }); + + it('should assign tokens to beneficiary', async function () { + await this.crowdsale.buyTokens(investor, { value, from: purchaser }); + const balance = await this.token.balanceOf(investor); + balance.should.be.bignumber.equal(expectedTokenAmount); + }); + + it('should forward funds to wallet', async function () { + const pre = await ethGetBalance(wallet); + await this.crowdsale.buyTokens(investor, { value, from: purchaser }); + const post = await ethGetBalance(wallet); + post.minus(pre).should.be.bignumber.equal(value); + }); + }); }); }); }); diff --git a/test/ownership/Superuser.test.js b/test/ownership/Superuser.test.js index e27b89cc8..7872e16da 100644 --- a/test/ownership/Superuser.test.js +++ b/test/ownership/Superuser.test.js @@ -7,6 +7,8 @@ require('chai') .should(); contract('Superuser', function ([_, firstOwner, newSuperuser, newOwner, anyone]) { + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; + beforeEach(async function () { this.superuser = await Superuser.new({ from: firstOwner }); }); @@ -27,6 +29,12 @@ contract('Superuser', function ([_, firstOwner, newSuperuser, newOwner, anyone]) newSuperuserIsSuperuser.should.be.equal(true); }); + it('should prevent changing to a null superuser', async function () { + await expectThrow( + this.superuser.transferSuperuser(ZERO_ADDRESS, { from: firstOwner }) + ); + }); + it('should change owner after the superuser transfers the ownership', async function () { await this.superuser.transferSuperuser(newSuperuser, { from: firstOwner }); diff --git a/test/payment/RefundEscrow.test.js b/test/payment/RefundEscrow.test.js index 8eb43593c..172742658 100644 --- a/test/payment/RefundEscrow.test.js +++ b/test/payment/RefundEscrow.test.js @@ -14,93 +14,118 @@ const RefundEscrow = artifacts.require('RefundEscrow'); contract('RefundEscrow', function ([_, owner, beneficiary, refundee1, refundee2]) { const amount = web3.toWei(54.0, 'ether'); const refundees = [refundee1, refundee2]; + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; - beforeEach(async function () { - this.escrow = await RefundEscrow.new(beneficiary, { from: owner }); + it('requires a non-null beneficiary', async function () { + await expectThrow( + RefundEscrow.new(ZERO_ADDRESS, { from: owner }) + ); }); - context('active state', function () { - it('accepts deposits', async function () { - await this.escrow.deposit(refundee1, { from: owner, value: amount }); - - const deposit = await this.escrow.depositsOf(refundee1); - deposit.should.be.bignumber.equal(amount); - }); - - it('does not refund refundees', async function () { - await this.escrow.deposit(refundee1, { from: owner, value: amount }); - await expectThrow(this.escrow.withdraw(refundee1), EVMRevert); - }); - - it('does not allow beneficiary withdrawal', async function () { - await this.escrow.deposit(refundee1, { from: owner, value: amount }); - await expectThrow(this.escrow.beneficiaryWithdraw(), EVMRevert); - }); - }); - - it('only owner can enter closed state', async function () { - await expectThrow(this.escrow.close({ from: beneficiary }), EVMRevert); - - const receipt = await this.escrow.close({ from: owner }); - - expectEvent.inLogs(receipt.logs, 'Closed'); - }); - - context('closed state', function () { + context('once deployed', function () { beforeEach(async function () { - await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); - - await this.escrow.close({ from: owner }); + this.escrow = await RefundEscrow.new(beneficiary, { from: owner }); }); - it('rejects deposits', async function () { - await expectThrow(this.escrow.deposit(refundee1, { from: owner, value: amount }), EVMRevert); + context('active state', function () { + it('accepts deposits', async function () { + await this.escrow.deposit(refundee1, { from: owner, value: amount }); + + const deposit = await this.escrow.depositsOf(refundee1); + deposit.should.be.bignumber.equal(amount); + }); + + it('does not refund refundees', async function () { + await this.escrow.deposit(refundee1, { from: owner, value: amount }); + await expectThrow(this.escrow.withdraw(refundee1), EVMRevert); + }); + + it('does not allow beneficiary withdrawal', async function () { + await this.escrow.deposit(refundee1, { from: owner, value: amount }); + await expectThrow(this.escrow.beneficiaryWithdraw(), EVMRevert); + }); }); - it('does not refund refundees', async function () { - await expectThrow(this.escrow.withdraw(refundee1), EVMRevert); + it('only owner can enter closed state', async function () { + await expectThrow(this.escrow.close({ from: beneficiary }), EVMRevert); + + const receipt = await this.escrow.close({ from: owner }); + + expectEvent.inLogs(receipt.logs, 'Closed'); }); - it('allows beneficiary withdrawal', async function () { - const beneficiaryInitialBalance = await ethGetBalance(beneficiary); - await this.escrow.beneficiaryWithdraw(); - const beneficiaryFinalBalance = await ethGetBalance(beneficiary); + context('closed state', function () { + beforeEach(async function () { + await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); - beneficiaryFinalBalance.sub(beneficiaryInitialBalance).should.be.bignumber.equal(amount * refundees.length); - }); - }); + await this.escrow.close({ from: owner }); + }); - it('only owner can enter refund state', async function () { - await expectThrow(this.escrow.enableRefunds({ from: beneficiary }), EVMRevert); + it('rejects deposits', async function () { + await expectThrow(this.escrow.deposit(refundee1, { from: owner, value: amount }), EVMRevert); + }); - const receipt = await this.escrow.enableRefunds({ from: owner }); + it('does not refund refundees', async function () { + await expectThrow(this.escrow.withdraw(refundee1), EVMRevert); + }); - expectEvent.inLogs(receipt.logs, 'RefundsEnabled'); - }); + it('allows beneficiary withdrawal', async function () { + const beneficiaryInitialBalance = await ethGetBalance(beneficiary); + await this.escrow.beneficiaryWithdraw(); + const beneficiaryFinalBalance = await ethGetBalance(beneficiary); - context('refund state', function () { - beforeEach(async function () { - await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); + beneficiaryFinalBalance.sub(beneficiaryInitialBalance).should.be.bignumber.equal(amount * refundees.length); + }); - await this.escrow.enableRefunds({ from: owner }); + it('prevents entering the refund state', async function () { + await expectThrow(this.escrow.enableRefunds({ from: owner }), EVMRevert); + }); + + it('prevents re-entering the closed state', async function () { + await expectThrow(this.escrow.close({ from: owner }), EVMRevert); + }); }); - it('rejects deposits', async function () { - await expectThrow(this.escrow.deposit(refundee1, { from: owner, value: amount }), EVMRevert); + it('only owner can enter refund state', async function () { + await expectThrow(this.escrow.enableRefunds({ from: beneficiary }), EVMRevert); + + const receipt = await this.escrow.enableRefunds({ from: owner }); + + expectEvent.inLogs(receipt.logs, 'RefundsEnabled'); }); - it('refunds refundees', async function () { - for (const refundee of [refundee1, refundee2]) { - const refundeeInitialBalance = await ethGetBalance(refundee); - await this.escrow.withdraw(refundee, { from: owner }); - const refundeeFinalBalance = await ethGetBalance(refundee); + context('refund state', function () { + beforeEach(async function () { + await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); - refundeeFinalBalance.sub(refundeeInitialBalance).should.be.bignumber.equal(amount); - } - }); + await this.escrow.enableRefunds({ from: owner }); + }); - it('does not allow beneficiary withdrawal', async function () { - await expectThrow(this.escrow.beneficiaryWithdraw(), EVMRevert); + it('rejects deposits', async function () { + await expectThrow(this.escrow.deposit(refundee1, { from: owner, value: amount }), EVMRevert); + }); + + it('refunds refundees', async function () { + for (const refundee of [refundee1, refundee2]) { + const refundeeInitialBalance = await ethGetBalance(refundee); + await this.escrow.withdraw(refundee, { from: owner }); + const refundeeFinalBalance = await ethGetBalance(refundee); + + refundeeFinalBalance.sub(refundeeInitialBalance).should.be.bignumber.equal(amount); + } + }); + + it('does not allow beneficiary withdrawal', async function () { + await expectThrow(this.escrow.beneficiaryWithdraw(), EVMRevert); + }); + + it('prevents entering the closed state', async function () { + await expectThrow(this.escrow.close({ from: owner }), EVMRevert); + }); + + it('prevents re-entering the refund state', async function () { + await expectThrow(this.escrow.enableRefunds({ from: owner }), EVMRevert); + }); }); }); }); diff --git a/test/payment/SplitPayment.test.js b/test/payment/SplitPayment.test.js index acaa90317..095849857 100644 --- a/test/payment/SplitPayment.test.js +++ b/test/payment/SplitPayment.test.js @@ -12,6 +12,7 @@ const SplitPayment = artifacts.require('SplitPayment'); contract('SplitPayment', function ([_, owner, payee1, payee2, payee3, nonpayee1, payer1]) { const amount = web3.toWei(1.0, 'ether'); + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; it('cannot be created with no payees', async function () { await expectThrow(SplitPayment.new([], []), EVMRevert); @@ -25,6 +26,18 @@ contract('SplitPayment', function ([_, owner, payee1, payee2, payee3, nonpayee1, await expectThrow(SplitPayment.new([payee1, payee2], [20, 30, 40]), EVMRevert); }); + it('requires non-null payees', async function () { + await expectThrow(SplitPayment.new([payee1, ZERO_ADDRESS], [20, 30]), EVMRevert); + }); + + it('requires non-zero shares', async function () { + await expectThrow(SplitPayment.new([payee1, payee2], [20, 0]), EVMRevert); + }); + + it('rejects repeated payees', async function () { + await expectThrow(SplitPayment.new([payee1, payee1], [20, 0]), EVMRevert); + }); + context('once deployed', function () { beforeEach(async function () { this.payees = [payee1, payee2, payee3]; diff --git a/test/token/ERC20/CappedToken.test.js b/test/token/ERC20/CappedToken.test.js index 3545c1288..1098de294 100644 --- a/test/token/ERC20/CappedToken.test.js +++ b/test/token/ERC20/CappedToken.test.js @@ -1,3 +1,4 @@ +const { assertRevert } = require('../../helpers/assertRevert'); const { ether } = require('../../helpers/ether'); const { shouldBehaveLikeMintableToken } = require('./MintableToken.behavior'); const { shouldBehaveLikeCappedToken } = require('./CappedToken.behavior'); @@ -7,10 +8,18 @@ const CappedToken = artifacts.require('CappedToken'); contract('Capped', function ([_, owner, ...otherAccounts]) { const cap = ether(1000); - beforeEach(async function () { - this.token = await CappedToken.new(cap, { from: owner }); + it('requires a non-zero cap', async function () { + await assertRevert( + CappedToken.new(0, { from: owner }) + ); }); - shouldBehaveLikeCappedToken(owner, otherAccounts, cap); - shouldBehaveLikeMintableToken(owner, owner, otherAccounts); + context('once deployed', async function () { + beforeEach(async function () { + this.token = await CappedToken.new(cap, { from: owner }); + }); + + shouldBehaveLikeCappedToken(owner, otherAccounts, cap); + shouldBehaveLikeMintableToken(owner, owner, otherAccounts); + }); }); diff --git a/test/token/ERC20/TokenTimelock.test.js b/test/token/ERC20/TokenTimelock.test.js index 525819f19..a391e4849 100644 --- a/test/token/ERC20/TokenTimelock.test.js +++ b/test/token/ERC20/TokenTimelock.test.js @@ -14,41 +14,55 @@ const TokenTimelock = artifacts.require('TokenTimelock'); contract('TokenTimelock', function ([_, owner, beneficiary]) { const amount = new BigNumber(100); - beforeEach(async function () { - this.token = await MintableToken.new({ from: owner }); - this.releaseTime = (await latestTime()) + duration.years(1); - this.timelock = await TokenTimelock.new(this.token.address, beneficiary, this.releaseTime); - await this.token.mint(this.timelock.address, amount, { from: owner }); - }); + context('with token', function () { + beforeEach(async function () { + this.token = await MintableToken.new({ from: owner }); + }); - it('cannot be released before time limit', async function () { - await expectThrow(this.timelock.release()); - }); + it('rejects a release time in the past', async function () { + const pastReleaseTime = (await latestTime()) - duration.years(1); + await expectThrow( + TokenTimelock.new(this.token.address, beneficiary, pastReleaseTime) + ); + }); - it('cannot be released just before time limit', async function () { - await increaseTimeTo(this.releaseTime - duration.seconds(3)); - await expectThrow(this.timelock.release()); - }); + context('once deployed', function () { + beforeEach(async function () { + this.releaseTime = (await latestTime()) + duration.years(1); + this.timelock = await TokenTimelock.new(this.token.address, beneficiary, this.releaseTime); + await this.token.mint(this.timelock.address, amount, { from: owner }); + }); - it('can be released just after limit', async function () { - await increaseTimeTo(this.releaseTime + duration.seconds(1)); - await this.timelock.release(); - const balance = await this.token.balanceOf(beneficiary); - balance.should.be.bignumber.equal(amount); - }); + it('cannot be released before time limit', async function () { + await expectThrow(this.timelock.release()); + }); - it('can be released after time limit', async function () { - await increaseTimeTo(this.releaseTime + duration.years(1)); - await this.timelock.release(); - const balance = await this.token.balanceOf(beneficiary); - balance.should.be.bignumber.equal(amount); - }); + it('cannot be released just before time limit', async function () { + await increaseTimeTo(this.releaseTime - duration.seconds(3)); + await expectThrow(this.timelock.release()); + }); - it('cannot be released twice', async function () { - await increaseTimeTo(this.releaseTime + duration.years(1)); - await this.timelock.release(); - await expectThrow(this.timelock.release()); - const balance = await this.token.balanceOf(beneficiary); - balance.should.be.bignumber.equal(amount); + it('can be released just after limit', async function () { + await increaseTimeTo(this.releaseTime + duration.seconds(1)); + await this.timelock.release(); + const balance = await this.token.balanceOf(beneficiary); + balance.should.be.bignumber.equal(amount); + }); + + it('can be released after time limit', async function () { + await increaseTimeTo(this.releaseTime + duration.years(1)); + await this.timelock.release(); + const balance = await this.token.balanceOf(beneficiary); + balance.should.be.bignumber.equal(amount); + }); + + it('cannot be released twice', async function () { + await increaseTimeTo(this.releaseTime + duration.years(1)); + await this.timelock.release(); + await expectThrow(this.timelock.release()); + const balance = await this.token.balanceOf(beneficiary); + balance.should.be.bignumber.equal(amount); + }); + }); }); }); diff --git a/test/token/ERC20/TokenVesting.test.js b/test/token/ERC20/TokenVesting.test.js index aa2132656..a5ceb623d 100644 --- a/test/token/ERC20/TokenVesting.test.js +++ b/test/token/ERC20/TokenVesting.test.js @@ -15,110 +15,134 @@ const TokenVesting = artifacts.require('TokenVesting'); contract('TokenVesting', function ([_, owner, beneficiary]) { const amount = new BigNumber(1000); + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; beforeEach(async function () { - this.token = await MintableToken.new({ from: owner }); - this.start = (await latestTime()) + duration.minutes(1); // +1 minute so it starts after contract instantiation this.cliff = duration.years(1); this.duration = duration.years(2); - - this.vesting = await TokenVesting.new(beneficiary, this.start, this.cliff, this.duration, true, { from: owner }); - - await this.token.mint(this.vesting.address, amount, { from: owner }); }); - it('cannot be released before cliff', async function () { + it('rejects a duration shorter than the cliff', async function () { + const cliff = this.duration; + const duration = this.cliff; + + cliff.should.be.gt(duration); + await expectThrow( - this.vesting.release(this.token.address), - EVMRevert, + TokenVesting.new(beneficiary, this.start, cliff, duration, true, { from: owner }) ); }); - it('can be released after cliff', async function () { - await increaseTimeTo(this.start + this.cliff + duration.weeks(1)); - await this.vesting.release(this.token.address); + it('requires a valid beneficiary', async function () { + await expectThrow( + TokenVesting.new(ZERO_ADDRESS, this.start, this.cliff, this.duration, true, { from: owner }) + ); }); - it('should release proper amount after cliff', async function () { - await increaseTimeTo(this.start + this.cliff); + context('once deployed', function () { + beforeEach(async function () { + this.vesting = await TokenVesting.new(beneficiary, this.start, this.cliff, this.duration, true, { from: owner }); - const { receipt } = await this.vesting.release(this.token.address); - const block = await ethGetBlock(receipt.blockNumber); - const releaseTime = block.timestamp; + this.token = await MintableToken.new({ from: owner }); + await this.token.mint(this.vesting.address, amount, { from: owner }); + }); - const balance = await this.token.balanceOf(beneficiary); - balance.should.bignumber.eq(amount.mul(releaseTime - this.start).div(this.duration).floor()); - }); + it('cannot be released before cliff', async function () { + await expectThrow( + this.vesting.release(this.token.address), + EVMRevert, + ); + }); - it('should linearly release tokens during vesting period', async function () { - const vestingPeriod = this.duration - this.cliff; - const checkpoints = 4; + it('can be released after cliff', async function () { + await increaseTimeTo(this.start + this.cliff + duration.weeks(1)); + await this.vesting.release(this.token.address); + }); - for (let i = 1; i <= checkpoints; i++) { - const now = this.start + this.cliff + i * (vestingPeriod / checkpoints); - await increaseTimeTo(now); + it('should release proper amount after cliff', async function () { + await increaseTimeTo(this.start + this.cliff); + const { receipt } = await this.vesting.release(this.token.address); + const block = await ethGetBlock(receipt.blockNumber); + const releaseTime = block.timestamp; + + const balance = await this.token.balanceOf(beneficiary); + balance.should.bignumber.eq(amount.mul(releaseTime - this.start).div(this.duration).floor()); + }); + + it('should linearly release tokens during vesting period', async function () { + const vestingPeriod = this.duration - this.cliff; + const checkpoints = 4; + + for (let i = 1; i <= checkpoints; i++) { + const now = this.start + this.cliff + i * (vestingPeriod / checkpoints); + await increaseTimeTo(now); + + await this.vesting.release(this.token.address); + const balance = await this.token.balanceOf(beneficiary); + const expectedVesting = amount.mul(now - this.start).div(this.duration).floor(); + + balance.should.bignumber.eq(expectedVesting); + } + }); + + it('should have released all after end', async function () { + await increaseTimeTo(this.start + this.duration); await this.vesting.release(this.token.address); const balance = await this.token.balanceOf(beneficiary); - const expectedVesting = amount.mul(now - this.start).div(this.duration).floor(); + balance.should.bignumber.eq(amount); + }); - balance.should.bignumber.eq(expectedVesting); - } - }); + it('should be revoked by owner if revocable is set', async function () { + await this.vesting.revoke(this.token.address, { from: owner }); + }); - it('should have released all after end', async function () { - await increaseTimeTo(this.start + this.duration); - await this.vesting.release(this.token.address); - const balance = await this.token.balanceOf(beneficiary); - balance.should.bignumber.eq(amount); - }); + it('should fail to be revoked by owner if revocable not set', async function () { + const vesting = await TokenVesting.new( + beneficiary, this.start, this.cliff, this.duration, false, { from: owner } + ); - it('should be revoked by owner if revocable is set', async function () { - await this.vesting.revoke(this.token.address, { from: owner }); - }); + await expectThrow( + vesting.revoke(this.token.address, { from: owner }), + EVMRevert, + ); + }); - it('should fail to be revoked by owner if revocable not set', async function () { - const vesting = await TokenVesting.new(beneficiary, this.start, this.cliff, this.duration, false, { from: owner }); - await expectThrow( - vesting.revoke(this.token.address, { from: owner }), - EVMRevert, - ); - }); + it('should return the non-vested tokens when revoked by owner', async function () { + await increaseTimeTo(this.start + this.cliff + duration.weeks(12)); - it('should return the non-vested tokens when revoked by owner', async function () { - await increaseTimeTo(this.start + this.cliff + duration.weeks(12)); + const vested = await this.vesting.vestedAmount(this.token.address); - const vested = await this.vesting.vestedAmount(this.token.address); + await this.vesting.revoke(this.token.address, { from: owner }); - await this.vesting.revoke(this.token.address, { from: owner }); + const ownerBalance = await this.token.balanceOf(owner); + ownerBalance.should.bignumber.eq(amount.sub(vested)); + }); - const ownerBalance = await this.token.balanceOf(owner); - ownerBalance.should.bignumber.eq(amount.sub(vested)); - }); + it('should keep the vested tokens when revoked by owner', async function () { + await increaseTimeTo(this.start + this.cliff + duration.weeks(12)); - it('should keep the vested tokens when revoked by owner', async function () { - await increaseTimeTo(this.start + this.cliff + duration.weeks(12)); + const vestedPre = await this.vesting.vestedAmount(this.token.address); - const vestedPre = await this.vesting.vestedAmount(this.token.address); + await this.vesting.revoke(this.token.address, { from: owner }); - await this.vesting.revoke(this.token.address, { from: owner }); + const vestedPost = await this.vesting.vestedAmount(this.token.address); - const vestedPost = await this.vesting.vestedAmount(this.token.address); + vestedPre.should.bignumber.eq(vestedPost); + }); - vestedPre.should.bignumber.eq(vestedPost); - }); + it('should fail to be revoked a second time', async function () { + await increaseTimeTo(this.start + this.cliff + duration.weeks(12)); - it('should fail to be revoked a second time', async function () { - await increaseTimeTo(this.start + this.cliff + duration.weeks(12)); + await this.vesting.vestedAmount(this.token.address); - await this.vesting.vestedAmount(this.token.address); + await this.vesting.revoke(this.token.address, { from: owner }); - await this.vesting.revoke(this.token.address, { from: owner }); - - await expectThrow( - this.vesting.revoke(this.token.address, { from: owner }), - EVMRevert, - ); + await expectThrow( + this.vesting.revoke(this.token.address, { from: owner }), + EVMRevert, + ); + }); }); }); diff --git a/test/token/ERC721/ERC721Token.test.js b/test/token/ERC721/ERC721Token.test.js index 89061b3ce..19b282243 100644 --- a/test/token/ERC721/ERC721Token.test.js +++ b/test/token/ERC721/ERC721Token.test.js @@ -16,7 +16,9 @@ contract('ERC721Token', function (accounts) { const symbol = 'NFT'; const firstTokenId = 100; const secondTokenId = 200; + const nonExistentTokenId = 999; const creator = accounts[0]; + const anyone = accounts[9]; beforeEach(async function () { this.token = await ERC721Token.new(name, symbol, { from: creator }); @@ -77,27 +79,35 @@ contract('ERC721Token', function (accounts) { }); describe('removeTokenFrom', function () { - beforeEach(async function () { - await this.token._removeTokenFrom(creator, firstTokenId, { from: creator }); + it('reverts if the correct owner is not passed', async function () { + await assertRevert( + this.token._removeTokenFrom(anyone, firstTokenId, { from: creator }) + ); }); - it('has been removed', async function () { - await assertRevert(this.token.tokenOfOwnerByIndex(creator, 1)); - }); + context('once removed', function () { + beforeEach(async function () { + await this.token._removeTokenFrom(creator, firstTokenId, { from: creator }); + }); - it('adjusts token list', async function () { - const token = await this.token.tokenOfOwnerByIndex(creator, 0); - token.toNumber().should.be.equal(secondTokenId); - }); + it('has been removed', async function () { + await assertRevert(this.token.tokenOfOwnerByIndex(creator, 1)); + }); - it('adjusts owner count', async function () { - const count = await this.token.balanceOf(creator); - count.toNumber().should.be.equal(1); - }); + it('adjusts token list', async function () { + const token = await this.token.tokenOfOwnerByIndex(creator, 0); + token.toNumber().should.be.equal(secondTokenId); + }); - it('does not adjust supply', async function () { - const total = await this.token.totalSupply(); - total.toNumber().should.be.equal(2); + it('adjusts owner count', async function () { + const count = await this.token.balanceOf(creator); + count.toNumber().should.be.equal(1); + }); + + it('does not adjust supply', async function () { + const total = await this.token.totalSupply(); + total.toNumber().should.be.equal(2); + }); }); }); @@ -120,6 +130,10 @@ contract('ERC721Token', function (accounts) { uri.should.be.equal(sampleUri); }); + it('reverts when setting metadata for non existent token id', async function () { + await assertRevert(this.token.setTokenURI(nonExistentTokenId, sampleUri)); + }); + it('can burn token with metadata', async function () { await this.token.setTokenURI(firstTokenId, sampleUri); await this.token.burn(firstTokenId); @@ -132,8 +146,8 @@ contract('ERC721Token', function (accounts) { uri.should.be.equal(''); }); - it('reverts when querying metadata for non existant token id', async function () { - await assertRevert(this.token.tokenURI(500)); + it('reverts when querying metadata for non existent token id', async function () { + await assertRevert(this.token.tokenURI(nonExistentTokenId)); }); });