DCIPs/assets/eip-5725/contracts/reference/LinearVestingNFT.sol

120 lines
3.6 KiB
Solidity

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;
import "../ERC5725.sol";
contract LinearVestingNFT is ERC5725 {
using SafeERC20 for IERC20;
struct VestDetails {
IERC20 payoutToken; /// @dev payout token
uint256 payout; /// @dev payout token remaining to be paid
uint128 startTime; /// @dev when vesting starts
uint128 endTime; /// @dev when vesting end
uint128 cliff; /// @dev duration in seconds of the cliff in which tokens will be begin releasing
}
mapping(uint256 => VestDetails) public vestDetails; /// @dev maps the vesting data with tokenIds
/// @dev tracker of current NFT id
uint256 private _tokenIdTracker;
/**
* @dev See {IERC5725}.
*/
constructor(string memory name, string memory symbol) ERC721(name, symbol) {}
/**
* @notice Creates a new vesting NFT and mints it
* @dev Token amount should be approved to be transferred by this contract before executing create
* @param to The recipient of the NFT
* @param amount The total assets to be locked over time
* @param startTime When the vesting starts in epoch timestamp
* @param duration The vesting duration in seconds
* @param cliff The cliff duration in seconds
* @param token The ERC20 token to vest over time
*/
function create(
address to,
uint256 amount,
uint128 startTime,
uint128 duration,
uint128 cliff,
IERC20 token
) public virtual {
require(startTime >= block.timestamp, "startTime cannot be on the past");
require(to != address(0), "to cannot be address 0");
require(cliff <= duration, "duration needs to be more than cliff");
uint256 newTokenId = _tokenIdTracker;
vestDetails[newTokenId] = VestDetails({
payoutToken: token,
payout: amount,
startTime: startTime,
endTime: startTime + duration,
cliff: startTime + cliff
});
_tokenIdTracker++;
_mint(to, newTokenId);
IERC20(payoutToken(newTokenId)).safeTransferFrom(msg.sender, address(this), amount);
}
/**
* @dev See {IERC5725}.
*/
function vestedPayoutAtTime(uint256 tokenId, uint256 timestamp)
public
view
override(ERC5725)
validToken(tokenId)
returns (uint256 payout)
{
if (timestamp < _cliff(tokenId)) {
return 0;
}
if (timestamp > _endTime(tokenId)) {
return _payout(tokenId);
}
return (_payout(tokenId) * (timestamp - _startTime(tokenId))) / (_endTime(tokenId) - _startTime(tokenId));
}
/**
* @dev See {ERC5725}.
*/
function _payoutToken(uint256 tokenId) internal view override returns (address) {
return address(vestDetails[tokenId].payoutToken);
}
/**
* @dev See {ERC5725}.
*/
function _payout(uint256 tokenId) internal view override returns (uint256) {
return vestDetails[tokenId].payout;
}
/**
* @dev See {ERC5725}.
*/
function _startTime(uint256 tokenId) internal view override returns (uint256) {
return vestDetails[tokenId].startTime;
}
/**
* @dev See {ERC5725}.
*/
function _endTime(uint256 tokenId) internal view override returns (uint256) {
return vestDetails[tokenId].endTime;
}
/**
* @dev Internal function to get the cliff time of a given linear vesting NFT
*
* @param tokenId to check
* @return uint256 the cliff time in seconds
*/
function _cliff(uint256 tokenId) internal view returns (uint256) {
return vestDetails[tokenId].cliff;
}
}