ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WTF Solidity 极简入门:第44讲 代币锁(Token Locker)合约——从 LP 锁仓原理到 ERC20 时间锁的完整实现

WTF Solidity 极简入门:第44讲 代币锁(Token Locker)合约——从 LP 锁仓原理到 ERC20 时间锁的完整实现 WTF Solidity 极简入门第44讲 代币锁Token Locker合约——从 LP 锁仓原理到 ERC20 时间锁的完整实现【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity导读本讲是 WTF Solidity 极简入门教程中面向实战应用的章节聚焦于代币锁Token Locker合约这是一种简单的时间锁合约能把 ERC20 代币锁仓一段时间锁仓期满后由受益人取回常用于锁定去中心化交易所DEX的流动性提供者LP代币从而缓解项目方rug-pull跑路风险。读完本讲你将掌握 LP 代币与 AMM 机制的基本概念、代币锁合约的事件/状态变量/函数设计模式并能基于仓库中的 TokenLocker.sol 源码在 Remix 中完整走通部署 ERC20 → 铸造 → 转入代币锁 → 到期释放的全流程。背景为什么需要代币锁什么是 LP 代币区块链用户在去中心化交易所DEX上交易代币例如 Uniswap。与中心化交易所CEX不同DEX 采用自动做市商AMM机制用户或项目方需要把相应的币对例如ETH/DAI质押到资金池中其他用户才能即时买卖。作为补偿DEX 会为流动性提供者铸造相应的LPLiquidity Provider流动性提供者代币凭证证明其质押了对应份额并凭此收取手续费分成。为什么要锁定流动性如果项目方毫无征兆地从流动性池中撤走 LP 代币投资者手中的代币将无法变现、价值直接归零——这种行为被称为rug-pull拉地毯。仅 2021 年各类 rug-pull 骗局就从投资者手中骗走了价值超过 28 亿美元的加密货币。而如果把 LP 代币锁仓在代币锁合约中在锁仓期结束以前项目方无法撤出流动性池也就无法 rug-pull。因此代币锁可以防止项目方过早跑路——当然仍然需要警惕锁仓期满后跑路的情况这是该方案的天然边界。TokenLocker 合约设计与源码解析仓库中的核心实现位于 44_TokenLocker/TokenLocker.sol其逻辑非常清晰开发者在部署合约时规定锁仓时间、受益人地址和代币合约开发者将代币转入TokenLocker合约锁仓期满后受益人取走合约中的全部代币。合约完整代码节选核心结构如下// SPDX-License-Identifier: MIT // wtf.academy pragma solidity ^0.8.34; import ../31_ERC20/IERC20.sol; import ../31_ERC20/ERC20.sol; /** * dev ERC20代币时间锁合约。受益人在锁仓一段时间后才能取出代币。 */ contract TokenLocker { // 事件 event TokenLockStart(address indexed beneficiary, address indexed token, uint256 startTime, uint256 lockTime); event Release(address indexed beneficiary, address indexed token, uint256 releaseTime, uint256 amount); // 被锁仓的ERC20代币合约 IERC20 public immutable token; // 受益人地址 address public immutable beneficiary; // 锁仓时间(秒) uint256 public immutable lockTime; // 锁仓起始时间戳(秒) uint256 public immutable startTime; ... }注意合约通过import ../31_ERC20/IERC20.sol引入了上一讲实现的 ERC20 接口见 31_ERC20/IERC20.sol只依赖balanceOf与transfer两个方法即可完成锁仓与释放体现了面向接口编程的简洁设计。事件链上状态的透明记录TokenLocker合约中共有 2 个事件事件触发时机记录内容TokenLockStart合约部署、锁仓开始时受益人地址、代币地址、锁仓起始时间、锁仓时长Release受益人取出代币时受益人地址、代币地址、释放时间、代币数量event TokenLockStart(address indexed beneficiary, address indexed token, uint256 startTime, uint256 lockTime); event Release(address indexed beneficiary, address indexed token, uint256 releaseTime, uint256 amount);其中beneficiary与token均声明为indexed便于链下索引器按受益人/代币地址高效检索事件日志——这与 31_ERC20/IERC20.sol 中Transfer、Approval事件对from/to/owner/spender使用indexed的做法一脉相承。状态变量全部不可变immutableTokenLocker合约共有 4 个状态变量且全部声明为immutable在构造函数中一次性赋值、此后不可修改变量类型含义tokenIERC20被锁仓的 ERC20 代币合约地址beneficiaryaddress受益人地址lockTimeuint256锁仓时间秒startTimeuint256锁仓起始时间戳秒IERC20 public immutable token; address public immutable beneficiary; uint256 public immutable lockTime; uint256 public immutable startTime;将这四个关键参数设为immutable是刻意的安全设计一旦部署任何人都无法篡改受益人、代币地址与锁仓期限这正是代币锁取信于投资者的根基。另外immutable变量的值会直接内联进合约字节码而非占用 storage 槽位可显著降低 Gas 消耗。构造函数初始化锁仓参数构造函数接收代币合约地址、受益人地址与锁仓时长并在部署时固化startTime block.timestampconstructor( IERC20 token_, address beneficiary_, uint256 lockTime_ ) { require(lockTime_ 0, TokenLock: lock time should greater than 0); token token_; beneficiary beneficiary_; lockTime lockTime_; startTime block.timestamp; emit TokenLockStart(beneficiary_, address(token_), block.timestamp, lockTime_); }关键点require(lockTime_ 0, ...)拒绝 0 秒锁仓防止锁了个寂寞startTime取block.timestamp当前区块时间戳即锁仓从部署那一刻自动开始计时部署即释放TokenLockStart事件向链上公开宣告锁仓开始。release 函数到期后一次性释放release()是唯一的取款入口由受益人主动调用function release() public { require(block.timestamp startTimelockTime, TokenLock: current time is before release time); uint256 amount token.balanceOf(address(this)); require(amount 0, TokenLock: no tokens to release); token.transfer(beneficiary, amount); emit Release(msg.sender, address(token), block.timestamp, amount); }三层逻辑逐级把关时间检查block.timestamp startTime lockTime未到锁仓期满则 revert余额检查通过token.balanceOf(address(this))获取合约内代币余额余额为 0 则 revert转账与事件token.transfer(beneficiary, amount)将全部余额转给受益人并释放Release事件。值得说明的是release()没有使用onlyOwner之类的权限控制任何人都可以代为触发——因为受益人是构造函数中固化的immutable地址代币只会转给该受益人所以公开调用是安全的。该函数会将合约内该代币的全部余额一次性转出因此它适合锁定整批 LP 代币的场景若需要分期释放则需扩展为带释放计划的 Vesting 合约可参考仓库第 43 讲 43_TokenVesting/TokenVesting.sol。与底层 ERC20 实现的联动TokenLocker之所以能锁住代币依赖的是 ERC20 合约中transfer对余额的扣除与增加逻辑。仓库在 31_ERC20/ERC20.sol 给出了简洁的实现function transfer(address recipient, uint amount) public override returns (bool) { balanceOf[msg.sender] - amount; balanceOf[recipient] amount; emit Transfer(msg.sender, recipient, amount); return true; }当开发者把代币转入TokenLocker合约后该合约地址在balanceOf映射中的余额即被锁定合约自身没有向外转账的函数唯一出口就是受时间约束的release()。需要留意的是该 ERC20 实现中的transfer没有做充足的余额下溢防护——这正是仓库第 S05 讲 S05_Overflow/Overflow.sol 讨论的安全主题实战中建议使用带安全检查的 OpenZeppelin 标准实现仓库lib/openzeppelin-contracts中已包含代币锁合约本身的逻辑并不依赖这一点。接口方面31_ERC20/IERC20.sol 中balanceOf(address account) external view returns (uint256)与transfer(address to, uint256 amount) external returns (bool)正是release()用到的两个方法TokenLocker以接口类型而非具体合约类型持有token意味着它可以锁仓任何遵循 IERC20 接口的代币包括 WETH、稳定币、LP 代币等通用性极强。Remix 实战演示仓库根目录的 foundry.toml 将solc固定为0.8.34与TokenLocker.sol声明的pragma solidity ^0.8.34一致因此合约可直接在 Remix 中以默认编译器设置编译运行。下面按仓库文档的 5 个步骤走通全流程。步骤 1部署 ERC20 合约并铸造 10000 枚代币先在 Remix 中打开并部署 31_ERC20/ERC20.sol部署时填写名称与符号例如WTF随后调用其mint函数给自己铸造10000枚代币mint的实现位于 31_ERC20/ERC20.sol它把代币从零地址转入调用者地址并累加totalSupply是演示代币锁的最佳代币来源。步骤 2部署 TokenLocker 合约打开 44_TokenLocker/TokenLocker.sol部署参数填写token_上一步 ERC20 合约地址beneficiary_受益人地址演示中即自己lockTime_锁仓时长演示填180秒。部署成功后控制台日志中会输出TokenLockStart事件记录受益人、代币地址、起始时间戳与锁仓时长代表锁仓正式启动。步骤 3将 10000 枚代币转入 TokenLocker回到 ERC20 合约调用transfer方法把10000枚代币转入TokenLocker合约地址。至此代币已处于锁仓状态。步骤 4锁仓期内调用 release 失败在锁仓期180 秒内调用release()函数会因require(block.timestamp startTimelockTime, ...)不满足而 revert交易失败代币无法取出。步骤 5锁仓期满后成功释放等待 180 秒后再次调用release()时间检查通过合约内全部代币转入受益人地址并释放Release事件取款成功。总结这一讲介绍了代币锁合约项目方通常在 DEX 上提供流动性供投资者交易若突然撤出 LP 代币就会造成 rug-pull而把 LP 代币锁进代币锁合约可在锁仓期内防止项目方撤池跑路。TokenLocker合约本身十分精简——4 个immutable状态变量固化锁仓要素2 个事件公开链上状态构造函数负责启动计时唯一的release()函数在时间校验通过后把全部代币转给受益人。从源码结构看该合约也演示了 Solidity 中几个可复用的设计范式用immutable保证不可篡改、用接口类型IERC20解耦具体代币实现、用事件而非存储来降低记账成本。如需进一步延伸可继续学习仓库第 43 讲 43_TokenVesting/readme.md按时间线性释放的代币归属合约与第 45 讲 45_Timelock/readme.md可执行任意调用的时间锁它们共同构成了 DeFi 项目中锁仓—释放—治理的完整时间锁体系。此外仓库在Languages/en/44_TokenLocker_en/提供了本讲的英文版scripts/run-forge-tests.sh也把44_TokenLocker纳入了全仓 Forge 测试清单读者可在本仓库内按需对照学习。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表