2017-07-05 10:28:15 +00:00
pragma solidity ^ 0 . 4 . 11 ;
import " ../math/Math.sol " ;
import " ./StandardToken.sol " ;
import " ./LimitedTransferToken.sol " ;
/**
* @ title Vested token
* @ dev Tokens that can be vested for a group of addresses .
* /
contract VestedToken is StandardToken , LimitedTransferToken {
uint256 MAX_GRANTS_PER_ADDRESS = 20 ;
struct TokenGrant {
address granter ; // 20 bytes
uint256 value ; // 32 bytes
uint64 cliff ;
uint64 vesting ;
uint64 start ; // 3 * 8 = 24 bytes
bool revokable ;
bool burnsOnRevoke ; // 2 * 1 = 2 bits? or 2 bytes?
} // total 78 bytes = 3 sstore per operation (32 per sstore)
mapping ( address => TokenGrant [ ] ) public grants ;
event NewTokenGrant ( address indexed from , address indexed to , uint256 value , uint256 grantId ) ;
/**
* @ dev Grant tokens to a specified address
* @ param _to address The address which the tokens will be granted to .
* @ param _value uint256 The amount of tokens to be granted .
* @ param _start uint64 Time of the beginning of the grant .
* @ param _cliff uint64 Time of the cliff period .
* @ param _vesting uint64 The vesting period .
* /
function grantVestedTokens (
address _to ,
uint256 _value ,
uint64 _start ,
uint64 _cliff ,
uint64 _vesting ,
bool _revokable ,
bool _burnsOnRevoke
) public {
// Check for date inconsistencies that may cause unexpected behavior
if ( _cliff < _start || _vesting < _cliff ) {
2018-07-11 23:49:00 +00:00
revert ( ) ;
2017-07-05 10:28:15 +00:00
}
2018-07-11 23:49:00 +00:00
if ( tokenGrantsCount ( _to ) > MAX_GRANTS_PER_ADDRESS ) revert ( ) ; // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting).
2017-07-05 10:28:15 +00:00
uint256 count = grants [ _to ] . push (
TokenGrant (
_revokable ? msg . sender : 0 , // avoid storing an extra 20 bytes when it is non-revokable
_value ,
_cliff ,
_vesting ,
_start ,
_revokable ,
_burnsOnRevoke
)
) ;
transfer ( _to , _value ) ;
2018-06-27 08:35:38 +00:00
emit NewTokenGrant ( msg . sender , _to , _value , count - 1 ) ;
2017-07-05 10:28:15 +00:00
}
/**
2018-07-10 07:18:19 +00:00
* @ dev Revoke the grant of tokens of a specified address .
2017-07-05 10:28:15 +00:00
* @ param _holder The address which will have its tokens revoked .
* @ param _grantId The id of the token grant .
* /
function revokeTokenGrant ( address _holder , uint256 _grantId ) public {
2018-07-11 11:30:46 +00:00
TokenGrant storage grant = grants [ _holder ] [ _grantId ] ;
2017-07-05 10:28:15 +00:00
if ( ! grant . revokable ) { // Check if grant was revokable
2018-07-11 23:49:00 +00:00
revert ( ) ;
2017-07-05 10:28:15 +00:00
}
if ( grant . granter != msg . sender ) { // Only granter can revoke it
2018-07-11 23:49:00 +00:00
revert ( ) ;
2017-07-05 10:28:15 +00:00
}
address receiver = grant . burnsOnRevoke ? 0xdead : msg . sender ;
uint256 nonVested = nonVestedTokens ( grant , uint64 ( now ) ) ;
// remove grant from array
delete grants [ _holder ] [ _grantId ] ;
grants [ _holder ] [ _grantId ] = grants [ _holder ] [ grants [ _holder ] . length . sub ( 1 ) ] ;
grants [ _holder ] . length -= 1 ;
balances [ receiver ] = balances [ receiver ] . add ( nonVested ) ;
balances [ _holder ] = balances [ _holder ] . sub ( nonVested ) ;
2018-06-27 08:35:38 +00:00
emit Transfer ( _holder , receiver , nonVested ) ;
2017-07-05 10:28:15 +00:00
}
/**
* @ dev Calculate the total amount of transferable tokens of a holder at a given time
* @ param holder address The address of the holder
* @ param time uint64 The specific time .
* @ return An uint256 representing a holder ' s total amount of transferable tokens.
* /
2018-07-02 09:14:28 +00:00
function transferableTokens ( address holder , uint64 time ) view public returns ( uint256 ) {
2017-07-05 10:28:15 +00:00
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 ++ ) {
nonVested = SafeMath . add ( nonVested , nonVestedTokens ( grants [ holder ] [ i ] , time ) ) ;
}
// Balance - totalNonVested is the amount of tokens a holder can transfer at any given time
uint256 vestedTransferable = SafeMath . sub ( 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 Math . min256 ( vestedTransferable , super . transferableTokens ( holder , time ) ) ;
}
/**
* @ dev Check the amount of grants that an address has .
* @ param _holder The holder of the grants .
* @ return A uint256 representing the total amount of grants .
* /
2018-07-04 17:20:51 +00:00
function tokenGrantsCount ( address _holder ) public view returns ( uint256 index ) {
2017-07-05 10:28:15 +00:00
return grants [ _holder ] . length ;
}
/**
2018-07-10 07:18:19 +00:00
* @ dev Calculate amount of vested tokens at a specific time .
* @ param tokens uint256 The amount of tokens granted .
2017-07-05 10:28:15 +00:00
* @ param time uint64 The time to be checked
2018-07-10 07:18:19 +00:00
* @ param start uint64 A time representing the beginning of the grant
2017-07-05 10:28:15 +00:00
* @ param cliff uint64 The cliff period .
* @ param vesting uint64 The vesting period .
2018-07-10 07:18:19 +00:00
* @ return An uint256 representing the amount of vested tokens of a specific grant .
2017-07-05 10:28:15 +00:00
* transferableTokens
* | _ / -- -- -- -- vestedTokens rect
* | _ /
* | _ /
* | _ /
* | _ /
* | /
* | . |
* | . |
* | . |
* | . |
* | . |
* | . |
* += == += == == == == == + -- -- -- -- - + -- -- -- -- -- > time
* Start Clift Vesting
* /
function calculateVestedTokens (
uint256 tokens ,
uint256 time ,
uint256 start ,
uint256 cliff ,
2018-07-04 17:20:51 +00:00
uint256 vesting ) public view returns ( uint256 )
2017-07-05 10:28:15 +00:00
{
// 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 = SafeMath . div (
SafeMath . mul (
tokens ,
SafeMath . sub ( time , start )
) ,
SafeMath . sub ( vesting , start )
) ;
return vestedTokens ;
}
/**
2018-07-10 07:18:19 +00:00
* @ dev Get all information about a specific grant .
2017-07-05 10:28:15 +00:00
* @ param _holder The address which will have its tokens revoked .
* @ param _grantId The id of the token grant .
* @ return Returns all the values that represent a TokenGrant ( address , value , start , cliff ,
* revokability , burnsOnRevoke , and vesting ) plus the vested value at the current time .
* /
2018-07-04 17:20:51 +00:00
function tokenGrant ( address _holder , uint256 _grantId ) public view returns ( address granter , uint256 value , uint256 vested , uint64 start , uint64 cliff , uint64 vesting , bool revokable , bool burnsOnRevoke ) {
2018-07-11 11:30:46 +00:00
TokenGrant storage grant = grants [ _holder ] [ _grantId ] ;
2017-07-05 10:28:15 +00:00
granter = grant . granter ;
value = grant . value ;
start = grant . start ;
cliff = grant . cliff ;
vesting = grant . vesting ;
revokable = grant . revokable ;
burnsOnRevoke = grant . burnsOnRevoke ;
vested = vestedTokens ( grant , uint64 ( now ) ) ;
}
/**
* @ dev Get the amount of vested tokens at a specific time .
* @ param grant TokenGrant The grant to be checked .
* @ param time The time to be checked
* @ return An uint256 representing the amount of vested tokens of a specific grant at a specific time .
* /
2018-07-14 21:42:01 +00:00
function vestedTokens ( TokenGrant memory grant , uint64 time ) private view returns ( uint256 ) {
2017-07-05 10:28:15 +00:00
return calculateVestedTokens (
grant . value ,
uint256 ( time ) ,
uint256 ( grant . start ) ,
uint256 ( grant . cliff ) ,
uint256 ( grant . vesting )
) ;
}
/**
* @ dev Calculate the amount of non vested tokens at a specific time .
* @ param grant TokenGrant The grant to be checked .
* @ param time uint64 The time to be checked
2018-07-10 07:18:19 +00:00
* @ return An uint256 representing the amount of non vested tokens of a specific grant on the
2017-07-05 10:28:15 +00:00
* passed time frame .
* /
2018-07-14 21:42:01 +00:00
function nonVestedTokens ( TokenGrant memory grant , uint64 time ) private view returns ( uint256 ) {
2017-07-05 10:28:15 +00:00
return grant . value . sub ( vestedTokens ( grant , time ) ) ;
}
/**
* @ dev Calculate the date when the holder can trasfer all its tokens
* @ param holder address The address of the holder
* @ return An uint256 representing the date of the last transferable tokens .
* /
2018-07-02 09:14:28 +00:00
function lastTokenIsTransferableDate ( address holder ) view public returns ( uint64 date ) {
2017-07-05 10:28:15 +00:00
date = uint64 ( now ) ;
uint256 grantIndex = grants [ holder ] . length ;
for ( uint256 i = 0 ; i < grantIndex ; i ++ ) {
date = Math . max64 ( grants [ holder ] [ i ] . vesting , date ) ;
}
}
}