Created
October 18, 2021 08:17
-
-
Save vsmelov/6aa249a721e79e534c1a8a0b9eee91fd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.1; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
contract Vesting { | |
IERC20 public immutable token; | |
struct Lock { | |
address receiver; | |
uint256 amount; | |
uint256 startTimestamp; | |
uint256 endTimestamp; | |
uint256 withdrawn; | |
} | |
mapping(address => Lock) public receiverLock; | |
event OnLock(address indexed sender, address indexed receiver, uint256 amount, uint256 startTimestamp, uint256 endTimestamp); | |
event OnWithdraw(address indexed receiver, uint256 amount); | |
constructor(address _token) { | |
require(_token != address(0), "Vesting: Token must not be 0x0"); | |
token = IERC20(_token); | |
} | |
// @notice locks tokens. The only one lock for a receiver can exist. | |
// @param receiver address what tokens are vested to. | |
// @param amount of locked tokens. | |
// @param startTimestamp when vesting starts. Representing the UNIX timestamp and must not be earlier then now. | |
// @param endTimestamp when vesting ends. Representing the UNIX timestamp and must not be earlier then `startTimestamp`. | |
function lockTokens(address receiver, uint256 amount, uint256 startTimestamp, uint256 endTimestamp) external { | |
require(receiver != address(0), "Vesting: must not be 0x0"); | |
require(amount > 0, "Vesting: locked amount must be > 0"); | |
require(startTimestamp >= block.timestamp, "Vesting: startTimestamp must be later then now"); | |
require(startTimestamp < endTimestamp, "Vesting: endTimestamp must be later then startTimestamp"); | |
require(endTimestamp < 4000000000, "Vesting: Invalid unlock time, it must be unix time in seconds"); | |
require(receiverLock[receiver].amount == 0, "Vesting: lock for receiver already exists"); | |
receiverLock[receiver] = Lock(receiver, amount, startTimestamp, endTimestamp, 0); | |
require(token.transferFrom(msg.sender, address(this), amount), "Vesting: unable to transfer tokens to the contract's address"); | |
emit OnLock(msg.sender, receiver, amount, startTimestamp, endTimestamp); | |
} | |
// @notice withdraws tokens which were vested and weren't withdrawn before | |
function withdraw() external { | |
Lock storage lock = receiverLock[msg.sender]; | |
require(lock.startTimestamp != 0, "Vesting: Lock doesn't exist"); | |
require(block.timestamp > lock.startTimestamp, "Vesting: vesting hasn't been started yet"); | |
uint256 amountToWithdraw = _allowedToWithdraw(lock); | |
lock.withdrawn += amountToWithdraw; | |
require(token.transfer(msg.sender, amountToWithdraw), "Vesting: Transfer was fallen"); | |
emit OnWithdraw(msg.sender, amountToWithdraw); | |
} | |
function _allowedToWithdraw(Lock storage lock) private view returns (uint256) { | |
uint256 timePassed = block.timestamp - lock.startTimestamp; | |
uint256 timeVesting = lock.endTimestamp - lock.startTimestamp; | |
if (timePassed >= timeVesting) { | |
return lock.amount - lock.withdrawn; | |
} | |
return ((lock.amount * timePassed) / timeVesting) - lock.withdrawn; | |
} | |
// @notice calculates amount of tokens which a user is allowed to withdraw. | |
// @param receiver user's address. | |
// @return amount of tokens which `receiver` is allowed to withdraw. | |
function allowedToWithdraw(address receiver) external view returns (uint256) { | |
Lock storage lock = receiverLock[receiver]; | |
return _allowedToWithdraw(lock); | |
} | |
// @notice returns parameters of a user's lock | |
// @param receiver user's address. | |
// @return parameters of the `receiver`'s lock: amount, startTimestamp, end timeStamp | |
// and withdrawn(how many tokens were already withdrawn). | |
function lockOf(address receiver) external view returns (uint256, uint256, uint256, uint256) { | |
Lock storage lock = receiverLock[receiver]; | |
return (lock.amount, lock.startTimestamp, lock.endTimestamp, lock.withdrawn); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment