diff --git a/contracts/token/VestedToken.sol b/contracts/token/VestedToken.sol index da350584b..9c0766945 100644 --- a/contracts/token/VestedToken.sol +++ b/contracts/token/VestedToken.sol @@ -94,7 +94,7 @@ contract VestedToken is StandardToken, LimitedTransferToken { if (time < cliff) { return 0; } - if (time > vesting) { + if (time >= vesting) { return tokens; } @@ -103,7 +103,7 @@ contract VestedToken is StandardToken, LimitedTransferToken { uint256 vestingTokens = safeSub(tokens, cliffTokens); - vestedTokens = safeAdd(vestedTokens, safeDiv(safeMul(vestingTokens, safeSub(time, cliff)), safeSub(vesting, start))); + vestedTokens = safeAdd(vestedTokens, safeDiv(safeMul(vestingTokens, safeSub(time, cliff)), safeSub(vesting, cliff))); } function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) { diff --git a/test/VestedToken.js b/test/VestedToken.js index 4fefa1ad8..50ec554f9 100644 --- a/test/VestedToken.js +++ b/test/VestedToken.js @@ -40,7 +40,7 @@ contract('VestedToken', function(accounts) { }) it('all tokens are transferable after vesting', async () => { - assert.equal(await token.transferableTokens(receiver, now + vesting + 1), tokenAmount); + assert.equal(await token.transferableTokens(receiver, now + vesting), tokenAmount); }) it('throws when trying to transfer non vested tokens', async () => { @@ -84,16 +84,34 @@ contract('VestedToken', function(accounts) { }) it('can transfer all tokens after vesting ends', async () => { - await timer(vesting + 1); + await timer(vesting); await token.transfer(accounts[7], tokenAmount, { from: receiver }) assert.equal(await token.balanceOf(accounts[7]), tokenAmount); }) it('can approve and transferFrom all tokens after vesting ends', async () => { - await timer(vesting + 1); + await timer(vesting); await token.approve(accounts[7], tokenAmount, { from: receiver }) await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] }) assert.equal(await token.balanceOf(accounts[7]), tokenAmount); }) + + it('can handle composed vesting schedules', async () => { + await timer(cliff); + await token.transfer(accounts[7], 12, { from: receiver }) + assert.equal(await token.balanceOf(accounts[7]), 12); + + let newNow = web3.eth.getBlock(web3.eth.blockNumber).timestamp + + await token.grantVestedTokens(receiver, tokenAmount, newNow, newNow + cliff, newNow + vesting, { from: granter }) + await token.transfer(accounts[7], 13, { from: receiver }) + assert.equal(await token.balanceOf(accounts[7]), tokenAmount / 2); + + assert.equal(await token.balanceOf(receiver), 3 * tokenAmount / 2) + assert.equal(await token.transferableTokens(receiver, newNow), 0) + await timer(vesting); + await token.transfer(accounts[7], 3 * tokenAmount / 2, { from: receiver }) + assert.equal(await token.balanceOf(accounts[7]), tokenAmount * 2) + }) }) });