线性释放

今天,我们将介绍代币归属条款,并写一个线性释放ERC20代币的合约。代码由OpenZeppelinVestingWallet合约简化而来。

代币归属条款

在传统金融领域,一些公司会向员工和管理层提供股权。但大量股权同时释放会在短期产生抛售压力,拖累股价。因此,公司通常会引入一个归属期来延迟承诺资产的所有权。同样的,在区块链领域,Web3初创公司会给团队分配代币,同时也会将代币低价出售给风投和私募。如果他们把这些低成本的代币同时提到交易所变现,币价将被砸穿,散户直接成为接盘侠。

所以,项目方一般会约定代币归属条款(token vesting),在归属期内逐步释放代币,减缓抛压,并防止团队和资本方过早躺平。

线性释放

线性释放指的是代币在归属期内匀速释放。举个例子,某私募持有365,000枚ICU代币,归属期为1年(365天),那么每天会释放1,000枚代币。

下面,我们就写一个锁仓并线性释放ERC20代币的合约TokenVesting。它的逻辑很简单:

  • 项目方规定线性释放的起始时间、归属期和受益人。
  • 项目方将锁仓的ERC20代币转账给TokenVesting合约。
  • 受益人可以调用release函数,从合约中取出释放的代币。

事件

线性释放合约中共有1个事件。

  • ERC20Released:提币事件,当受益人提取释放代币时释放。
csharp 复制代码
contract TokenVesting {
    // 事件
    event ERC20Released(address indexed token, uint256 amount); // 提币事件

状态变量

线性释放合约中共有4个状态变量。

  • beneficiary:受益人地址。
  • start:归属期起始时间戳。
  • duration:归属期,单位为秒。
  • erc20Released:代币地址->释放数量的映射,记录受益人已领取的代币数量。
typescript 复制代码
    // 状态变量
    mapping(address => uint256) public erc20Released; // 代币地址->释放数量的映射,记录已经释放的代币
    address public immutable beneficiary; // 受益人地址
    uint256 public immutable start; // 起始时间戳
    uint256 public immutable duration; // 归属期

函数

线性释放合约中共有3个函数。

  • 构造函数:初始化受益人地址,归属期(秒), 起始时间戳。参数为受益人地址beneficiaryAddress和归属期durationSeconds。为了方便,起始时间戳用的部署时的区块链时间戳block.timestamp
  • release():提取代币函数,将已释放的代币转账给受益人。调用了vestedAmount()函数计算可提取的代币数量,释放ERC20Released事件,然后将代币transfer给受益人。参数为代币地址token
  • vestedAmount():根据线性释放公式,查询已经释放的代币数量。开发者可以通过修改这个函数,自定义释放方式。参数为代币地址token和查询的时间戳timestamp
java 复制代码
    /**
     * @dev 初始化受益人地址,释放周期(秒), 起始时间戳(当前区块链时间戳)
     */
    constructor(
        address beneficiaryAddress,
        uint256 durationSeconds
    ) {
        require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address");
        beneficiary = beneficiaryAddress;
        start = block.timestamp;
        duration = durationSeconds;
    }

    /**
     * @dev 受益人提取已释放的代币。
     * 调用vestedAmount()函数计算可提取的代币数量,然后transfer给受益人。
     * 释放 {ERC20Released} 事件.
     */
    function release(address token) public {
        // 调用vestedAmount()函数计算可提取的代币数量
        uint256 releasable = vestedAmount(token, uint256(block.timestamp)) - erc20Released[token];
        // 更新已释放代币数量   
        erc20Released[token] += releasable; 
        // 转代币给受益人
        emit ERC20Released(token, releasable);
        IERC20(token).transfer(beneficiary, releasable);
    }

    /**
     * @dev 根据线性释放公式,计算已经释放的数量。开发者可以通过修改这个函数,自定义释放方式。
     * @param token: 代币地址
     * @param timestamp: 查询的时间戳
     */
    function vestedAmount(address token, uint256 timestamp) public view returns (uint256) {
        // 合约里总共收到了多少代币(当前余额 + 已经提取)
        uint256 totalAllocation = IERC20(token).balanceOf(address(this)) + erc20Released[token];
        // 根据线性释放公式,计算已经释放的数量
        if (timestamp < start) {
            return 0;
        } else if (timestamp > start + duration) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start)) / duration;
        }
    }
相关推荐
天涯学馆8 小时前
Solidity中的访问控制:保护你的智能合约
智能合约·solidity·以太坊
大白猴3 天前
大白话解析 Solidity 中的防重放参数
区块链·智能合约·solidity·时间戳·重放攻击·nonce·防重放参数
大白猴3 天前
大白话解析“入口点合约”
区块链·智能合约·solidity·以太坊·账户抽象·入口点合约·erc4337
天涯学馆5 天前
如何在Solidity中实现安全的数学运算
智能合约·solidity
天涯学馆12 天前
Solidity中的事件和监听器:如何实现合约间的通信
智能合约·solidity·以太坊
天涯学馆16 天前
Solidity 中的高级模式匹配:提升代码的可读性和可维护性
后端·区块链·solidity
技术路上的探险家18 天前
Web3:在 VSCode 中基于 Foundry 快速构建 Solidity 智能合约本地开发环境
vscode·web3·智能合约·solidity·foundry
技术路上的探险家18 天前
Web3:在 VSCode 中使用 Vue 前端与已部署的 Solidity 智能合约进行交互
vscode·web3·区块链·交互·react·solidity·ethers.js
技术路上的探险家18 天前
Web3:以太坊虚拟机
web3·区块链·智能合约·solidity·foundry
天涯学馆18 天前
别让你的智能合约崩了!Web3程序员都该知道的可靠性技巧
web3·区块链·solidity