├── README.md └── contracts ├── KYCregistry.sol ├── StakingPool.sol ├── TierSystemManual.sol └── Tokensale.sol /README.md: -------------------------------------------------------------------------------- 1 | # WePad Smart Contracts 2 | 3 | ## Smart contracts 4 | 5 | * StakingPool: Staking rewards, lockup period, information provider for Tiers ([Audit report](https://solidity.finance/audits/WePadStaking/)) 6 | * KYCRegistry: Registry of users who passed KYC and whitelisted for tokensale ([Audit report](https://solidity.finance/audits/WePad/)) 7 | * TierSystemManual: Allocation Provider for the Tokensale Contract ([Audit report](https://solidity.finance/audits/WePad/)) 8 | * Tokensale: Tokensale Contract with vesting period ([Audit report](https://solidity.finance/audits/WePad/)) 9 | 10 | ## Audits 11 | 12 | **Audits by Solidity.Finance**: 13 | 14 | * https://solidity.finance/audits/WePadStaking/ 15 | * https://solidity.finance/audits/WePad/ 16 | -------------------------------------------------------------------------------- /contracts/KYCregistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.7; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | /* 8 | KYC registry smart contract is managed by WePad adminitratory 9 | 10 | Functionlity: 11 | * Administrator can add reviewed allowed address into the registry 12 | * Registry could be used in Tokensale to verify allowed address 13 | * Adminitrator could remove allowed address from the registry 14 | * Administrator could transfer ownership to another administrator or smart contact 15 | * Registry Smart Contract should be deployed once and then used by other smart contracts 16 | */ 17 | 18 | /* 19 | 20 | Usage example 21 | 22 | abstract contract IKYCRegistry { 23 | mapping(address => bool) public allowed; 24 | } 25 | 26 | 27 | contract TokensaleContract { 28 | 29 | IKYCRegistry kycRegistry; 30 | 31 | constructor(IKYCRegistry _kycRegistery) { 32 | kycRegistry = _kycRegistery; 33 | 34 | require(kycRegistry.allowed(address(0x0)) == false, "should be always false"); 35 | } 36 | 37 | } 38 | 39 | */ 40 | 41 | contract KYCRegistry is Ownable { 42 | 43 | /* 44 | Event can be used by the https://github.com/graphprotocol/graph-node or getPastLogs(fromBlock, toBlock) 45 | */ 46 | event Added(address user, bool allowed); 47 | 48 | /* 49 | Registry itself 50 | */ 51 | mapping(address => bool) public allowed; 52 | 53 | /* 54 | Aministrator function to add KYC allowance 55 | */ 56 | function setAcceptStatus(address token, bool status) private { 57 | require(token != address(0x0)); 58 | allowed[token] = status; 59 | emit Added(token, status); 60 | } 61 | 62 | struct Allowance { 63 | address user; 64 | bool status; 65 | } 66 | 67 | /* 68 | Aministrator function to add multiple KYC allowances 69 | */ 70 | function setAcceptStatuses(Allowance[] calldata allowances) public onlyOwner { 71 | for (uint i =0; i < allowances.length; i++) { 72 | setAcceptStatus(allowances[i].user, allowances[i].status); 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /contracts/StakingPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.7; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 9 | import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 10 | 11 | 12 | abstract contract IBEP20 is IERC20 { 13 | function decimals() public virtual returns (uint); 14 | } 15 | 16 | contract StakingPool is Ownable, ReentrancyGuard { 17 | using SafeERC20 for IBEP20; 18 | using EnumerableSet for EnumerableSet.AddressSet; 19 | 20 | 21 | // Whether a limit is set for users 22 | bool public hasUserLimit; 23 | 24 | // Accrued token per share 25 | uint256 public accTokenPerShare; 26 | 27 | // The block number when mining ends. 28 | uint256 public bonusEndTimestamp; 29 | 30 | // The block number when mining starts. 31 | uint256 public startTimestamp; 32 | 33 | // The block number of the last pool update 34 | uint256 public lastRewardTimestamp; 35 | 36 | // The pool limit (0 if none) 37 | uint256 public poolLimitPerUser; 38 | 39 | // tokens created per block. 40 | uint256 public rewardPerSecond; 41 | 42 | // The precision factor 43 | uint256 public PRECISION_FACTOR; 44 | 45 | // The reward token 46 | IBEP20 public rewardToken; 47 | 48 | // The staked token 49 | IBEP20 public stakedToken; 50 | 51 | // Info of each user that stakes tokens (stakedToken) 52 | mapping(address => UserInfo) public userInfo; 53 | 54 | // EnumerableSet to extract or calculate length of all stakers from the contract 55 | EnumerableSet.AddressSet private allStakers; 56 | 57 | struct UserInfo { 58 | uint256 amount; // How many staked tokens the user has provided 59 | uint256 rewardDebt; // Reward debt 60 | uint256 stakeTime; 61 | } 62 | 63 | //event AdminTokenRecovery(address tokenRecovered, uint256 amount); 64 | event Deposit(address indexed user, uint256 amount); 65 | //event EmergencyWithdraw(address indexed user, uint256 amount); 66 | event NewStartAndEndBlocks(uint256 startTimestamp, uint256 endBlock); 67 | event NewrewardPerSecond(uint256 rewardPerSecond); 68 | event NewPoolLimit(uint256 poolLimitPerUser); 69 | event RewardsStop(uint256 blockNumber); 70 | event WithdrawRequest(address indexed user, uint256 amount); 71 | event StakeRewards(address user, uint amount); 72 | 73 | 74 | // get count of stakers 75 | function countOfStakers() public view returns (uint) { 76 | return allStakers.length(); 77 | } 78 | 79 | // get staker address by index 80 | function stakerAt(uint index) public view returns (address) { 81 | return allStakers.at(index); 82 | } 83 | 84 | /* 85 | * @notice return the information about staking amount and period 86 | * @param _user: Staking address 87 | */ 88 | function getUserStakingInfo(address _user) public view returns (uint[2] memory) { 89 | uint[2] memory info; 90 | info[0] = userInfo[_user].amount; 91 | info[1] = userInfo[_user].stakeTime; 92 | return info; 93 | } 94 | 95 | /* 96 | * @notice Initialize the contract 97 | * @param _stakedToken: staked token address 98 | * @param _rewardToken: equal to _stakedToken 99 | * @param _rewardPerSecond: reward per block (in rewardToken) 100 | * @param _startTimestamp: start block 101 | * @param _bonusEndTimestamp: end block 102 | * @param _poolLimitPerUser: pool limit per user in stakedToken (if any, else 0) 103 | * @param _admin: admin address with ownership 104 | */ 105 | constructor( 106 | IBEP20 _stakedToken, 107 | uint256 _rewardPerSecond, 108 | uint256 _startTimestamp, 109 | uint256 _bonusEndTimestamp, 110 | uint256 _poolLimitPerUser 111 | ) { 112 | 113 | stakedToken = _stakedToken; 114 | rewardToken = _stakedToken; 115 | // TO protect the user 116 | require(_rewardPerSecond >= 0.001 ether, "CANNOT_BE_LESS"); 117 | rewardPerSecond = _rewardPerSecond; 118 | startTimestamp = _startTimestamp; 119 | bonusEndTimestamp = _bonusEndTimestamp; 120 | 121 | if (_poolLimitPerUser > 0) { 122 | hasUserLimit = true; 123 | poolLimitPerUser = _poolLimitPerUser; 124 | } 125 | 126 | uint256 decimalsRewardToken = uint256(rewardToken.decimals()); 127 | require(decimalsRewardToken < 30, "Must be inferior to 30"); 128 | 129 | PRECISION_FACTOR = uint256(10**( uint256(30) - decimalsRewardToken )); 130 | 131 | // Set the lastRewardTimestamp as the startTimestamp 132 | lastRewardTimestamp = startTimestamp; 133 | 134 | // Transfer ownership to the admin address who becomes owner of the contract 135 | } 136 | 137 | /* 138 | * @notice Deposit staked tokens and collect reward tokens (if any) 139 | * @param _amount: amount to withdraw (in rewardToken) 140 | */ 141 | function deposit(uint256 _amount) external nonReentrant { 142 | UserInfo storage user = userInfo[msg.sender]; 143 | 144 | if (hasUserLimit) { 145 | require(_amount + user.amount <= poolLimitPerUser, "User amount above limit"); 146 | } 147 | 148 | _updatePool(); 149 | 150 | if (user.amount == 0) { 151 | allStakers.add(msg.sender); 152 | 153 | } 154 | 155 | /* 156 | if (user.amount > 0) { 157 | uint256 pending = getWithdrawableRewardAmount(user); 158 | if (pending > 0) { 159 | rewardToken.safeTransfer(address(msg.sender), pending); 160 | user.rewardDebt += pending; 161 | } 162 | } 163 | */ 164 | 165 | if (_amount > 0) { 166 | user.amount = user.amount + _amount; 167 | stakedTokenSupply = stakedTokenSupply + _amount; 168 | 169 | stakedToken.safeTransferFrom(address(msg.sender), address(this), _amount); 170 | } 171 | 172 | 173 | user.stakeTime = block.timestamp; 174 | 175 | emit Deposit(msg.sender, _amount); 176 | } 177 | 178 | uint constant withdrawStakingTime = 604800; // week in seconds 179 | 180 | // calculate reward and check ability to withdraw 181 | function getWithdrawableRewardAmount(UserInfo memory user) private view returns (uint) { 182 | uint256 pending = ((user.amount * accTokenPerShare) / PRECISION_FACTOR) - user.rewardDebt; 183 | 184 | uint tokenBalance = IERC20(rewardToken).balanceOf(address(this)); 185 | uint rest = tokenBalance - stakedTokenSupply; 186 | // as far rest can never been negative (<) is not necessary but logically wise it should be explicetelly here 187 | if (rest <= 0) 188 | return 0; 189 | 190 | // adjust amount in case when rewards are not deposited 191 | pending = rest - pending > 0 ? pending : rest; 192 | 193 | return pending; 194 | 195 | } 196 | 197 | // calculate the APR for the front-end in percent (for the whole period) 198 | function getProfitabilityInPercent() public view returns (uint) { 199 | return (rewardPerSecond * (bonusEndTimestamp - startTimestamp) ) * 100 / stakedTokenSupply; 200 | } 201 | 202 | /* 203 | * @notice Withdraw staked tokens and collect reward tokens 204 | * @param _amount: amount to withdraw (in rewardToken) 205 | */ 206 | function withdraw(uint256 _amount) external nonReentrant { 207 | UserInfo storage user = userInfo[msg.sender]; 208 | require(user.amount >= _amount, "Amount to withdraw too high"); 209 | require(user.stakeTime + withdrawStakingTime <= block.timestamp, "TIME"); 210 | 211 | _updatePool(); 212 | 213 | uint256 pending = getWithdrawableRewardAmount(user); 214 | 215 | if (_amount > 0) { 216 | user.amount = user.amount - _amount; 217 | 218 | stakedTokenSupply -= _amount; 219 | 220 | stakedToken.safeTransfer(address(msg.sender), _amount); 221 | 222 | 223 | if (user.amount == 0) 224 | allStakers.remove(msg.sender); 225 | 226 | } 227 | 228 | if (pending > 0) { 229 | rewardToken.safeTransfer(address(msg.sender), pending); 230 | user.rewardDebt += pending; 231 | } 232 | 233 | 234 | 235 | emit WithdrawRequest(msg.sender, _amount); 236 | } 237 | 238 | /* 239 | Let user staker rewards 240 | */ 241 | function stakeRewards() public { 242 | 243 | UserInfo storage user = userInfo[msg.sender]; 244 | 245 | //require(user.stakeTime + withdrawStakingTime <= block.timestamp, "TIME"); 246 | 247 | _updatePool(); 248 | 249 | uint256 pending = getWithdrawableRewardAmount(user); 250 | 251 | require(pending > 0, "REWARD"); 252 | 253 | if (user.amount == 0) { 254 | allStakers.add(msg.sender); 255 | } 256 | 257 | user.amount += pending; 258 | stakedTokenSupply += pending; 259 | 260 | 261 | user.rewardDebt += pending; 262 | 263 | emit StakeRewards(msg.sender, pending); 264 | 265 | } 266 | 267 | /* 268 | * @notice Withdraw staked tokens without caring about rewards rewards 269 | * @dev Needs to be for emergency. 270 | */ 271 | /* 272 | function emergencyWithdraw() external nonReentrant { 273 | UserInfo storage user = userInfo[msg.sender]; 274 | uint256 amountToTransfer = user.amount; 275 | user.amount = 0; 276 | user.rewardDebt = 0; 277 | 278 | if (amountToTransfer > 0) { 279 | stakedToken.safeTransfer(address(msg.sender), amountToTransfer); 280 | stakedTokenSupply = stakedTokenSupply.sub(amountToTransfer); 281 | } 282 | 283 | emit EmergencyWithdraw(msg.sender, user.amount); 284 | } 285 | */ 286 | 287 | /* 288 | * @notice Stop rewards 289 | * @dev Only callable by owner. Needs to be for emergency. 290 | */ 291 | //function emergencyRewardWithdraw(uint256 _amount) external onlyOwner { 292 | // rewardToken.safeTransfer(address(msg.sender), _amount); 293 | //} 294 | 295 | /** 296 | * @notice It allows the admin to recover wrong tokens sent to the contract 297 | * @dev This function is only callable by admin. 298 | 299 | function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external onlyOwner { 300 | require(_tokenAddress != address(stakedToken), "Cannot be staked token"); 301 | require(_tokenAddress != address(rewardToken), "Cannot be reward token"); 302 | 303 | IBEP20(_tokenAddress).safeTransfer(address(msg.sender), _tokenAmount); 304 | 305 | emit AdminTokenRecovery(_tokenAddress, _tokenAmount); 306 | } 307 | */ 308 | 309 | /* 310 | * @notice Stop rewards 311 | * @dev Only callable by owner 312 | */ 313 | //function stopReward() external onlyOwner { 314 | // bonusEndTimestamp = block.timestamp; 315 | //} 316 | 317 | /* 318 | * @notice Update pool limit per user 319 | * @dev Only callable by owner. 320 | * @param _hasUserLimit: whether the limit remains forced 321 | * @param _poolLimitPerUser: new pool limit per user 322 | */ 323 | function updatePoolLimitPerUser(bool _hasUserLimit, uint256 _poolLimitPerUser) external onlyOwner { 324 | require(hasUserLimit, "Must be set"); 325 | if (_hasUserLimit) { 326 | require(_poolLimitPerUser > poolLimitPerUser, "New limit must be higher"); 327 | poolLimitPerUser = _poolLimitPerUser; 328 | } else { 329 | hasUserLimit = _hasUserLimit; 330 | poolLimitPerUser = 0; 331 | } 332 | emit NewPoolLimit(poolLimitPerUser); 333 | } 334 | 335 | // Just for admin to undrstand how much he needs to send tokens to pay rewards. He should do it once when he created a contract 336 | function shouldDepositAdmin() public view returns (uint) { 337 | return rewardPerSecond * (bonusEndTimestamp - startTimestamp); 338 | } 339 | 340 | // Should be called before extendStaking to understand how much to add coins to extend the staking duration 341 | function shouldDepositAdminToExtendStaking(uint duration) public view returns (uint) { 342 | return rewardPerSecond * duration; 343 | } 344 | 345 | // Extend the staking duration 346 | function extendStaking(uint duration) public onlyOwner { 347 | bonusEndTimestamp += duration; 348 | } 349 | 350 | /* 351 | * @notice Update reward per block 352 | * @dev Only callable by owner. 353 | * @param _rewardPerSecond: the reward per block 354 | */ 355 | function updateRewardPerSecond(uint256 _rewardPerSecond) external onlyOwner { 356 | //require(block.timestamp < startTimestamp, "Pool has started"); 357 | // TO protect the user 358 | require(_rewardPerSecond >= 0.001 ether, "CANNOT_BE_LESS"); 359 | rewardPerSecond = _rewardPerSecond; 360 | emit NewrewardPerSecond(_rewardPerSecond); 361 | } 362 | 363 | /** 364 | * @notice It allows the admin to update start and end blocks 365 | * @dev This function is only callable by owner. 366 | function updateStartAndEndBlocks(uint256 _startTimestamp, uint256 _bonusEndTimestamp) external onlyOwner { 367 | require(block.timestamp < startTimestamp, "Pool has started"); 368 | require(_startTimestamp < _bonusEndTimestamp, "New startTimestamp must be lower than new endBlock"); 369 | require(block.timestamp < _startTimestamp, "New startTimestamp must be higher than current block"); 370 | 371 | startTimestamp = _startTimestamp; 372 | bonusEndTimestamp = _bonusEndTimestamp; 373 | 374 | // Set the lastRewardTimestamp as the startTimestamp 375 | lastRewardTimestamp = startTimestamp; 376 | 377 | emit NewStartAndEndBlocks(_startTimestamp, _bonusEndTimestamp); 378 | }*/ 379 | 380 | uint public stakedTokenSupply; 381 | 382 | /* 383 | * @notice View function to see pending reward on frontend. 384 | * @param _user: user address 385 | * @return Pending reward for a given user 386 | */ 387 | function pendingReward(address _user) external view returns (uint256) { 388 | UserInfo storage user = userInfo[_user]; 389 | //uint256 stakedTokenSupply = stakedToken.balanceOf(address(this)); 390 | if (block.timestamp > lastRewardTimestamp && stakedTokenSupply != 0) { 391 | uint256 multiplier = _getMultiplier(lastRewardTimestamp, block.timestamp); 392 | uint256 reward = multiplier * rewardPerSecond; 393 | uint256 adjustedTokenPerShare = 394 | accTokenPerShare + (reward * PRECISION_FACTOR / stakedTokenSupply); 395 | return ((user.amount * adjustedTokenPerShare) / PRECISION_FACTOR) - (user.rewardDebt); 396 | } else { 397 | return ((user.amount * accTokenPerShare) / PRECISION_FACTOR) - (user.rewardDebt); 398 | } 399 | } 400 | 401 | /* 402 | * @notice Update reward variables of the given pool to be up-to-date. 403 | */ 404 | function _updatePool() internal { 405 | if (block.timestamp <= lastRewardTimestamp) { 406 | return; 407 | } 408 | 409 | if (stakedTokenSupply == 0) { 410 | lastRewardTimestamp = block.timestamp; 411 | return; 412 | } 413 | 414 | uint256 multiplier = _getMultiplier(lastRewardTimestamp, block.timestamp); 415 | uint256 reward = multiplier * rewardPerSecond; 416 | accTokenPerShare = accTokenPerShare + (reward * PRECISION_FACTOR / stakedTokenSupply); 417 | lastRewardTimestamp = block.timestamp; 418 | } 419 | 420 | /* 421 | * @notice Return reward multiplier over the given _from to _to block. 422 | * @param _from: block to start 423 | * @param _to: block to finish 424 | */ 425 | function _getMultiplier(uint256 _from, uint256 _to) internal view returns (uint256) { 426 | if (_to <= bonusEndTimestamp) { 427 | return _to - _from; 428 | } else if (_from >= bonusEndTimestamp) { 429 | return 0; 430 | } else { 431 | return bonusEndTimestamp -_from; 432 | } 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /contracts/TierSystemManual.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | 5 | pragma solidity ^0.8.7; 6 | 7 | 8 | /** 9 | * @title Tier System 10 | * @dev Defined list of contributors who can participate in UPDAO projects 11 | */ 12 | contract TierSystemManual is Ownable { 13 | 14 | mapping(address => mapping(address => uint) ) public allocations; 15 | 16 | /* 17 | Setup Allocaitons by admin 18 | */ 19 | function setupAllocations(address tokensale, address[] calldata users, uint[] calldata amounts) public onlyOwner { 20 | 21 | require(users.length == amounts.length, "MATCH"); 22 | 23 | for (uint i = 0; i < users.length; i++) { 24 | allocations[tokensale][users[i]] = amounts[i]; 25 | } 26 | } 27 | 28 | /* 29 | Get allowed amount to contribute (used by tokensale contract) 30 | */ 31 | function getAllocation(address tokensale, address _contributor) public view returns (uint) { 32 | return allocations[tokensale][_contributor]; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /contracts/Tokensale.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.7; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/security/Pausable.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 9 | 10 | abstract contract TierSystem { 11 | function getAllocation(address tokensale, address _contributor) public virtual view returns (uint); 12 | } 13 | 14 | abstract contract IKYCRegistry { 15 | mapping(address => bool) public allowed; 16 | } 17 | 18 | contract Tokensale is Ownable, Pausable { 19 | 20 | using SafeERC20 for IERC20; 21 | 22 | address public tokenAddress; 23 | 24 | uint256 public totalTokensSold; 25 | uint256 public totalStableReceived; 26 | 27 | uint256 public maxCapInStable; 28 | 29 | uint256 public timeLockSeconds; 30 | 31 | uint256 public startTime; 32 | uint256 public endTime; 33 | 34 | address public withdrawAddress; 35 | 36 | IKYCRegistry public kycRegistry; 37 | TierSystem public tierSystem; 38 | IERC20 public stableCoin; 39 | IERC20 public token; 40 | uint public tokensForSale; 41 | bool depositedTokens; 42 | 43 | struct Contribution { 44 | uint256 amount; 45 | uint256 tokens; 46 | uint256 releasedTokens; 47 | } 48 | 49 | mapping (address => Contribution) contributions; 50 | 51 | constructor( 52 | uint _startTime, 53 | uint _endTime, 54 | uint _maxCapInStable, 55 | uint _tokensForSale, 56 | uint _timeLockSeconds, 57 | uint _vestingDurationSeconds, 58 | uint _vestingWidthdrawInterval, 59 | IERC20 _token, 60 | address _withdrawAddress, 61 | TierSystem _tierSystem, 62 | IKYCRegistry _kycRegistry 63 | ) { 64 | 65 | 66 | stableCoin = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56); 67 | maxCapInStable = _maxCapInStable; 68 | vestingWidthdrawInterval = _vestingWidthdrawInterval; 69 | withdrawAddress = _withdrawAddress; 70 | startTime = _startTime; 71 | tokensForSale = _tokensForSale; 72 | endTime = _endTime; 73 | kycRegistry = _kycRegistry; 74 | tierSystem = _tierSystem; 75 | token = _token; 76 | timeLockSeconds = _timeLockSeconds; 77 | vestingDurationSeconds = _vestingDurationSeconds; 78 | } 79 | 80 | 81 | // how many tokens I can buy with 1 BUSD 82 | function getTokenPrice() view public returns (uint) { 83 | return tokensForSale / maxCapInStable ; 84 | } 85 | 86 | // get allocation in stables 87 | function getAllocation(address user) public view returns (uint) { 88 | return tierSystem.getAllocation(address(this), user); 89 | } 90 | 91 | 92 | // get available amount in stables 93 | function getAvailableParticipateAmount(address user) public view returns (uint) { 94 | 95 | Contribution memory contrib = contributions[user]; 96 | return getAllocation(user) - contrib.amount + bonusRoundAllocationInStables; 97 | } 98 | 99 | // team should deposit tokens for sale 100 | function depositTokens() public { 101 | require(depositedTokens == false, "DONE"); 102 | token.transferFrom(msg.sender, address(this), tokensForSale); 103 | depositedTokens = true; 104 | } 105 | 106 | // if somebody sent wrong tokens - help him to recover 107 | function emergencyWithdrawTokens(address _token, uint _amount) public onlyOwner { 108 | IERC20(_token).safeTransfer(msg.sender, _amount); 109 | } 110 | 111 | // is bonus round started 112 | bool public bonusRound; 113 | uint public bonusRoundAllocationInStables; 114 | 115 | // start bonus round to sell rest of the tokens 116 | function startBonusRound(uint _bonusRoundAllocationInStables) public onlyOwner { 117 | 118 | require(endTime < block.timestamp); 119 | require(totalTokensSold < tokensForSale, "COND2"); 120 | require(vestingStart == 0, "COND3"); 121 | require(bonusRound == false, "COND4"); 122 | 123 | bonusRoundAllocationInStables = _bonusRoundAllocationInStables; 124 | bonusRound = true; 125 | } 126 | 127 | // emergency pause 128 | function pause() public onlyOwner { 129 | _pause(); 130 | } 131 | 132 | // unpause the tokensale after emergency is over 133 | function unpause() public onlyOwner { 134 | _unpause(); 135 | } 136 | 137 | event Participate(address user, uint amount); 138 | 139 | /** 140 | * Main function to participate in IDO based on user's tier lavel (won lottery) and passed KYC. 141 | _amount in stables 142 | */ 143 | function participate(uint _amountStables) whenNotPaused public { 144 | 145 | require(startTime <= block.timestamp, "START"); 146 | 147 | require(block.timestamp <= endTime || bonusRound, "FINISH"); 148 | 149 | require(_amountStables > 0, "AMOUNT"); 150 | 151 | require(kycRegistry.allowed(msg.sender), "KYC"); 152 | 153 | // get amount in stables 154 | uint availableToBuyInStables = getAvailableParticipateAmount(msg.sender); 155 | 156 | require(availableToBuyInStables > 0, "PA1"); 157 | 158 | require(_amountStables <= availableToBuyInStables, "PA2"); 159 | 160 | // should be allowed by user 161 | stableCoin.safeTransferFrom(msg.sender, address(this), _amountStables); 162 | 163 | uint tokensToBuy = _amountStables * getTokenPrice(); 164 | 165 | Contribution storage contrib = contributions[msg.sender]; 166 | 167 | contrib.amount += _amountStables; 168 | 169 | contrib.tokens += tokensToBuy; 170 | 171 | totalStableReceived += _amountStables; 172 | 173 | totalTokensSold += tokensToBuy; 174 | 175 | require(totalTokensSold <= tokensForSale, "OVERFLOW"); 176 | 177 | emit Participate(msg.sender, _amountStables); 178 | 179 | if (totalTokensSold == tokensForSale) { 180 | _finish(); 181 | } 182 | 183 | 184 | } 185 | 186 | /** 187 | * The timestamp when user could withdraw the first token according to vesting schedule 188 | */ 189 | uint public vestingStart; 190 | 191 | /* 192 | * Vesting period 193 | */ 194 | uint public vestingDurationSeconds; 195 | 196 | /* 197 | * Withdraw Interval in seconds 198 | */ 199 | uint public vestingWidthdrawInterval; 200 | 201 | /** 202 | * @dev Return the status for the IDO for the front-end 203 | */ 204 | function getStatus(uint currentTimestamp) public view returns (string memory) { 205 | if (vestingStart > 0) { 206 | return "vesting"; 207 | } 208 | 209 | if (!bonusRound && currentTimestamp > endTime && totalTokensSold < tokensForSale) { 210 | return "need bonusRound"; 211 | } 212 | 213 | if (startTime > currentTimestamp) { 214 | return "waiting"; 215 | } 216 | 217 | if (bonusRound) { 218 | return "bonus"; 219 | } 220 | 221 | return "round 1"; 222 | } 223 | 224 | /** 225 | * @dev Internal Function to start vesting with initial lockup and vesting duration for all participants 226 | Inspired by https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/finance/VestingWallet.sol for multiple wallets 227 | */ 228 | function _startVesting() internal { 229 | 230 | require(vestingStart == 0, "START"); 231 | vestingStart = block.timestamp + timeLockSeconds; 232 | } 233 | 234 | /** 235 | * @dev Internal Function to finish IDO 236 | */ 237 | function _finish() internal { 238 | _startVesting(); 239 | 240 | } 241 | 242 | /** 243 | * @dev Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for 244 | * an asset given its total historical allocation. 245 | */ 246 | function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) { 247 | 248 | 249 | if (timestamp < vestingStart) { 250 | return 0; 251 | } else if (timestamp > vestingStart + vestingDurationSeconds) { 252 | return totalAllocation; 253 | } else { 254 | 255 | uint numberOfPeriods = vestingDurationSeconds / vestingWidthdrawInterval; 256 | uint allocationPart = totalAllocation / numberOfPeriods; 257 | 258 | uint distributed = (totalAllocation * (timestamp - vestingStart)) / vestingDurationSeconds; 259 | 260 | return distributed - (distributed % allocationPart); 261 | } 262 | 263 | } 264 | 265 | /** 266 | * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve. 267 | */ 268 | function vestedAmount(address user, uint64 timestamp) public view virtual returns (uint256) { 269 | 270 | Contribution memory contrib = contributions[user]; 271 | 272 | return _vestingSchedule(contrib.tokens, timestamp) ; 273 | } 274 | 275 | event Released(address user, uint tokens); 276 | 277 | /** 278 | * @dev User should call this function from the front-end to get vested tokens 279 | */ 280 | function release() public virtual { 281 | 282 | Contribution storage contrib = contributions[msg.sender]; 283 | 284 | uint256 releasable = vestedAmount(msg.sender, uint64(block.timestamp)) - contrib.releasedTokens; 285 | contrib.releasedTokens += releasable; 286 | 287 | emit Released(msg.sender, releasable); 288 | token.safeTransfer(msg.sender, releasable); 289 | } 290 | 291 | 292 | 293 | } 294 | --------------------------------------------------------------------------------