/** * Source Code first verified at https://etherscan.io on Tuesday, April 16, 2019 (UTC) */ pragma solidity ^0.5.0; contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } contract CommunityVesting is Ownable { using SafeMath for uint256; mapping (address => Holding) public holdings; uint256 constant public MinimumHoldingPeriod = 90 days; uint256 constant public Interval = 90 days; uint256 constant public MaximumHoldingPeriod = 360 days; uint256 constant public CommunityCap = 14300000 ether; // 14.3 million tokens uint256 public totalCommunityTokensCommitted; struct Holding { uint256 tokensCommitted; uint256 tokensRemaining; uint256 startTime; } event CommunityVestingInitialized(address _to, uint256 _tokens, uint256 _startTime); event CommunityVestingUpdated(address _to, uint256 _totalTokens, uint256 _startTime); function claimTokens(address beneficiary) external onlyOwner returns (uint256 tokensToClaim) { uint256 tokensRemaining = holdings[beneficiary].tokensRemaining; uint256 startTime = holdings[beneficiary].startTime; require(tokensRemaining > 0, "All tokens claimed"); require(now.sub(startTime) > MinimumHoldingPeriod, "Claiming period not started yet"); if (now.sub(startTime) >= MaximumHoldingPeriod) { tokensToClaim = tokensRemaining; delete holdings[beneficiary]; } else { uint256 percentage = calculatePercentageToRelease(startTime); uint256 tokensNotToClaim = (holdings[beneficiary].tokensCommitted.mul(100 - percentage)).div(100); tokensToClaim = tokensRemaining.sub(tokensNotToClaim); tokensRemaining = tokensNotToClaim; holdings[beneficiary].tokensRemaining = tokensRemaining; } } function calculatePercentageToRelease(uint256 _startTime) internal view returns (uint256 percentage) { // how many 90 day periods have passed uint periodsPassed = ((now.sub(_startTime)).div(Interval)); percentage = periodsPassed.mul(25); // 25% to be released every 90 days } function initializeVesting( address _beneficiary, uint256 _tokens, uint256 _startTime ) external onlyOwner { totalCommunityTokensCommitted = totalCommunityTokensCommitted.add(_tokens); require(totalCommunityTokensCommitted <= CommunityCap); if (holdings[_beneficiary].tokensCommitted != 0) { holdings[_beneficiary].tokensCommitted = holdings[_beneficiary].tokensCommitted.add(_tokens); holdings[_beneficiary].tokensRemaining = holdings[_beneficiary].tokensRemaining.add(_tokens); emit CommunityVestingUpdated( _beneficiary, holdings[_beneficiary].tokensRemaining, holdings[_beneficiary].startTime ); } else { holdings[_beneficiary] = Holding( _tokens, _tokens, _startTime ); emit CommunityVestingInitialized(_beneficiary, _tokens, _startTime); } } } contract EcosystemVesting is Ownable { using SafeMath for uint256; mapping (address => Holding) public holdings; uint256 constant public Interval = 90 days; uint256 constant public MaximumHoldingPeriod = 630 days; uint256 constant public EcosystemCap = 54100000 ether; // 54.1 million tokens uint256 public totalEcosystemTokensCommitted; struct Holding { uint256 tokensCommitted; uint256 tokensRemaining; uint256 startTime; } event EcosystemVestingInitialized(address _to, uint256 _tokens, uint256 _startTime); event EcosystemVestingUpdated(address _to, uint256 _totalTokens, uint256 _startTime); function claimTokens(address beneficiary) external onlyOwner returns (uint256 tokensToClaim) { uint256 tokensRemaining = holdings[beneficiary].tokensRemaining; uint256 startTime = holdings[beneficiary].startTime; require(tokensRemaining > 0, "All tokens claimed"); if (now.sub(startTime) >= MaximumHoldingPeriod) { tokensToClaim = tokensRemaining; delete holdings[beneficiary]; } else { uint256 permill = calculatePermillToRelease(startTime); uint256 tokensNotToClaim = (holdings[beneficiary].tokensCommitted.mul(1000 - permill)).div(1000); tokensToClaim = tokensRemaining.sub(tokensNotToClaim); tokensRemaining = tokensNotToClaim; holdings[beneficiary].tokensRemaining = tokensRemaining; } } function calculatePermillToRelease(uint256 _startTime) internal view returns (uint256 permill) { // how many 90 day periods have passed uint periodsPassed = ((now.sub(_startTime)).div(Interval)).add(1); permill = periodsPassed.mul(125); // 125 per thousand to be released every 90 days } function initializeVesting( address _beneficiary, uint256 _tokens, uint256 _startTime ) external onlyOwner { totalEcosystemTokensCommitted = totalEcosystemTokensCommitted.add(_tokens); require(totalEcosystemTokensCommitted <= EcosystemCap); if (holdings[_beneficiary].tokensCommitted != 0) { holdings[_beneficiary].tokensCommitted = holdings[_beneficiary].tokensCommitted.add(_tokens); holdings[_beneficiary].tokensRemaining = holdings[_beneficiary].tokensRemaining.add(_tokens); emit EcosystemVestingUpdated( _beneficiary, holdings[_beneficiary].tokensRemaining, holdings[_beneficiary].startTime ); } else { holdings[_beneficiary] = Holding( _tokens, _tokens, _startTime ); emit EcosystemVestingInitialized(_beneficiary, _tokens, _startTime); } } } contract SeedPrivateAdvisorVesting is Ownable { using SafeMath for uint256; enum User { Public, Seed, Private, Advisor } mapping (address => Holding) public holdings; uint256 constant public MinimumHoldingPeriod = 90 days; uint256 constant public Interval = 30 days; uint256 constant public MaximumHoldingPeriod = 180 days; uint256 constant public SeedCap = 28000000 ether; // 28 million tokens uint256 constant public PrivateCap = 9000000 ether; // 9 million tokens uint256 constant public AdvisorCap = 7400000 ether; // 7.4 million tokens uint256 public totalSeedTokensCommitted; uint256 public totalPrivateTokensCommitted; uint256 public totalAdvisorTokensCommitted; struct Holding { uint256 tokensCommitted; uint256 tokensRemaining; uint256 startTime; User user; } event VestingInitialized(address _to, uint256 _tokens, uint256 _startTime, User user); event VestingUpdated(address _to, uint256 _totalTokens, uint256 _startTime, User user); function claimTokens(address beneficiary) external onlyOwner returns (uint256 tokensToClaim) { uint256 tokensRemaining = holdings[beneficiary].tokensRemaining; uint256 startTime = holdings[beneficiary].startTime; require(tokensRemaining > 0, "All tokens claimed"); require(now.sub(startTime) > MinimumHoldingPeriod, "Claiming period not started yet"); if (now.sub(startTime) >= MaximumHoldingPeriod) { tokensToClaim = tokensRemaining; delete holdings[beneficiary]; } else { uint256 percentage = calculatePercentageToRelease(startTime); uint256 tokensNotToClaim = (holdings[beneficiary].tokensCommitted.mul(100 - percentage)).div(100); tokensToClaim = tokensRemaining.sub(tokensNotToClaim); tokensRemaining = tokensNotToClaim; holdings[beneficiary].tokensRemaining = tokensRemaining; } } function calculatePercentageToRelease(uint256 _startTime) internal view returns (uint256 percentage) { // how many 30 day periods have passed uint periodsPassed = ((now.sub(_startTime.add(MinimumHoldingPeriod))).div(Interval)).add(1); percentage = periodsPassed.mul(25); // 25% to be released every 30 days } function initializeVesting( address _beneficiary, uint256 _tokens, uint256 _startTime, uint8 user ) external onlyOwner { User _user; if (user == uint8(User.Seed)) { _user = User.Seed; totalSeedTokensCommitted = totalSeedTokensCommitted.add(_tokens); require(totalSeedTokensCommitted <= SeedCap); } else if (user == uint8(User.Private)) { _user = User.Private; totalPrivateTokensCommitted = totalPrivateTokensCommitted.add(_tokens); require(totalPrivateTokensCommitted <= PrivateCap); } else if (user == uint8(User.Advisor)) { _user = User.Advisor; totalAdvisorTokensCommitted = totalAdvisorTokensCommitted.add(_tokens); require(totalAdvisorTokensCommitted <= AdvisorCap); } else { revert( "incorrect category, not eligible for vesting" ); } if (holdings[_beneficiary].tokensCommitted != 0) { holdings[_beneficiary].tokensCommitted = holdings[_beneficiary].tokensCommitted.add(_tokens); holdings[_beneficiary].tokensRemaining = holdings[_beneficiary].tokensRemaining.add(_tokens); emit VestingUpdated( _beneficiary, holdings[_beneficiary].tokensRemaining, holdings[_beneficiary].startTime, holdings[_beneficiary].user ); } else { holdings[_beneficiary] = Holding( _tokens, _tokens, _startTime, _user ); emit VestingInitialized(_beneficiary, _tokens, _startTime, _user); } } } contract TeamVesting is Ownable { using SafeMath for uint256; mapping (address => Holding) public holdings; uint256 constant public MinimumHoldingPeriod = 180 days; uint256 constant public Interval = 180 days; uint256 constant public MaximumHoldingPeriod = 720 days; uint256 constant public TeamCap = 12200000 ether; // 12.2 million tokens uint256 public totalTeamTokensCommitted; struct Holding { uint256 tokensCommitted; uint256 tokensRemaining; uint256 startTime; } event TeamVestingInitialized(address _to, uint256 _tokens, uint256 _startTime); event TeamVestingUpdated(address _to, uint256 _totalTokens, uint256 _startTime); function claimTokens(address beneficiary) external onlyOwner returns (uint256 tokensToClaim) { uint256 tokensRemaining = holdings[beneficiary].tokensRemaining; uint256 startTime = holdings[beneficiary].startTime; require(tokensRemaining > 0, "All tokens claimed"); require(now.sub(startTime) > MinimumHoldingPeriod, "Claiming period not started yet"); if (now.sub(startTime) >= MaximumHoldingPeriod) { tokensToClaim = tokensRemaining; delete holdings[beneficiary]; } else { uint256 percentage = calculatePercentageToRelease(startTime); uint256 tokensNotToClaim = (holdings[beneficiary].tokensCommitted.mul(100 - percentage)).div(100); tokensToClaim = tokensRemaining.sub(tokensNotToClaim); tokensRemaining = tokensNotToClaim; holdings[beneficiary].tokensRemaining = tokensRemaining; } } function calculatePercentageToRelease(uint256 _startTime) internal view returns (uint256 percentage) { // how many 180 day periods have passed uint periodsPassed = ((now.sub(_startTime)).div(Interval)); percentage = periodsPassed.mul(25); // 25% to be released every 180 days } function initializeVesting( address _beneficiary, uint256 _tokens, uint256 _startTime ) external onlyOwner { totalTeamTokensCommitted = totalTeamTokensCommitted.add(_tokens); require(totalTeamTokensCommitted <= TeamCap); if (holdings[_beneficiary].tokensCommitted != 0) { holdings[_beneficiary].tokensCommitted = holdings[_beneficiary].tokensCommitted.add(_tokens); holdings[_beneficiary].tokensRemaining = holdings[_beneficiary].tokensRemaining.add(_tokens); emit TeamVestingUpdated( _beneficiary, holdings[_beneficiary].tokensRemaining, holdings[_beneficiary].startTime ); } else { holdings[_beneficiary] = Holding( _tokens, _tokens, _startTime ); emit TeamVestingInitialized(_beneficiary, _tokens, _startTime); } } } interface TokenInterface { function totalSupply() external view returns (uint256); function balanceOf(address _owner) external view returns (uint256 balance); function transfer(address _to, uint256 _value) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } contract Vesting is Ownable { using SafeMath for uint256; enum VestingUser { Public, Seed, Private, Advisor, Team, Community, Ecosystem } TokenInterface public token; CommunityVesting public communityVesting; TeamVesting public teamVesting; EcosystemVesting public ecosystemVesting; SeedPrivateAdvisorVesting public seedPrivateAdvisorVesting; mapping (address => VestingUser) public userCategory; uint256 public totalAllocated; event TokensReleased(address _to, uint256 _tokensReleased, VestingUser user); constructor(address _token) public { //require(_token != 0x0, "Invalid address"); token = TokenInterface(_token); communityVesting = new CommunityVesting(); teamVesting = new TeamVesting(); ecosystemVesting = new EcosystemVesting(); seedPrivateAdvisorVesting = new SeedPrivateAdvisorVesting(); } function claimTokens() external { uint8 category = uint8(userCategory[msg.sender]); uint256 tokensToClaim; if (category == 1 || category == 2 || category == 3) { tokensToClaim = seedPrivateAdvisorVesting.claimTokens(msg.sender); } else if (category == 4) { tokensToClaim = teamVesting.claimTokens(msg.sender); } else if (category == 5) { tokensToClaim = communityVesting.claimTokens(msg.sender); } else if (category == 6){ tokensToClaim = ecosystemVesting.claimTokens(msg.sender); } else { revert( "incorrect category, maybe unknown user" ); } totalAllocated = totalAllocated.sub(tokensToClaim); require(token.transfer(msg.sender, tokensToClaim), "Insufficient balance in vesting contract"); emit TokensReleased(msg.sender, tokensToClaim, userCategory[msg.sender]); } function initializeVesting( address _beneficiary, uint256 _tokens, uint256 _startTime, VestingUser user ) external onlyOwner { uint8 category = uint8(user); require(category != 0, "Not eligible for vesting"); require( uint8(userCategory[_beneficiary]) == 0 || userCategory[_beneficiary] == user, "cannot change user category" ); userCategory[_beneficiary] = user; totalAllocated = totalAllocated.add(_tokens); if (category == 1 || category == 2 || category == 3) { seedPrivateAdvisorVesting.initializeVesting(_beneficiary, _tokens, _startTime, category); } else if (category == 4) { teamVesting.initializeVesting(_beneficiary, _tokens, _startTime); } else if (category == 5) { communityVesting.initializeVesting(_beneficiary, _tokens, _startTime); } else if (category == 6){ ecosystemVesting.initializeVesting(_beneficiary, _tokens, _startTime); } else { revert( "incorrect category, not eligible for vesting" ); } } function claimUnallocated( address _sendTo) external onlyOwner{ uint256 allTokens = token.balanceOf(address(this)); token.transfer(_sendTo, allTokens); } }