质押合约 Stake Contract 源码分析
发布于: 2025-07-18 · 15 min read · 更新于: 2025-07-18
合约开发Web3Solidity
MetaNode质押合约完整项目分析
项目概述
这是一个完整的MetaNode代币质押项目,包含智能合约和前端应用。用户可以通过质押ETH获得MetaNode代币奖励,项目采用流动性挖矿机制,支持多池子质押和灵活的奖励分配策略。
项目结构
1Advanced2-contract-stake/ 2├── stake-contract/ # 智能合约 3│ ├── contracts/ 4│ │ ├── MetaNodeStake.sol # 主质押合约 5│ │ └── MetaNode.sol # MetaNode代币合约 6│ └── test/ # 测试文件 7└── stake-fe/ # 前端应用 8├── src/ 9│ ├── components/ # React组件 10│ ├── pages/ # 页面组件 11│ ├── hooks/ # 自定义Hooks 12│ ├── utils/ # 工具函数 13│ └── assets/ # 静态资源 14└── package.json
智能合约详解
合约架构
1contract MetaNodeStake is 2 Initializable, 3 UUPSUpgradeable, 4 PausableUpgradeable, 5 AccessControlUpgradeable
继承关系说明:
- Initializable: 支持合约升级初始化
- UUPSUpgradeable: 支持代理升级模式
- PausableUpgradeable: 支持暂停功能
- AccessControlUpgradeable: 支持角色权限管理
核心数据结构
Pool结构体
1struct Pool { 2 address stTokenAddress; // 质押代币地址 3 uint256 poolWeight; // 池子权重 4 uint256 lastRewardBlock; // 最后奖励计算区块 5 uint256 accMetaNodePerST; // 每个质押代币累积的MetaNode奖励 6 uint256 stTokenAmount; // 池子总质押量 7 uint256 minDepositAmount; // 最小质押数量 8 uint256 unstakeLockedBlocks; // 解质押锁定区块数 9}
关键参数详解:
lastRewardBlock
: 记录池子最后一次计算奖励的区块号,用于时间间隔计算, 用户只要质押ETH, USDT.... 等知名代币(具体取决于项目方融资的代币类型), 则按时间一定会获得的奖励accMetaNodePerST
: 每个质押代币的累积MetaNode奖励,用于用户奖励计算, 按照用户质押ETH, USDT.... 等知名代币的数量, 获取一定的比例的MetaNode
这一新代币的奖励, 可以理解为质押时的所产生的利息
User结构体
1struct User { 2 uint256 stAmount; // 用户质押数量 3 uint256 finishedMetaNode; // 已完成的MetaNode奖励 4 uint256 pendingMetaNode; // 待领取的MetaNode奖励 5 UnstakeRequest[] requests; // 解质押请求列表 6}
UnstakeRequest结构体
1struct UnstakeRequest { 2 uint256 amount; // 请求解质押数量 3 uint256 unlockBlocks; // 解锁区块号 4}
奖励计算机制
核心公式
1// 用户待领取奖励计算公式 2pending MetaNode = (user.stAmount * pool.accMetaNodePerST) - user.finishedMetaNode
奖励分配流程
- 时间间隔计算:
multiplier = block.number - pool.lastRewardBlock
- 池子奖励计算:
MetaNodeForPool = multiplier * poolWeight / totalPoolWeight
- 累积奖励率更新:
accMetaNodePerST += MetaNodeForPool * 1e18 / stSupply
- 用户奖励计算: 基于用户质押量和累积奖励率
主要函数详解
初始化函数
initialize()
1function initialize( 2 IERC20 _MetaNode, 3 uint256 _startBlock, 4 uint256 _endBlock, 5 uint256 _MetaNodePerBlock 6) public initializer
作用: 合约初始化,设置MetaNode代币地址、开始/结束区块、每区块奖励数量
管理员函数
addPool()
1function addPool( 2 address _stTokenAddress, 3 uint256 _poolWeight, 4 uint256 _minDepositAmount, 5 uint256 _unstakeLockedBlocks, 6 bool _withUpdate 7) public onlyRole(ADMIN_ROLE)
作用: 添加新的质押池,设置质押代币、权重、最小质押量、锁定时间
setMetaNodePerBlock()
1function setMetaNodePerBlock(uint256 _MetaNodePerBlock) public onlyRole(ADMIN_ROLE)
作用: 设置每个区块的MetaNode奖励数量
setPoolWeight()
1function setPoolWeight(uint256 _pid, uint256 _poolWeight, bool _withUpdate) public onlyRole(ADMIN_ROLE)
作用: 设置池子权重,影响奖励分配比例
用户操作函数
depositETH()
1function depositETH() public whenNotPaused() payable
作用: 质押ETH,自动调用_deposit()
处理质押逻辑
deposit()
1function deposit(uint256 _pid, uint256 _amount) public whenNotPaused() checkPid(_pid)
作用: 质押ERC20代币,需要先授权合约
unstake()
1function unstake(uint256 _pid, uint256 _amount) public whenNotPaused() checkPid(_pid) whenNotWithdrawPaused()
作用: 申请解质押,立即结算奖励,但资金需要锁定一段时间
withdraw()
1function withdraw(uint256 _pid) public whenNotPaused() checkPid(_pid) whenNotWithdrawPaused()
作用: 提取已解锁的质押资金
claim()
1function claim(uint256 _pid) public whenNotPaused() checkPid(_pid) whenNotClaimPaused()
作用: 领取MetaNode奖励
内部函数
_deposit()
1function _deposit(uint256 _pid, uint256 _amount) internal
作用: 处理质押逻辑的核心函数
- 更新池子奖励计算
- 计算并保存用户待领取奖励
- 更新用户质押量和已完成奖励
updatePool()
1function updatePool(uint256 _pid) public checkPid(_pid)
作用: 更新池子奖励计算
- 计算时间间隔奖励
- 更新累积奖励率
- 更新最后奖励区块
奖励机制详解
时间追踪
lastRewardBlock
: 记录池子最后奖励计算时间- 所有用户共享同一个时间点
- 确保不会重复计算奖励
奖励分配
- 按质押量比例分配奖励
- 用户退出时立即结算
- 支持多池子不同权重
安全机制
- 使用SafeMath防止溢出
- 支持暂停功能
- 解质押锁定机制防止快速进出
前端应用详解
技术栈
1{ 2 "dependencies": { 3 "@rainbow-me/rainbowkit": "^2.2.8", // 钱包连接 4 "@tanstack/react-query": "^5.55.3", // 数据查询 5 "framer-motion": "^12.23.5", // 动画效果 6 "next": "^15.3.3", // React框架 7 "react-toastify": "^11.0.5", // 通知提示 8 "viem": "2.29.2", // 以太坊交互 9 "wagmi": "^2.15.6" // React Hooks for Ethereum 10 } 11}