——用时间锁合约守护 LP 流动性,抵御 Rug Pull)
WTF Solidity 教程44. 代币锁TokenLocker——用时间锁合约守护 LP 流动性抵御 Rug Pull【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity代币锁Token Locker是一种基于时间锁思想设计的 ERC20 智能合约它在部署时固化锁仓期限与受益人锁定期内任何地址都无法动用被锁代币。本讲将结合 WTF-Solidity 仓库中的TokenLocker合约源码与 Remix 实操演示说明如何用约 70 行代码锁定 LP 代币、防止项目方提前撤池跑路。学完本讲你将理解 LP 代币与 AMM 流动性池的关系、时间锁合约的完整实现细节并能在 Remix 中独立完成铸造代币 → 部署锁仓 → 到期提取的完整流程。代币锁Token Locker是什么代币锁Token Locker是一种简单的时间锁合约它可以把合约中的代币锁仓一段时间受益人在锁仓期满后才能取走代币。代币锁最常见的应用场景就是锁仓流动性提供者Liquidity ProviderLP代币。contract TokenLocker { // 被锁仓的 ERC20 代币合约 IERC20 public immutable token; // 受益人地址 address public immutable beneficiary; // 锁仓时间秒 uint256 public immutable lockTime; // 锁仓起始时间戳秒 uint256 public immutable startTime; }什么是 LP 代币在去中心化交易所DEX如 Uniswap上用户可以直接交易代币。与中心化交易所CEX不同DEX 采用自动做市商AMM机制用户或项目方需要把相应的币对例如ETH/DAI质押进资金池其他用户才能即时买卖这些代币。作为提供流动性的补偿DEX 会为用户/项目方铸造相应的流动性提供者代币凭证即LP 代币。LP 代币证明持有人质押了相应份额并据此收取交易手续费。简单说质押币对 → 获得 LP 凭证 → 分享手续费。为什么要锁定流动性Rug Pull 风险如果项目方毫无征兆地撤出流动性池中的 LP 代币投资者手中的代币就会因为失去交易场所而无法变现、价值直接归零。这种行为被称为rug-pull拉地毯/跑路。仅 2021 年各种 rug-pull 骗局就从投资者那里骗取了价值超过 28 亿美元的加密货币。但若 LP 代币被锁进代币锁合约那么在锁仓期结束之前项目方就无法撤出流动性池也就无法实施 rug-pull。因此代币锁可以在锁仓期内防止项目方过早跑路当然锁仓期一旦届满仍需警惕项目方立即撤池跑路的可能性。TokenLocker 合约实现下面就来写一个锁仓 ERC20 代币的合约TokenLocker。它的逻辑非常简单一共只有 3 条开发者在部署合约时规定锁仓时间、受益人地址以及代币合约地址开发者将代币转入TokenLocker合约锁仓期满后受益人可以从合约中取走代币。完整源码位于 Languages/en/44_TokenLocker_en/TokenLocker.sol根目录中文版见 44_TokenLocker/TokenLocker.sol合约基于^0.8.34编译。事件EventsTokenLocker合约中共有 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允许链下索引器按受益人地址或代币地址高效过滤相关事件便于 DApp 前端与区块浏览器展示锁仓/释放历史。状态变量State VariablesTokenLocker合约中共有 4 个状态变量全部声明为immutabletoken被锁仓的 ERC20 代币合约地址beneficiary受益人地址lockTime锁仓时间秒startTime锁仓起始时间戳秒。// Locked ERC20 token contracts IERC20 public immutable token; // Beneficiary address address public immutable beneficiary; // Lockup time (seconds) uint256 public immutable lockTime; // Lockup start timestamp (seconds) uint256 public immutable startTime;从源码结构看immutable关键字保证了这 4 个参数在部署时一次性写入、此后不可再被任何人修改。这是时间锁合约可信度的关键锁仓期、受益人、代币地址在链上固化后任何人都无法篡改锁仓承诺因此具备可验证性。构造函数初始化锁仓参数构造函数接收三个参数并完成初始化constructor(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 startTime lockTime, 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则回滚并提示no tokens to release代币转账调用token.transfer(beneficiary, amount)把全部余额一次性转给受益人释放事件触发Release事件记录本次释放的受益人、代币、时间与数量。从代码实现可以推断两点边界行为其一release()是public函数任何人不限于受益人都可以在到期后触发它但代币始终只会转给部署时固化的beneficiary地址因此无需权限校验也不会造成资金去向风险其二合约每次释放的是全部余额且没有设计分多次释放的机制——代币一经释放即一次性转出。源码级扩展合约如何与 ERC20 交互TokenLocker通过IERC20接口与代币交互该接口定义于 Languages/en/31_ERC20_en/IERC20.sol根目录中文版为 31_ERC20/IERC20.sol。本合约实际用到的接口方法有三个balanceOf(address account) external view returns (uint256)查询指定地址的代币余额用于确认可释放数量transfer(address to, uint256 amount) external returns (bool)将代币从合约账户转给受益人approve/transferFromrelease()本身并不需要但部署演示中把代币转入合约这一步通常借助 ERC20 的approvetransferFrom或直接transfer完成。对应的 ERC20 标准实现可参考 Languages/en/31_ERC20_en/ERC20.sol其中transfer的实现在 Languages/en/31_ERC20_en/ERC20.sol 附近先扣减balanceOf[msg.sender]再增加balanceOf[recipient]最后触发Transfer事件。需要说明的是TokenLocker只锁定 ERC20 标准代币含 LP 代币合约本身不持有 ETH也不实现任何收益分红逻辑——它只是一个纯粹、可审计的时间锁容器。Remix 实操演示下面用 Remix IDE 完整演示代币锁的部署与释放流程共 5 步。第 1 步部署 ERC20 合约并铸造 10000 枚代币部署第 31 讲中的 ERC20 合约见 Lesson 31合约构造函数需要传入name与symbol例如WTF/WTF。部署完成后调用mint函数给自己铸造10000枚代币。第 2 步部署 TokenLocker 合约切换到TokenLocker.sol在 DEPLOY RUN TRANSACTIONS 面板中填入三个参数token上一步部署的 ERC20 合约地址beneficiary受益人地址演示中设为自己lockTime锁仓期本演示填180秒。点击 transact 部署后事件日志中会出现TokenLockStart记录受益人、代币地址与锁仓时长证明参数已正确固化。第 3 步将 10000 枚代币转入合约调用 ERC20 合约的transfer或先approve再用transferFrom把10000枚代币转入TokenLocker合约地址。第 4 步锁仓期内调用 release()取币失败在 180 秒锁仓期内调用release()交易会因require校验失败而回滚控制台报错TokenLock: current time is before release time无法取出代币——这正是时间锁的核心约束在起作用。第 5 步锁仓期满后调用 release()成功取回等 180 秒锁仓期满后再次调用release()此时时间校验通过代币被一次性转给受益人事件日志中会出现Transfer与Release事件证明释放成功。演示要点小结阶段操作结果锁仓前铸造 10000 枚代币余额充足部署锁仓填 token / beneficiary / lockTime180触发 TokenLockStart转入代币transfer 10000 枚给合约合约持币锁仓期内调用 release()回滚无法取出锁仓期满再次调用 release()触发 Transfer 与 Release取回代币总结这一讲介绍了代币锁合约。项目方通常在 DEX 上提供流动性供投资者交易一旦项目方突然撤出 LP就会造成rug-pull而把 LP 代币锁进代币锁合约可以在锁仓期内有效避免这种风险。TokenLocker的核心设计可归纳为三点部署时固化参数immutable 时间戳起点、到期前强制回滚require 时间校验、到期后一键全量释放balanceOf transfer。代码极简、无权限绕过路径适合作为理解时间锁与 LP 流动性的入门示例。需要提醒的是锁仓期满后项目方仍可能立即撤池因此代币锁是延后风险而非消灭风险的投资保障手段。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考