Reorg code
This commit is contained in:
@ -75,19 +75,71 @@ contract VestedToken is StandardToken, TransferableToken {
|
|||||||
Transfer(_holder, receiver, nonVested);
|
Transfer(_holder, receiver, nonVested);
|
||||||
}
|
}
|
||||||
|
|
||||||
function transferableTokens(address holder, uint64 time) constant public returns (uint256 nonVested) {
|
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
|
||||||
uint256 grantIndex = tokenGrantsCount(holder);
|
uint256 grantIndex = tokenGrantsCount(holder);
|
||||||
|
|
||||||
|
if (grantIndex == 0) return balanceOf(holder); // shortcut for holder without grants
|
||||||
|
|
||||||
|
// Iterate through all the grants the holder has, and add all non-vested tokens
|
||||||
|
uint256 nonVested = 0;
|
||||||
for (uint256 i = 0; i < grantIndex; i++) {
|
for (uint256 i = 0; i < grantIndex; i++) {
|
||||||
nonVested = safeAdd(nonVested, nonVestedTokens(grants[holder][i], time));
|
nonVested = safeAdd(nonVested, nonVestedTokens(grants[holder][i], time));
|
||||||
}
|
}
|
||||||
|
|
||||||
return min256(safeSub(balances[holder], nonVested), super.transferableTokens(holder, time));
|
// Balance - totalNonVested is the amount of tokens a holder can transfer at any given time
|
||||||
|
uint256 vestedTransferable = safeSub(balanceOf(holder), nonVested);
|
||||||
|
|
||||||
|
// Return the minimum of how many vested can transfer and other value
|
||||||
|
// in case there are other limiting transferability factors (default is balanceOf)
|
||||||
|
return min256(vestedTransferable, super.transferableTokens(holder, time));
|
||||||
}
|
}
|
||||||
|
|
||||||
function tokenGrantsCount(address _holder) constant returns (uint index) {
|
function tokenGrantsCount(address _holder) constant returns (uint index) {
|
||||||
return grants[_holder].length;
|
return grants[_holder].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transferableTokens
|
||||||
|
// | _/-------- vestedTokens rect
|
||||||
|
// | _/
|
||||||
|
// | _/
|
||||||
|
// | _/
|
||||||
|
// | _/
|
||||||
|
// | /
|
||||||
|
// | .|
|
||||||
|
// | . |
|
||||||
|
// | . |
|
||||||
|
// | . |
|
||||||
|
// | . |
|
||||||
|
// | . |
|
||||||
|
// +===+===========+---------+----------> time
|
||||||
|
// Start Clift Vesting
|
||||||
|
function calculateVestedTokens(
|
||||||
|
uint256 tokens,
|
||||||
|
uint256 time,
|
||||||
|
uint256 start,
|
||||||
|
uint256 cliff,
|
||||||
|
uint256 vesting) constant returns (uint256)
|
||||||
|
{
|
||||||
|
// Shortcuts for before cliff and after vesting cases.
|
||||||
|
if (time < cliff) return 0;
|
||||||
|
if (time >= vesting) return tokens;
|
||||||
|
|
||||||
|
// Interpolate all vested tokens.
|
||||||
|
// As before cliff the shortcut returns 0, we can use just calculate a value
|
||||||
|
// in the vesting rect (as shown in above's figure)
|
||||||
|
|
||||||
|
// vestedTokens = tokens * (time - start) / (vesting - start)
|
||||||
|
uint256 vestedTokens = safeDiv(
|
||||||
|
safeMul(
|
||||||
|
tokens,
|
||||||
|
safeSub(time, start)
|
||||||
|
),
|
||||||
|
safeSub(vesting, start)
|
||||||
|
);
|
||||||
|
|
||||||
|
return vestedTokens;
|
||||||
|
}
|
||||||
|
|
||||||
function tokenGrant(address _holder, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) {
|
function tokenGrant(address _holder, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) {
|
||||||
TokenGrant grant = grants[_holder][_grantId];
|
TokenGrant grant = grants[_holder][_grantId];
|
||||||
|
|
||||||
@ -112,29 +164,6 @@ contract VestedToken is StandardToken, TransferableToken {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateVestedTokens(
|
|
||||||
uint256 tokens,
|
|
||||||
uint256 time,
|
|
||||||
uint256 start,
|
|
||||||
uint256 cliff,
|
|
||||||
uint256 vesting) constant returns (uint256 vestedTokens)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (time < cliff) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (time > vesting) {
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 cliffTokens = safeDiv(safeMul(tokens, safeSub(cliff, start)), safeSub(vesting, start));
|
|
||||||
vestedTokens = cliffTokens;
|
|
||||||
|
|
||||||
uint256 vestingTokens = safeSub(tokens, cliffTokens);
|
|
||||||
|
|
||||||
vestedTokens = safeAdd(vestedTokens, safeDiv(safeMul(vestingTokens, safeSub(time, cliff)), safeSub(vesting, start)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
|
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
|
||||||
return safeSub(grant.value, vestedTokens(grant, time));
|
return safeSub(grant.value, vestedTokens(grant, time));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,6 +95,24 @@ contract('VestedToken', function(accounts) {
|
|||||||
await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] })
|
await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] })
|
||||||
assert.equal(await token.balanceOf(accounts[7]), tokenAmount);
|
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, false, false, { 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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getting a non-revokable token grant', async () => {
|
describe('getting a non-revokable token grant', async () => {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ require('babel-polyfill');
|
|||||||
var HDWalletProvider = require('truffle-hdwallet-provider');
|
var HDWalletProvider = require('truffle-hdwallet-provider');
|
||||||
|
|
||||||
var mnemonic = '[REDACTED]';
|
var mnemonic = '[REDACTED]';
|
||||||
var provider = new HDWalletProvider(mnemonic, 'https://ropsten.infura.io/');
|
// var provider = new HDWalletProvider(mnemonic, 'https://ropsten.infura.io/');
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -14,9 +14,11 @@ module.exports = {
|
|||||||
port: 8545,
|
port: 8545,
|
||||||
network_id: '*'
|
network_id: '*'
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
ropsten: {
|
ropsten: {
|
||||||
provider: provider,
|
provider: provider,
|
||||||
network_id: 3 // official id of the ropsten network
|
network_id: 3 // official id of the ropsten network
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user