// File: @openzeppelin/contracts/utils/Context.sol
pragma solidity >=0.6.0 <0.8.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: @openzeppelin/contracts/access/Ownable.sol
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender)
external
view
returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: @openzeppelin/contracts/math/SafeMath.sol
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
// File: contracts/Interfaces/TokenVestingInterface.sol
pragma solidity 0.6.12;
abstract contract TokenVestingInterface {
event VestingScheduleCreated(
address indexed vestingLocation,
uint32 cliffDuration,
uint32 duration,
uint32 interval,
bool isRevocable
);
event VestingTokensGranted(
address indexed beneficiary,
uint256 vestingAmount,
uint32 startDay,
address vestingLocation
);
event VestingTokensClaimed(address indexed beneficiary, uint256 amount);
event GrantRevoked(address indexed grantHolder);
struct vestingSchedule {
bool isRevocable; /* true if the vesting option is revocable (a gift), false if irrevocable (purchased) */
uint32 cliffDuration; /* Duration of the cliff, with respect to the grant start day, in days. */
uint32 duration; /* Duration of the vesting schedule, with respect to the grant start day, in days. */
uint32 interval; /* Duration in days of the vesting interval. */
}
struct tokenGrant {
bool isActive; /* true if this vesting entry is active and in-effect entry. */
bool wasRevoked; /* true if this vesting schedule was revoked. */
uint32 startDay; /* Start day of the grant, in days since the UNIX epoch (start of day). */
uint256 amount; /* Total number of tokens that vest. */
address vestingLocation; /* Address of wallet that is holding the vesting schedule. */
uint256 claimedAmount; /* Out of vested amount, the amount that has been already transferred to beneficiary */
}
function token() public view virtual returns (IERC20);
function kill(address payable beneficiary) external virtual;
function withdrawTokens(address beneficiary, uint256 amount) external virtual;
// =========================================================================
// === Methods for claiming tokens.
// =========================================================================
function claimVestingTokens(address beneficiary) external virtual;
function claimVestingTokensForAll() external virtual;
// =========================================================================
// === Methods for administratively creating a vesting schedule for an account.
// =========================================================================
function setVestingSchedule(
address vestingLocation,
uint32 cliffDuration,
uint32 duration,
uint32 interval,
bool isRevocable
) external virtual;
// =========================================================================
// === Token grants (general-purpose)
// === Methods to be used for administratively creating one-off token grants with vesting schedules.
// =========================================================================
function addGrant(
address beneficiary,
uint256 vestingAmount,
uint32 startDay,
uint32 duration,
uint32 cliffDuration,
uint32 interval,
bool isRevocable
) public virtual;
function addGrantWithScheduleAt(
address beneficiary,
uint256 vestingAmount,
uint32 startDay,
address vestingLocation
) external virtual;
function addGrantFromToday(
address beneficiary,
uint256 vestingAmount,
uint32 duration,
uint32 cliffDuration,
uint32 interval,
bool isRevocable
) external virtual;
// =========================================================================
// === Check vesting.
// =========================================================================
function today() public view virtual returns (uint32 dayNumber);
function getGrantInfo(address grantHolder, uint32 onDayOrToday)
external
view
virtual
returns (
uint256 amountVested,
uint256 amountNotVested,
uint256 amountOfGrant,
uint256 amountAvailable,
uint256 amountClaimed,
uint32 vestStartDay,
bool isActive,
bool wasRevoked
);
function getScheduleAtInfo(address vestingLocation)
public
view
virtual
returns (
bool isRevocable,
uint32 vestDuration,
uint32 cliffDuration,
uint32 vestIntervalDays
);
function getScheduleInfo(address grantHolder)
external
view
virtual
returns (
bool isRevocable,
uint32 vestDuration,
uint32 cliffDuration,
uint32 vestIntervalDays
);
// =========================================================================
// === Grant revocation
// =========================================================================
function revokeGrant(address grantHolder) external virtual;
}
// File: contracts/Vesting/TokenVesting.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
/**
* @title Contract for token vesting schedules
*
* @dev Contract which gives the ability to act as a pool of funds for allocating
* tokens to any number of other addresses. Token grants support the ability to vest over time in
* accordance a predefined vesting schedule. A given wallet can receive no more than one token grant.
*/
contract TokenVesting is TokenVestingInterface, Context, Ownable {
using SafeMath for uint256;
// Date-related constants for sanity-checking dates to reject obvious erroneous inputs
// and conversions from seconds to days and years that are more or less leap year-aware.
uint32 private constant _THOUSAND_YEARS_DAYS = 365243; /* See https://www.timeanddate.com/date/durationresult.html?m1=1&d1=1&y1=2000&m2=1&d2=1&y2=3000 */
uint32 private constant _TEN_YEARS_DAYS = _THOUSAND_YEARS_DAYS / 100; /* Includes leap years (though it doesn't really matter) */
uint32 private constant _SECONDS_PER_DAY = 24 * 60 * 60; /* 86400 seconds in a day */
uint32 private constant _JAN_1_2000_SECONDS = 946684800; /* Saturday, January 1, 2000 0:00:00 (GMT) (see https://www.epochconverter.com/) */
uint32 private constant _JAN_1_2000_DAYS =
_JAN_1_2000_SECONDS / _SECONDS_PER_DAY;
uint32 private constant _JAN_1_3000_DAYS =
_JAN_1_2000_DAYS + _THOUSAND_YEARS_DAYS;
modifier onlyOwnerOrSelf(address account) {
require(
_msgSender() == owner() || _msgSender() == account,
"onlyOwnerOrSelf"
);
_;
}
mapping(address => vestingSchedule) private _vestingSchedules;
mapping(address => tokenGrant) private _tokenGrants;
address[] private _allBeneficiaries;
IERC20 private _token;
constructor(IERC20 token_) public {
require(address(token_) != address(0), "token must be non-zero address");
_token = token_;
}
function token() public view override returns (IERC20) {
return _token;
}
function kill(address payable beneficiary) external override onlyOwner {
_withdrawTokens(beneficiary, token().balanceOf(address(this)));
selfdestruct(beneficiary);
}
function withdrawTokens(address beneficiary, uint256 amount)
external
override
onlyOwner
{
_withdrawTokens(beneficiary, amount);
}
function _withdrawTokens(address beneficiary, uint256 amount) internal {
require(amount > 0, "amount must be > 0");
require(
amount <= token().balanceOf(address(this)),
"amount must be <= current balance"
);
require(token().transfer(beneficiary, amount));
}
// =========================================================================
// === Methods for claiming tokens.
// =========================================================================
function claimVestingTokens(address beneficiary)
external
override
onlyOwnerOrSelf(beneficiary)
{
_claimVestingTokens(beneficiary);
}
function claimVestingTokensForAll() external override onlyOwner {
for (uint256 i = 0; i < _allBeneficiaries.length; i++) {
_claimVestingTokens(_allBeneficiaries[i]);
}
}
function _claimVestingTokens(address beneficiary) internal {
uint256 amount = _getAvailableAmount(beneficiary, 0);
if (amount > 0) {
_deliverTokens(beneficiary, amount);
_tokenGrants[beneficiary].claimedAmount = _tokenGrants[beneficiary]
.claimedAmount
.add(amount);
emit VestingTokensClaimed(beneficiary, amount);
}
}
function _deliverTokens(address beneficiary, uint256 amount) internal {
require(amount > 0, "amount must be > 0");
require(
amount <= token().balanceOf(address(this)),
"amount must be <= current balance"
);
require(
_tokenGrants[beneficiary].claimedAmount.add(amount) <=
_tokenGrants[beneficiary].amount,
"new claimed amount must be <= total grant amount"
);
require(token().transfer(beneficiary, amount));
}
// =========================================================================
// === Methods for administratively creating a vesting schedule for an account.
// =========================================================================
/**
* @dev This one-time operation permanently establishes a vesting schedule in the given account.
*
* @param cliffDuration = Duration of the cliff, with respect to the grant start day, in days.
* @param duration = Duration of the vesting schedule, with respect to the grant start day, in days.
* @param interval = Number of days between vesting increases.
* @param isRevocable = True if the grant can be revoked (i.e. was a gift) or false if it cannot
* be revoked (i.e. tokens were purchased).
*/
function setVestingSchedule(
address vestingLocation,
uint32 cliffDuration,
uint32 duration,
uint32 interval,
bool isRevocable
) external override onlyOwner {
_setVestingSchedule(
vestingLocation,
cliffDuration,
duration,
interval,
isRevocable
);
}
function _setVestingSchedule(
address vestingLocation,
uint32 cliffDuration,
uint32 duration,
uint32 interval,
bool isRevocable
) internal {
// Check for a valid vesting schedule given (disallow absurd values to reject likely bad input).
require(
duration > 0 &&
duration <= _TEN_YEARS_DAYS &&
cliffDuration < duration &&
interval >= 1,
"invalid vesting schedule"
);
// Make sure the duration values are in harmony with interval (both should be an exact multiple of interval).
require(
duration % interval == 0 && cliffDuration % interval == 0,
"invalid cliff/duration for interval"
);
// Create and populate a vesting schedule.
_vestingSchedules[vestingLocation] = vestingSchedule(
isRevocable,
cliffDuration,
duration,
interval
);
// Emit the event.
emit VestingScheduleCreated(
vestingLocation,
cliffDuration,
duration,
interval,
isRevocable
);
}
// =========================================================================
// === Token grants (general-purpose)
// === Methods to be used for administratively creating one-off token grants with vesting schedules.
// =========================================================================
/**
* @dev Grants tokens to an account.
*
* @param beneficiary = Address to which tokens will be granted.
* @param vestingAmount = The number of tokens subject to vesting.
* @param startDay = Start day of the grant's vesting schedule, in days since the UNIX epoch
* (start of day). The startDay may be given as a date in the future or in the past, going as far
* back as year 2000.
* @param vestingLocation = Account where the vesting schedule is held (must already exist).
*/
function _addGrant(
address beneficiary,
uint256 vestingAmount,
uint32 startDay,
address vestingLocation
) internal {
// Make sure no prior grant is in effect.
require(!_tokenGrants[beneficiary].isActive, "grant already exists");
// Check for valid vestingAmount
require(
vestingAmount > 0 &&
startDay >= _JAN_1_2000_DAYS &&
startDay < _JAN_1_3000_DAYS,
"invalid vesting params"
);
// Create and populate a token grant, referencing vesting schedule.
_tokenGrants[beneficiary] = tokenGrant(
true, // isActive
false, // wasRevoked
startDay,
vestingAmount,
vestingLocation, // The wallet address where the vesting schedule is kept.
0 // claimedAmount
);
_allBeneficiaries.push(beneficiary);
// Emit the event.
emit VestingTokensGranted(
beneficiary,
vestingAmount,
startDay,
vestingLocation
);
}
/**
* @dev Grants tokens to an address, including a portion that will vest over time
* according to a set vesting schedule. The overall duration and cliff duration of the grant must
* be an even multiple of the vesting interval.
*
* @param beneficiary = Address to which tokens will be granted.
* @param vestingAmount = The number of tokens subject to vesting.
* @param startDay = Start day of the grant's vesting schedule, in days since the UNIX epoch
* (start of day). The startDay may be given as a date in the future or in the past, going as far
* back as year 2000.
* @param duration = Duration of the vesting schedule, with respect to the grant start day, in days.
* @param cliffDuration = Duration of the cliff, with respect to the grant start day, in days.
* @param interval = Number of days between vesting increases.
* @param isRevocable = True if the grant can be revoked (i.e. was a gift) or false if it cannot
* be revoked (i.e. tokens were purchased).
*/
function addGrant(
address beneficiary,
uint256 vestingAmount,
uint32 startDay,
uint32 duration,
uint32 cliffDuration,
uint32 interval,
bool isRevocable
) public override onlyOwner {
// Make sure no prior vesting schedule has been set.
require(!_tokenGrants[beneficiary].isActive, "grant already exists");
// The vesting schedule is unique to this wallet and so will be stored here,
_setVestingSchedule(
beneficiary,
cliffDuration,
duration,
interval,
isRevocable
);
// Issue tokens to the beneficiary, using beneficiary's own vesting schedule.
_addGrant(beneficiary, vestingAmount, startDay, beneficiary);
}
function addGrantWithScheduleAt(
address beneficiary,
uint256 vestingAmount,
uint32 startDay,
address vestingLocation
) external override onlyOwner {
// Issue tokens to the beneficiary, using custom vestingLocation.
_addGrant(beneficiary, vestingAmount, startDay, vestingLocation);
}
function addGrantFromToday(
address beneficiary,
uint256 vestingAmount,
uint32 duration,
uint32 cliffDuration,
uint32 interval,
bool isRevocable
) external override onlyOwner {
addGrant(
beneficiary,
vestingAmount,
today(),
duration,
cliffDuration,
interval,
isRevocable
);
}
// =========================================================================
// === Check vesting.
// =========================================================================
function today() public view virtual override returns (uint32 dayNumber) {
return uint32(block.timestamp / _SECONDS_PER_DAY);
}
function _effectiveDay(uint32 onDayOrToday)
internal
view
returns (uint32 dayNumber)
{
return onDayOrToday == 0 ? today() : onDayOrToday;
}
/**
* @dev Determines the amount of tokens that have not vested in the given account.
*
* The math is: not vested amount = vesting amount * (end date - on date)/(end date - start date)
*
* @param grantHolder = The account to check.
* @param onDayOrToday = The day to check for, in days since the UNIX epoch. Can pass
* the special value 0 to indicate today.
*/
function _getNotVestedAmount(address grantHolder, uint32 onDayOrToday)
internal
view
returns (uint256 amountNotVested)
{
tokenGrant storage grant = _tokenGrants[grantHolder];
vestingSchedule storage vesting = _vestingSchedules[grant.vestingLocation];
uint32 onDay = _effectiveDay(onDayOrToday);
// If there's no schedule, or before the vesting cliff, then the full amount is not vested.
if (!grant.isActive || onDay < grant.startDay + vesting.cliffDuration) {
// None are vested (all are not vested)
return grant.amount;
}
// If after end of vesting, then the not vested amount is zero (all are vested).
else if (onDay >= grant.startDay + vesting.duration) {
// All are vested (none are not vested)
return uint256(0);
}
// Otherwise a fractional amount is vested.
else {
// Compute the exact number of days vested.
uint32 daysVested = onDay - grant.startDay;
// Adjust result rounding down to take into consideration the interval.
uint32 effectiveDaysVested = (daysVested / vesting.interval) *
vesting.interval;
// Compute the fraction vested from schedule using 224.32 fixed point math for date range ratio.
// Note: This is safe in 256-bit math because max value of X billion tokens = X*10^27 wei, and
// typical token amounts can fit into 90 bits. Scaling using a 32 bits value results in only 125
// bits before reducing back to 90 bits by dividing. There is plenty of room left, even for token
// amounts many orders of magnitude greater than mere billions.
uint256 vested = grant.amount.mul(effectiveDaysVested).div(
vesting.duration
);
uint256 result = grant.amount.sub(vested);
require(result <= grant.amount && vested <= grant.amount);
return result;
}
}
/**
* @dev Computes the amount of funds in the given account which are available for use as of
* the given day, i.e. the claimable amount.
*
* The math is: available amount = totalGrantAmount - notVestedAmount - claimedAmount.
*
* @param grantHolder = The account to check.
* @param onDay = The day to check for, in days since the UNIX epoch.
*/
function _getAvailableAmount(address grantHolder, uint32 onDay)
internal
view
returns (uint256 amountAvailable)
{
tokenGrant storage grant = _tokenGrants[grantHolder];
return
_getAvailableAmountImpl(grant, _getNotVestedAmount(grantHolder, onDay));
}
function _getAvailableAmountImpl(
tokenGrant storage grant,
uint256 notVastedOnDay
) internal view returns (uint256 amountAvailable) {
uint256 vested = grant.amount.sub(notVastedOnDay);
if (vested < grant.claimedAmount) {
// .sub below will fail, only possible when grant revoked
require(vested == 0 && grant.wasRevoked);
return 0;
}
uint256 result = vested.sub(grant.claimedAmount);
require(
result <= grant.amount &&
grant.claimedAmount.add(result) <= grant.amount &&
result <= vested &&
vested <= grant.amount
);
return result;
}
/**
* @dev returns all information about the grant's vesting as of the given day
* for the given account. Only callable by the account holder or a contract owner.
*
* @param grantHolder = The address to do this for.
* @param onDayOrToday = The day to check for, in days since the UNIX epoch. Can pass
* the special value 0 to indicate today.
* return = A tuple with the following values:
* amountVested = the amount that is already vested
* amountNotVested = the amount that is not yet vested (equal to vestingAmount - vestedAmount)
* amountOfGrant = the total amount of tokens subject to vesting.
* amountAvailable = the amount of funds in the given account which are available for use as of the given day
* amountClaimed = out of amountVested, the amount that has been already transferred to beneficiary
* vestStartDay = starting day of the grant (in days since the UNIX epoch).
* isActive = true if the vesting schedule is currently active.
* wasRevoked = true if the vesting schedule was revoked.
*/
function getGrantInfo(address grantHolder, uint32 onDayOrToday)
external
view
override
returns (
uint256 amountVested,
uint256 amountNotVested,
uint256 amountOfGrant,
uint256 amountAvailable,
uint256 amountClaimed,
uint32 vestStartDay,
bool isActive,
bool wasRevoked
)
{
tokenGrant storage grant = _tokenGrants[grantHolder];
uint256 notVestedAmount = _getNotVestedAmount(grantHolder, onDayOrToday);
return (
grant.amount.sub(notVestedAmount),
notVestedAmount,
grant.amount,
_getAvailableAmountImpl(grant, notVestedAmount),
grant.claimedAmount,
grant.startDay,
grant.isActive,
grant.wasRevoked
);
}
function getScheduleAtInfo(address vestingLocation)
public
view
override
returns (
bool isRevocable,
uint32 vestDuration,
uint32 cliffDuration,
uint32 vestIntervalDays
)
{
vestingSchedule storage vesting = _vestingSchedules[vestingLocation];
return (
vesting.isRevocable,
vesting.duration,
vesting.cliffDuration,
vesting.interval
);
}
function getScheduleInfo(address grantHolder)
external
view
override
returns (
bool isRevocable,
uint32 vestDuration,
uint32 cliffDuration,
uint32 vestIntervalDays
)
{
tokenGrant storage grant = _tokenGrants[grantHolder];
return getScheduleAtInfo(grant.vestingLocation);
}
// =========================================================================
// === Grant revocation
// =========================================================================
/**
* @dev If the account has a revocable grant, this forces the grant to end immediately.
* After this function is called, getGrantInfo will return incomplete data
* and there will be no possibility to claim non-claimed tokens
*
* @param grantHolder = Address to which tokens will be granted.
*/
function revokeGrant(address grantHolder) external override onlyOwner {
tokenGrant storage grant = _tokenGrants[grantHolder];
vestingSchedule storage vesting = _vestingSchedules[grant.vestingLocation];
// Make sure a vesting schedule has previously been set.
require(grant.isActive, "no active grant");
// Make sure it's revocable.
require(vesting.isRevocable, "irrevocable");
// Kill the grant by updating wasRevoked and isActive.
_tokenGrants[grantHolder].wasRevoked = true;
_tokenGrants[grantHolder].isActive = false;
// Emits the GrantRevoked event.
emit GrantRevoked(grantHolder);
}
}
{
"compilationTarget": {
"TokenVesting.sol": "TokenVesting"
},
"evmVersion": "byzantium",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"token_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"grantHolder","type":"address"}],"name":"GrantRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vestingLocation","type":"address"},{"indexed":false,"internalType":"uint32","name":"cliffDuration","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"duration","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"interval","type":"uint32"},{"indexed":false,"internalType":"bool","name":"isRevocable","type":"bool"}],"name":"VestingScheduleCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VestingTokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"startDay","type":"uint32"},{"indexed":false,"internalType":"address","name":"vestingLocation","type":"address"}],"name":"VestingTokensGranted","type":"event"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"internalType":"uint32","name":"startDay","type":"uint32"},{"internalType":"uint32","name":"duration","type":"uint32"},{"internalType":"uint32","name":"cliffDuration","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"name":"addGrant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"internalType":"uint32","name":"duration","type":"uint32"},{"internalType":"uint32","name":"cliffDuration","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"name":"addGrantFromToday","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"internalType":"uint32","name":"startDay","type":"uint32"},{"internalType":"address","name":"vestingLocation","type":"address"}],"name":"addGrantWithScheduleAt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"claimVestingTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimVestingTokensForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"grantHolder","type":"address"},{"internalType":"uint32","name":"onDayOrToday","type":"uint32"}],"name":"getGrantInfo","outputs":[{"internalType":"uint256","name":"amountVested","type":"uint256"},{"internalType":"uint256","name":"amountNotVested","type":"uint256"},{"internalType":"uint256","name":"amountOfGrant","type":"uint256"},{"internalType":"uint256","name":"amountAvailable","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint32","name":"vestStartDay","type":"uint32"},{"internalType":"bool","name":"isActive","type":"bool"},{"internalType":"bool","name":"wasRevoked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vestingLocation","type":"address"}],"name":"getScheduleAtInfo","outputs":[{"internalType":"bool","name":"isRevocable","type":"bool"},{"internalType":"uint32","name":"vestDuration","type":"uint32"},{"internalType":"uint32","name":"cliffDuration","type":"uint32"},{"internalType":"uint32","name":"vestIntervalDays","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"grantHolder","type":"address"}],"name":"getScheduleInfo","outputs":[{"internalType":"bool","name":"isRevocable","type":"bool"},{"internalType":"uint32","name":"vestDuration","type":"uint32"},{"internalType":"uint32","name":"cliffDuration","type":"uint32"},{"internalType":"uint32","name":"vestIntervalDays","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"beneficiary","type":"address"}],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"grantHolder","type":"address"}],"name":"revokeGrant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vestingLocation","type":"address"},{"internalType":"uint32","name":"cliffDuration","type":"uint32"},{"internalType":"uint32","name":"duration","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"name":"setVestingSchedule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"today","outputs":[{"internalType":"uint32","name":"dayNumber","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]