September 29, 2023

Токеномика. Линейный вестинг на  Solidity

Вестинг - схема получения токенов в зависимости от времени.

cliff - период времени в котором снятие токенов заблокировано

duration - период времени, за который происходит полное разблокирование токенов для снятия

Ниже приведен код контракта линейного вестинга.

pragma solidity 0.8.20;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract Vesting {
    event TokenReleased(
        address indexed account,
        address indexed token,
        uint256 amount
    );
    
    struct Info {
        uint256 locked;
        uint256 released;
    }
    
    address public immutable token;
    uint256 public immutable startTimestamp;
    uint256 internal cliffDuration;
    uint256 internal vestingDuration;
    
    mapping(address => Info) internal _vesting;
    
    constructor(
        address token_,
        uint256 cliffMonthDuration,
        uint256 vestingMonthDuration,
        address[] memory accounts,
        uint256[] memory amounts
    ) {
        require(
                vestingMonthDuration >= cliffMonthDuration, 
                "duration less then cliff"
        );
        
        startTimestamp = uint64(block.timestamp);
        token = token_;
        cliffDuration = cliffMonthDuration * 4 weeks;
        vestingDuration = vestingMonthDuration * 4 weeks;

        for (uint256 i = 0; i < accounts.length; i++) {
            _vesting[accounts[i]] = Info({locked: amounts[i], released: 0});
        }
    }
    function release() external {
        require(
            block.timestamp > startTimestamp + cliffDuration,
            "cliff period has not ended yet."
        );
        
        address sender = msg.sender;
        Info storage vestingInfo = _vesting[sender];
        uint256 amountByMonth = vestingInfo.locked /
            ((vestingDuration) / 4 weeks); 
        uint256 releaseAmount = 0;
        // Проверка, что если юзер клеймит после разлока всех токенов, то ему отправляется предназначающийся ему остаток не заклеймленных токенов.
        if(block.timestamp >= startTimestamp + vestingDuration){
            releaseAmount = vestingInfo.locked - vestingInfo.released;
        }else{
            releaseAmount = (((block.timestamp - startTimestamp) * amountByMonth) /
                        4 weeks) -
                        vestingInfo.released;
        }
        require(releaseAmount > 0, "not enough release amount.");
        vestingInfo.released += releaseAmount;
        SafeERC20.safeTransfer(IERC20(token), sender, releaseAmount);
    }
}

В конструктор передается адрес токена, начальный блок, cliff, duration и два массива. Первый массив содержит адреса, которые смогут клеймить токены. Второй массив содержит количества токенов для соответствующего адреса.

Например массив 1: [0х1, 0х2] и массив 2: [100, 200]. Значит с адреса 0x1 может быть заклеймлено 100 токенов, а с адреса 0x2 - 200 токенов