forked from DecentralizedClimateFoundation/DCIPs
100 lines
2.9 KiB
Solidity
100 lines
2.9 KiB
Solidity
|
// SPDX-License-Identifier: CC0-1.0
|
||
|
pragma solidity ^0.8.17;
|
||
|
|
||
|
import "../ERC5725.sol";
|
||
|
|
||
|
contract VestingNFT 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
|
||
|
}
|
||
|
mapping(uint256 => VestDetails) public vestDetails; /// @dev maps the vesting data with tokenIds
|
||
|
|
||
|
/// @dev tracker of current NFT id
|
||
|
uint256 private _tokenIdTracker;
|
||
|
|
||
|
/**
|
||
|
* @dev Initializes the contract by setting a `name` and a `symbol` to the token.
|
||
|
*/
|
||
|
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 releaseTimestamp When the full amount of tokens get released
|
||
|
* @param token The ERC20 token to vest over time
|
||
|
*/
|
||
|
function create(
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
uint128 releaseTimestamp,
|
||
|
IERC20 token
|
||
|
) public virtual {
|
||
|
require(to != address(0), "to cannot be address 0");
|
||
|
require(releaseTimestamp > block.timestamp, "release must be in future");
|
||
|
|
||
|
uint256 newTokenId = _tokenIdTracker;
|
||
|
|
||
|
vestDetails[newTokenId] = VestDetails({
|
||
|
payoutToken: token,
|
||
|
payout: amount,
|
||
|
startTime: uint128(block.timestamp),
|
||
|
endTime: releaseTimestamp
|
||
|
});
|
||
|
|
||
|
_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 >= _endTime(tokenId)) {
|
||
|
return _payout(tokenId);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @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;
|
||
|
}
|
||
|
}
|