Browse Source

[TokenVesting] Add a start parameter to constructor to defer vesting

Martín Triay 8 years ago
parent
commit
822de45bfc
2 changed files with 28 additions and 30 deletions
  1. 13 14
      contracts/token/TokenVesting.sol
  2. 15 16
      test/TokenVesting.js

+ 13 - 14
contracts/token/TokenVesting.sol

@@ -22,7 +22,7 @@ contract TokenVesting is Ownable {
 
   uint256 cliff;
   uint256 start;
-  uint256 end;
+  uint256 duration;
 
   bool revocable;
 
@@ -30,24 +30,23 @@ contract TokenVesting is Ownable {
 
   /**
    * @dev Creates a vesting contract that vests its balance of any ERC20 token to the
-   * _beneficiary, gradually in a linear fashion until _end. By then all of the balance
-   * will have vested.
+   * _beneficiary, gradually in a linear fashion until _start + _duration. By then all
+   * of the balance will have vested.
    * @param _beneficiary address of the beneficiary to whom vested tokens are transferred
-   * @param _cliff timestamp of the moment when tokens will begin to vest
-   * @param _end timestamp of the moment when all balance will have been vested
+   * @param _cliff duration in seconds of the cliff in which tokens will begin to vest
+   * @param _duration duration in seconds of the period in which the tokens will vest
    * @param _revocable whether the vesting is revocable or not
    */
-  function TokenVesting(address _beneficiary, uint256 _cliff, uint256 _end, bool _revocable) {
+  function TokenVesting(address _beneficiary, uint256 _start, uint256 _cliff, uint256 _duration, bool _revocable) {
     require(_beneficiary != 0x0);
-    require(_cliff > now);
-    require(_end > _cliff);
+    require(_cliff < _duration);
+    require(_start >= now);
 
     beneficiary = _beneficiary;
-    cliff = _cliff;
-    end = _end;
     revocable = _revocable;
-
-    start = now;
+    duration = _duration;
+    cliff = _start + _cliff;
+    start = _start;
   }
 
   /**
@@ -89,13 +88,13 @@ contract TokenVesting is Ownable {
   function vestedAmount(ERC20Basic token) constant returns (uint256) {
     if (now < cliff) {
       return 0;
-    } else if (now >= end) {
+    } else if (now >= start + duration) {
       return token.balanceOf(this);
     } else {
       uint256 currentBalance = token.balanceOf(this);
       uint256 totalBalance = currentBalance.add(released[token]);
 
-      uint256 vested = totalBalance.mul(now - start).div(end - start);
+      uint256 vested = totalBalance.mul(now - start).div(duration);
       uint256 unreleased = vested.sub(released[token]);
 
       // currentBalance can be 0 in case of a revoke

+ 15 - 16
test/TokenVesting.js

@@ -19,12 +19,11 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
   beforeEach(async function () {
     this.token = await MintableToken.new({ from: owner });
 
-    this.cliff = latestTime() + duration.years(1);
-    this.end = latestTime() + duration.years(2);
+    this.start = 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.cliff, this.end, true, { from: owner });
-
-    this.start = latestTime(); // gets the timestamp at construction
+    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 });
   });
@@ -34,55 +33,55 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
   });
 
   it('can be released after cliff', async function () {
-    await increaseTimeTo(this.cliff + duration.weeks(1));
+    await increaseTimeTo(this.start + this.cliff + duration.weeks(1));
     await this.vesting.release(this.token.address).should.be.fulfilled;
   });
 
   it('should release proper amount after cliff', async function () {
-    await increaseTimeTo(this.cliff);
+    await increaseTimeTo(this.start + this.cliff);
 
     const { receipt } = await this.vesting.release(this.token.address);
     const releaseTime = web3.eth.getBlock(receipt.blockNumber).timestamp;
 
     const balance = await this.token.balanceOf(beneficiary);
-    balance.should.bignumber.equal(amount.mul(releaseTime - this.start).div(this.end - this.start).floor());
+    balance.should.bignumber.equal(amount.mul(releaseTime - this.start).div(this.duration).floor());
   });
 
   it('should linearly release tokens during vesting period', async function () {
-    const duration = this.end - this.cliff;
+    const vestingPeriod = this.duration - this.cliff;
     const checkpoints = 4;
 
     for (let i = 1; i <= checkpoints; i++) {
-      const now = this.cliff + i * (duration / checkpoints);
+      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.end - this.start).floor();
+      const expectedVesting = amount.mul(now - this.start).div(this.duration).floor();
 
       balance.should.bignumber.equal(expectedVesting);
     }
   });
 
   it('should have released all after end', async function () {
-    await increaseTimeTo(this.end);
+    await increaseTimeTo(this.start + this.duration);
     await this.vesting.release(this.token.address);
     const balance = await this.token.balanceOf(beneficiary);
     balance.should.bignumber.equal(amount);
   });
 
   it('should be revoked by owner if revocable is set', async function () {
-    const vesting = await TokenVesting.new(beneficiary, this.cliff, this.end, true, { from: owner } );
+    const vesting = await TokenVesting.new(beneficiary, this.start, this.cliff, this.duration, true, { from: owner } );
     await vesting.revoke(this.token.address, { from: owner }).should.be.fulfilled;
   });
 
   it('should fail to be revoked by owner if revocable not set', async function () {
-    const vesting = await TokenVesting.new(beneficiary, this.cliff, this.end, false, { from: owner } );
+    const vesting = await TokenVesting.new(beneficiary, this.start, this.cliff, this.duration, false, { from: owner } );
     await vesting.revoke(this.token.address, { from: owner }).should.be.rejectedWith(EVMThrow);
   });
 
   it('should return the non-vested tokens when revoked by owner', async function () {
-    await increaseTimeTo(this.cliff + duration.weeks(1));
+    await increaseTimeTo(this.start + this.cliff + duration.weeks(1));
     await this.vesting.release(this.token.address);
 
     const vested = await this.vesting.vestedAmount(this.token.address);
@@ -95,7 +94,7 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
   });
 
   it('should keep the vested tokens when revoked by owner', async function () {
-    await increaseTimeTo(this.cliff + duration.weeks(1));
+    await increaseTimeTo(this.start + this.cliff + duration.weeks(1));
     await this.vesting.release(this.token.address);
 
     const vested = await this.vesting.vestedAmount(this.token.address);