Goat27StakingUpgradeable 合约函数文档
Goat27StakingUpgradeable 合约函数文档
本文档列出 src/Goat27/Goat27StakingUpgradeable.sol 中所有外部可调用函数的签名、用途、参数与返回值。
权限说明:OWNER 表示需要 OwnableUpgradeable.onlyOwner;ANYONE 表示任何地址可调用;REENTRANCY_SAFE 表示挂 nonReentrant 修饰符(由 OZ ReentrancyGuardTransient 提供,使用 transient storage,不占 storage slot)。
合约定位:UUPS 可升级的多层级质押挖矿合约;提供单币质押静态收益、推荐关系驱动的多层级业绩、加速奖励、直推奖励、销毁排行榜奖励。
1. 初始化(代理入口)
1.1 initialize
- 签名:
function initialize(address initialOwner, address stakingToken_, address usdtToken_) external initializer - 权限:ANYONE(仅可调用一次,后续调用 revert
InvalidInitialization) - 用途:通过 ERC1967 代理完成合约初始化,一次调用即完成代币、预言机、销毁地址、奖池参数等全部初始化。
owner = initialOwnerstakingToken = stakingToken_(项目自有质押代币)usdtToken = usdtToken_(USDT 稳定币,用于计价)stakingUsdtPair = PancakeLibrary.pairFor(stakingToken_, usdtToken_)(自动由 CREATE2 算出 GOAT27/USDT 交易对地址)burnAddress = 0x000000000000000000000000000000000000dEaD(硬编码,不可配置)launchedAt = block.timestamplastLevelDistributionDay = _currentDay()lastBurnRankDistributionDay = _currentDay()minStakeValue = MIN_STAKE_VALUE (100 ether USDT)maxStakeValue = MAX_STAKE_VALUE (11_000 ether USDT)rewardInterval = 1 days- 默认等级门槛(USDT 计):
[10_000, 50_000, 200_000, 500_000, 1_000_000] - 默认等级奖池分配比例(BPS):
[300, 200, 200, 100, 100](合计 9%)
- 参数:
initialOwner:合约所有者地址,必须非零(OZ Ownable 校验)。stakingToken_:质押代币地址,必须非零(项目自有代币,部署前需确保在 Pancake V2 上已和 USDT 形成 Pair)。usdtToken_:USDT 稳定币地址,必须非零(BSC 上一般用0x55d398326f...)。
- 返回值:无。
- Pair 地址自动计算:Pair 地址由合约内部通过
PancakeLibrary.pairFor()按 CREATE2 算出,无需部署者传入,也避免了人工算错或前后顺序不一致。底层使用 BSC 主网常量PANCAKE_FACTORY=0xcA143Ce32...和PANCAKE_INIT_CODE_HASH=0x00fb7f8...。 - 单跳预言:GOAT27 ↔ USDT 直接换算,价格精度与执行路径都更短。Pair 必须已经存在并有流动性(部署流程 Step 3 已保证)。
- 销毁地址固定:
burnAddress一律为0x...dEaD,不接受参数传入;若需自定义销毁地址,需通过 UUPS 升级新实现合约。 - 错误:
InvalidInitialization()当已被初始化时抛出(OZ 自带)。 - 事件:
Configured(stakingToken, usdtToken, stakingUsdtPair, burnAddress)(沿用Configured事件名,便于链下索引)。 - 注意:历史版本曾有独立的
configure(...)函数允许 owner 二次配置并使用 WBNB 两跳预言,新版本已将其并入initialize并改为单跳 GOAT27/USDT Pair,该函数不再存在;若需修改代币/Pair/销毁地址,必须通过 UUPS 升级。
2. owner 配置接口
2.1 setStakeValueLimits
- 签名:
function setStakeValueLimits(uint256 minStakeValue_, uint256 maxStakeValue_) external - 权限:OWNER
- 用途:调整单笔质押价值上下限(USDT 计)。存为
0表示回退到常量默认值。 - 参数:
minStakeValue_:新的最小质押价值,必须 > 0。maxStakeValue_:新的最大质押价值,必须 ≥minStakeValue_。
- 返回值:无。
- 错误:
MinZero()当minStakeValue_ == 0时抛出。BadLimits()当maxStakeValue_ < minStakeValue_时抛出。
- 事件:
StakeValueLimitsUpdated(minStakeValue, maxStakeValue)。
2.2 setLevelThresholds
- 签名:
function setLevelThresholds(uint256[5] calldata thresholds) external - 权限:OWNER
- 用途:调整 5 个等级的业绩门槛(USDT 计)。会自动调用
_rebuildLevelCounts()重建所有用户的等级人数统计,确保次日等级奖池瓜分时使用最新人数。 - 参数:
thresholds:5 个新门槛,要求thresholds[0] > 0且严格单调递增。
- 返回值:无。
- 错误:
ThresholdZero()当thresholds[0] == 0时抛出。BadThresholds()当门槛不单调递增时抛出。
- 事件:
LevelThresholdsUpdated(thresholds)。
2.3 setRewardInterval
- 签名:
function setRewardInterval(uint256 rewardInterval_) external - 权限:OWNER
- 用途:调整静态收益领取间隔(秒)。默认 1 天。质押停止时间、奖池销毁时间、利率下降时间均为常量,owner 不可调整。
- 参数:
rewardInterval_:新的间隔,必须 > 0。
- 返回值:无。
- 错误:
IntervalZero()当rewardInterval_ == 0时抛出。 - 事件:
RewardIntervalUpdated(rewardInterval)。
2.4 resetActiveCycleRewardValue
- 签名:
function resetActiveCycleRewardValue(address user) external - 权限:OWNER
- 用途:重置指定用户本周期的”已分配加速奖励价值”。用于处理误操作导致加速奖励分配异常的情况。仅在本周期刚开始时(未领取任何静态收益)允许。
- 参数:
user:目标用户地址。
- 返回值:无。
- 错误:
NoActiveStake()当用户无有效质押时抛出。CycleStarted()当startedAt != lastStaticClaimAt或startedAt == 0时抛出。CycleClaimed()当claimedStaticDays != 0时抛出。
- 事件:
ActiveCycleRewardValueReset(user)。
2.5 rebuildLevelCounts
- 签名:
function rebuildLevelCounts() external - 权限:OWNER
- 用途:重建所有用户的等级人数统计(在等级门槛变更后已自动调用一次,本接口提供手动入口,用于应对极端异常)。
- 返回值:无。
- 副作用:
delete levelUserCounts→ 遍历users重新统计各等级人数。
3. 用户操作接口
3.1 bindReferrer
- 签名:
function bindReferrer(address referrer) external - 权限:ANYONE
- 用途:用户绑定推荐人(只能绑定一次)。必须在
stake()前绑定,否则 5% 直推奖励会因无主而销毁。 - 参数:
referrer:推荐人地址,不能为自己或零地址。
- 返回值:无。
- 错误:
ReferrerExists()当用户已绑定过推荐人时抛出。ReferrerZero()当referrer == 0时抛出。ReferrerSelf()当referrer == msg.sender时抛出。
- 副作用:
- 自动注册
msg.sender到全局用户列表。 - 自动注册
referrer(若未注册)。 - 增加
accounts[referrer].directCount。 - 追加
msg.sender到directs[referrer]。
- 自动注册
- 事件:
ReferrerBound(user, referrer)。
3.2 stake
- 签名:
function stake(uint256 amount) external - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:用户质押入口。流程:
- 检查合约已配置且未到质押停止时间(
STAKING_STOP_TIMESTAMP)。 - 自动分配今日的等级奖池与销毁排行榜奖励(若跨日)。
- 处理上一周期过期。
- 通过 Uniswap V2 两跳预言把
amount换算为 USDT 价值。 - 校验价值在
[effectiveMinStakeValue, effectiveMaxStakeValue]内。 - 转账代币到本合约并按比例分配:
- 85% 销毁(
burnAmount) - 1% 进入销毁排行榜奖励池(
rankAmount) - 5% 直推奖励(若推荐人存在;否则销毁)
- 9% 进入 5 个等级奖池(按
levelDepositBps)
- 85% 销毁(
- 沿推荐链向上累加团队业绩(最多 200 代)。
- 检查合约已配置且未到质押停止时间(
- 参数:
amount:质押的代币数量(18 位小数)。
- 返回值:无。
- 错误:
NotConfigured()stakingToken == 0时抛出。AmountZero()amount == 0时抛出。StakingStopped()已到STAKING_STOP_TIMESTAMP时抛出。ActiveStakeExists()用户已有有效质押时抛出。BelowMin()USDT 价值 < 最小质押价值时抛出。AboveMax()USDT 价值 > 最大质押价值时抛出。BadSplit()安全校验失败(理论上不可达,防御性)。NoLiquidity()价格预言机交易对储备为 0 时抛出。PairTokenMismatch()价格预言机配置错误时抛出。
- 事件:
Staked(user, tokenAmount, valueInUsdt)。
3.3 claimStatic
- 签名:
function claimStatic() external returns (uint256 rewardAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:领取用户的静态收益。会先触发今日的等级奖池与销毁排行榜奖励分配,再按”上次领取时间到当前时间”的天数发放代币,同时按 10% × 最多 5 代向上分配加速奖励。
- 参数:无。
- 返回值:
rewardAmount实际发放的代币数量。 - 错误:
NoActiveStake()无有效质押时抛出。NothingToClaim()可领取天数为 0 或奖励 USDT 价值为 0 时抛出。
3.4 claimLevelRewards
- 签名:
function claimLevelRewards() external returns (uint256 levelAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:领取用户的等级奖池奖励(
pendingLevelReward)。 - 参数:无。
- 返回值:
levelAmount实际发放的代币数量,无奖励时返回 0。
3.5 claimAccelerationRewards
- 签名:
function claimAccelerationRewards() external returns (uint256 accelerationAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:领取用户的加速奖励(
pendingAcceleration)。 - 参数:无。
- 返回值:
accelerationAmount实际发放的代币数量,无奖励时返回 0。
3.6 claimDirectReward
- 签名:
function claimDirectReward() external returns (uint256 directAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:领取用户的直推奖励(
pendingDirectReward)。 - 参数:无。
- 返回值:
directAmount实际发放的代币数量,无奖励时返回 0。
3.7 claimBurnRankReward
- 签名:
function claimBurnRankReward() external returns (uint256 burnRankAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:领取用户的销毁排行榜奖励(
pendingBurnRankReward)。 - 参数:无。
- 返回值:
burnRankAmount实际发放的代币数量,无奖励时返回 0。
3.8 claimAll
- 签名:
function claimAll() external returns (uint256 staticAmount, uint256 levelAmount, uint256 accelerationAmount, uint256 directAmount, uint256 burnRankAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:一键领取所有奖励(静态 + 等级 + 加速 + 直推 + 销毁排行榜)。同时作为
receive()函数的回调入口。 - 参数:无。
- 返回值:5 个实际发放的代币数量;若所有都为空则 revert
NothingToClaim()。
3.9 claimRewards(旧接口)
- 签名:
function claimRewards() external returns (uint256 totalAmount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:旧版合并接口,仅包含加速 + 等级 + 直推奖励,不包含静态收益。新版本推荐用
claimAll()。 - 参数:无。
- 返回值:
totalAmount实际发放的代币数量,无奖励时 revertNothingToClaim()。
3.10 distributeLevelRewards
- 签名:
function distributeLevelRewards() external - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:手动触发今日的等级奖池分配。同时若销毁排行榜奖励跨日未分配,会一并触发。
- 参数:无。
- 返回值:无。
- 错误:
AlreadyDistributed()今日已分配过时抛出。
3.11 distributeBurnRankRewards
- 签名:
function distributeBurnRankRewards() external - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:手动触发今日的销毁排行榜奖励分配。同时若等级奖池跨日未分配,会一并触发。
- 参数:无。
- 返回值:无。
- 错误:
AlreadyDistributed()今日已分配过时抛出。
3.12 burnRemainingPool
- 签名:
function burnRemainingPool() external returns (uint256 amount) - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:一次性操作,将合约当前所有质押代币余额转入销毁地址(
burnAddress = 0x...dEaD)。仅当达到销毁时间(POOL_BURN_TIMESTAMP = 2028-06-01 UTC)且尚未销毁过时允许。 - 参数:无。
- 返回值:
amount实际销毁的代币数量。 - 错误:
NotConfigured()stakingToken == 0时抛出。AlreadyBurned()已销毁过时抛出。TooEarly()未到POOL_BURN_TIMESTAMP时抛出。
- 副作用:
remainingPoolBurned = true防止重复销毁。 - 事件:
RemainingPoolBurned(amount)。
3.13 receive
- 签名:
receive() external payable - 权限:ANYONE(
REENTRANCY_SAFE) - 用途:作为”一键领取所有奖励”的链上触发入口。要求
msg.value == 0,自动调用_claimAll(msg.sender)。 - 错误:
BnbNotAccepted()当msg.value != 0时抛出。NothingToClaim()当用户没有任何奖励可领取时抛出。
4. 视图函数
4.1 静态收益查询
4.1.1 claimableStaticDays
- 签名:
function claimableStaticDays(address user) public view returns (uint256) - 用途:查询用户当前可领取的静态收益天数(向下取整)。仅在用户处于有效质押状态且尚未达到退出倍数时返回非零值。
- 参数:
user:目标用户地址。
- 返回值:
uint256可领取天数。
4.1.2 staticRewardValue
- 签名:
function staticRewardValue(address user) public view returns (uint256) - 用途:查询用户当前可领取的静态收益 USDT 价值。结果受退出倍数限制。
- 参数:
user:目标用户地址。
- 返回值:
uint256USDT 计价的奖励价值(USDT 自身精度)。
4.2 退出倍数与加速奖励
4.2.1 exitCapValue
- 签名:
function exitCapValue(address user) external view returns (uint256) - 用途:查询用户本周期的退出倍数价值上限,即
stakeValue * 200%。 - 参数:
user:目标用户地址。
- 返回值:
uint256USDT 计价的退出倍数上限。
4.2.2 remainingStaticAccelerationValue
- 签名:
function remainingStaticAccelerationValue(address user) external view returns (uint256) - 用途:查询用户本周期剩余可分配的加速奖励价值(USDT 计),即”退出倍数上限 - 已分配”。
- 参数:
user:目标用户地址。
- 返回值:
uint256USDT 计价的剩余可分配加速奖励;返回 0 表示本周期已达退出倍数。
4.3 等级
4.3.1 currentLevel
- 签名:
function currentLevel(address user) public view returns (uint256 level) - 用途:实时计算用户当前的等级(根据团队业绩即时计算,可能尚未同步到
accounts[user].level)。
4.3.2 storedLevel
- 签名:
function storedLevel(address user) external view returns (uint256) - 用途:查询用户已存储的等级(上次同步时的结果)。
4.3.3 exactLevelCount
- 签名:
function exactLevelCount(uint256 level) external view returns (uint256) - 用途:查询某等级(L1~L5)的用户人数(仅统计该等级)。
- 错误:
BadLevel()当level < 1 || level > 5时抛出。
4.3.4 qualifiedLevelCount
- 签名:
function qualifiedLevelCount(uint256 level) external view returns (uint256 count) - 用途:查询某等级(L1~L5)及以上的总达标人数。例如
qualifiedLevelCount(2)返回 L2+L3+L4+L5 的人数之和。 - 错误:
BadLevel()当level < 1 || level > 5时抛出。
4.3.5 currentDay
签名:
function currentDay() external view returns (uint256)用途:查询当前的”北京日”序号(实现为
(block.timestamp + 8 hours) / 1 days)。链下脚本可与lastLevelDistributionDay/lastBurnRankDistributionDay比较,判断是否需要触发每日分配。权限:无访问限制,纯查询。
典型用法:
1
cast call $STAKING_PROXY "currentDay()(uint256)" --rpc-url bsc
4.3.6 needsDistribution
签名:
function needsDistribution() external view returns (bool needsLevelRewards, bool needsBurnRankRewards)用途:一次性返回”今日是否还需要触发等级奖池分配”以及”今日是否还需要触发销毁排行榜奖励分配”,即
_currentDay() > lastXxxDistributionDay的两个布尔值。便于链下脚本(包括 cron / 前端)在发送交易前判断是否值得发送,避免无谓的 gas 浪费。权限:无访问限制,纯查询。
典型用法:
1
cast call $STAKING_PROXY "needsDistribution()(bool,bool)" --rpc-url bsc
注:
lastLevelDistributionDay/lastBurnRankDistributionDay是 state 上声明的public uint256,Solidity 自动生成同名external viewgetter,可直接cast call $STAKING_PROXY "lastLevelDistributionDay()(uint256)"/"lastBurnRankDistributionDay()(uint256)",无需额外添加 view 函数。
4.4 团队业绩
4.4.1 effectiveTeamValue
- 签名:
function effectiveTeamValue(address user) public view returns (uint256) - 用途:计算用户的有效团队业绩。规则:
总业绩 - 最大单线业绩,防止通过单个大户下线达到等级。
4.4.2 effectiveDirectCount
- 签名:
function effectiveDirectCount(address user) public view returns (uint256 count) - 用途:计算用户的有效直推人数(只统计当前存在有效质押的下线)。用于加速奖励分配时校验是否满足”必须达到该层级的直推人数”条件。
4.5 用户列表
4.5.1 userCount
- 签名:
function userCount() external view returns (uint256) - 用途:查询系统注册过的用户总数。
4.5.2 getDirects
- 签名:
function getDirects(address user) external view returns (address[] memory) - 用途:查询用户的直接下级地址列表。
4.6 一键查询奖励
4.6.1 pendingRewards
- 签名:
function pendingRewards(address user) external view returns (uint256 staticTokenAmount, uint256 accelerationAmount, uint256 levelAmount, uint256 directAmount, uint256 burnRankAmount) - 用途:一键查询用户待领取的所有奖励。
- 参数:
user:目标用户地址。
- 返回值:5 个代币数量,分别对应静态收益(代币)、加速奖励、等级奖池、直推奖励、销毁排行榜奖励。
4.7 每日统计
4.7.1 todayBurned
- 签名:
function todayBurned() external view returns (uint256) - 用途:查询今天(北京时间)已销毁的代币总量。
4.7.2 todayLevelPoolTotal
- 签名:
function todayLevelPoolTotal() external view returns (uint256 total) - 用途:查询今天(北京时间)累积进入 5 个等级奖池的代币总量。
4.7.3 todayBurnRankPool
- 签名:
function todayBurnRankPool() external view returns (uint256) - 用途:查询今天(北京时间)累积进入销毁排行榜奖励池的代币总量。
4.8 销毁排行榜查询
4.8.1 dailyBurnOf
- 签名:
function dailyBurnOf(address user, uint256 day) external view returns (uint256) - 用途:查询指定用户在指定北京日的销毁量(用于排行榜判定)。
- 参数:
user:目标用户地址。day:北京日序号((block.timestamp + 8h) / 1 days)。
4.8.2 dailyTopRankCount
- 签名:
function dailyTopRankCount(uint256 day) external view returns (uint256) - 用途:查询指定北京日的销毁排行榜参与者数量(≤
BURN_RANK_TOP_N = 20)。
4.8.3 dailyTopRank
- 签名:
function dailyTopRank(uint256 day, uint256 index) external view returns (address user, uint256 amount) - 用途:查询指定北京日销毁排行榜第
index名的用户与销毁量(index从 0 开始)。 - 错误:
BadRankIndex()当index >= BURN_RANK_TOP_N时抛出。
4.8.4 getDailyTopRanks
- 签名:
function getDailyTopRanks(uint256 day) external view returns (address[20] memory users_, uint256[20] memory amounts_, uint256 count) - 用途:查询指定北京日销毁排行榜前 N 名的批量信息,用于链下前端一次性展示完整榜单。
4.9 等级奖池查询
4.9.1 currentLevelPoolTotal
- 签名:
function currentLevelPoolTotal() external view returns (uint256 total) - 用途:查询当前所有等级奖池中尚未分配的代币总量。
4.10 等级奖池与等级门槛(public 状态变量)
| 名称 | 类型 | 说明 |
|---|---|---|
levelThresholds |
uint256[5] public |
5 个等级业绩门槛(USDT 计) |
levelDepositBps |
uint256[5] public |
5 个等级分配比例(BPS,合计 9%) |
levelPools |
uint256[5] public |
5 个等级待分配奖池余额 |
levelUserCounts |
uint256[6] public |
index 0 不用,index 1~5 记录各等级人数 |
4.11 价格预言
4.11.1 tokenToUsdt
- 签名:
function tokenToUsdt(uint256 tokenAmount) public view returns (uint256) - 用途:将质押代币数量换算为 USDT 价值。通过 PancakeSwap V2 单跳预言
stakingToken ↔ USDT,使用恒定乘积公式(忽略 0.3% 手续费)。 - 依赖:
stakingUsdtPair(GOAT27/USDT Pair)由initialize内部PancakeLibrary.pairFor自动算出。
4.11.2 usdtToToken
- 签名:
function usdtToToken(uint256 usdtAmount) public view returns (uint256) - 用途:反向换算:USDT → stakingToken(单跳)。
4.12 当前生效参数
| 名称 | 签名 | 说明 |
|---|---|---|
effectiveMinStakeValue |
function effectiveMinStakeValue() public view returns (uint256) |
当前生效的最小质押价值,为 0 时回退到 MIN_STAKE_VALUE (100 ether USDT) |
effectiveMaxStakeValue |
function effectiveMaxStakeValue() public view returns (uint256) |
当前生效的最大质押价值,为 0 时回退到 MAX_STAKE_VALUE (11_000 ether USDT) |
effectiveRewardInterval |
function effectiveRewardInterval() public view returns (uint256) |
当前生效的静态收益领取间隔,为 0 时回退到 1 days |
4.13 不可变量与公开状态
| 名称 | 类型 | 说明 |
|---|---|---|
stakingToken |
IERC20 public |
质押代币(由 initialize 设置) |
usdtToken |
IERC20 public |
USDT 计价(由 initialize 设置) |
wbnbToken |
IERC20 public |
已删除:新版改用 GOAT27/USDT 单跳预言,不再需要 WBNB |
pricePair |
IPancakePair public |
已删除 |
wbnbUsdtPair |
IPancakePair public |
已删除 |
stakingUsdtPair |
IPancakePair public |
GOAT27/USDT Pancake V2 Pair(PancakeLibrary.pairFor 自动算出) |
burnAddress |
address public |
销毁地址(硬编码为 0x000000000000000000000000000000000000dEaD,不可配置) |
burnRankPool |
uint256 public |
销毁排行榜奖励池累积余额 |
lastLevelDistributionDay |
uint256 public |
上次分配等级奖池的北京日 |
lastBurnRankDistributionDay |
uint256 public |
上次分配销毁排行榜奖励的北京日 |
minStakeValue |
uint256 public |
当前生效最小质押价值(USDT 计) |
maxStakeValue |
uint256 public |
当前生效最大质押价值(USDT 计) |
rewardInterval |
uint256 public |
当前生效静态收益领取间隔 |
launchedAt |
uint256 public |
合约启动时间戳 |
remainingPoolBurned |
bool public |
是否已执行过剩余池子销毁 |
burnedByDay(day) |
mapping(uint256 => uint256) public |
按北京日索引的每日销毁量 |
dailyBurns(day, user) |
mapping(uint256 => mapping(address => uint256)) public |
按北京日索引的每用户销毁量 |
topRankUsersByDay(day) |
mapping(uint256 => address[20]) public |
按北京日索引的排行榜前 N 名地址 |
topRankAmountsByDay(day) |
mapping(uint256 => uint256[20]) public |
按北京日索引的排行榜前 N 名销毁量 |
topRankCountByDay(day) |
mapping(uint256 => uint256) public |
按北京日索引的排行榜实际参与人数 |
burnRankPoolByDay(day) |
mapping(uint256 => uint256) public |
按北京日索引的排行榜奖励池累积数量 |
levelPoolsByDay(day) |
mapping(uint256 => uint256[5]) public |
按北京日索引的等级奖池累积数量 |
accounts(user) |
mapping(address => Account) public |
用户账户信息(详见 Account 结构体) |
directs(user) |
mapping(address => address[]) public |
直接下级地址列表 |
branchValue(user, child) |
mapping(address => mapping(address => uint256)) public |
单线下线贡献的业绩 |
users(uint256) |
address[] public |
全局用户列表 |
5. Account 结构体
| 字段 | 类型 | 说明 |
|---|---|---|
referrer |
address |
推荐人地址 |
directCount |
uint256 |
直接下级数量 |
stakeAmount |
uint256 |
当前周期质押代币数量 |
stakeValue |
uint256 |
当前周期质押价值(USDT 计) |
claimedStaticDays |
uint256 |
已领取静态收益的天数 |
startedAt |
uint256 |
当前周期开始时间戳 |
lastStaticClaimAt |
uint256 |
上次领取静态收益的时间戳 |
totalStakedAmount |
uint256 |
历史累计质押总量 |
totalClaimedAmount |
uint256 |
历史累计领取总量 |
pendingAcceleration |
uint256 |
待领取加速奖励 |
pendingLevelReward |
uint256 |
待领取等级奖池奖励 |
pendingDirectReward |
uint256 |
待领取直推奖励 |
pendingBurnRankReward |
uint256 |
待领取销毁排行榜奖励 |
totalTeamValue |
uint256 |
当前周期累计团队业绩(USDT 计) |
staticAccelerationRewardValue |
uint256 |
当前周期已分配加速奖励价值(USDT 计) |
level |
uint256 |
当前等级(0 表示未达 L1) |
exists |
bool |
账户是否存在 |
active |
bool |
当前是否存在有效质押 |
teamActive |
bool |
当前是否还计入团队业绩 |
6. UUPS 升级标准接口
合约使用 OpenZeppelin UUPSUpgradeable,提供以下标准接口:
| 函数 | 签名 | 说明 |
|---|---|---|
upgradeToAndCall |
function upgradeToAndCall(address newImplementation, bytes data) external payable |
由 owner 调用以升级到新实现合约(升级授权见 _authorizeUpgrade,仅 owner 可触发) |
proxiableUUID |
function proxiableUUID() external view returns (bytes32) |
返回实现合约 UUID,ERC1967 标准 |
推荐升级流程(完整说明见 docs/deploy/部署流程.md#步骤-53–升级已有代理):
1 | # 1. 部署新实现合约(留空 NEW_IMPL 时由脚本现场部署) |
脚本会自动做的事:
- 通过 ERC1967 槽位(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)读取并打印旧实现地址
- 部署新实现(或读取 env 传入的)
- 调用
upgradeToAndCall(newImpl, "")— 空 data 表示本次不执行额外初始化(适合纯增量升级,例如本次新增currentDay()/needsDistribution()view) - 校验 ERC1967 槽位已切到新地址
- 校验 storage 保持不变:
owner/stakingToken/stakingUsdtPair都非零且与升级前一致 - 验证新 view 函数可调用
- 把升级记录写到
./staking-upgrade.json
⚠️ 升级时严格遵守 storage layout:只能在已有 slot 后追加新状态变量,不能修改现有变量的顺序或类型,不能缩小
__gap槽位(当前为 50)。已有初始化的字段不能再赋默认值,必须通过reinitializer(N)新建初始化函数。⚠️ 代币 / Pair / 销毁地址不可再修改:
stakingToken/usdtToken/stakingUsdtPair/burnAddress都已固化到initialize内部,owner 无法在升级后调整。如需更换,只能部署全新的代理。💡 本次升级属于纯增量:新增
currentDay()/needsDistribution()只把_currentDay从 internal 暴露成 external、加一个 helper,不动 storage、不需要 reinitializer。
7. Ownable 标准函数
合约继承 OpenZeppelin OwnableUpgradeable,提供以下标准接口(节选):
| 函数 | 签名 | 说明 |
|---|---|---|
owner |
function owner() external view returns (address) |
当前合约所有者 |
transferOwnership |
function transferOwnership(address newOwner) external |
发起所有权转移(仅 owner) |
renounceOwnership |
function renounceOwnership() external |
放弃所有权(仅 owner) |
acceptOwnership |
function acceptOwnership() external |
待定 owner 调用以接受所有权 |
8. 事件索引
| 事件 | 签名 | 触发时机 |
|---|---|---|
Initialized |
event Initialized(uint64 version) |
初始化完成(OZ 自带) |
Configured |
event Configured(address stakingToken, address usdtToken, address stakingUsdtPair, address burnAddress) |
initialize 完成时同时发出(沿用 Configured 事件名,便于链下索引;Pair 由 PancakeLibrary.pairFor 自动算出,burnAddress 固定为 0x...dEaD) |
ReferrerBound |
event ReferrerBound(address indexed user, address indexed referrer) |
用户绑定推荐人 |
Staked |
event Staked(address indexed user, uint256 tokenAmount, uint256 valueInUsdt) |
用户完成质押 |
StaticClaimed |
event StaticClaimed(address indexed user, uint256 daysClaimed, uint256 rewardAmount) |
用户领取静态收益 |
AccelerationQueued |
event AccelerationQueued(address indexed from, address indexed to, uint256 rewardAmount) |
加速奖励被加入上级待领取余额 |
RewardsClaimed |
event RewardsClaimed(address indexed user, uint256 accelerationAmount, uint256 levelAmount, uint256 directAmount, uint256 burnRankAmount) |
用户领取奖励(任意类型) |
BurnRankRewardsDistributed |
event BurnRankRewardsDistributed(uint256 indexed dayIndex, uint256 poolAmount, uint256 participantCount) |
销毁排行榜每日奖励分配完成 |
StakeExpired |
event StakeExpired(address indexed user, uint256 valueInUsdt) |
用户本周期达到退出倍数 |
LevelRewardsDistributed |
event LevelRewardsDistributed(uint256 indexed dayIndex) |
等级奖池每日分配完成 |
StakeValueLimitsUpdated |
event StakeValueLimitsUpdated(uint256 minStakeValue, uint256 maxStakeValue) |
质押价值上下限更新 |
LevelThresholdsUpdated |
event LevelThresholdsUpdated(uint256[5] levelThresholds) |
等级门槛更新 |
RewardIntervalUpdated |
event RewardIntervalUpdated(uint256 rewardInterval) |
静态收益领取间隔更新 |
RemainingPoolBurned |
event RemainingPoolBurned(uint256 amount) |
剩余奖池销毁 |
LevelUpdated |
event LevelUpdated(address indexed user, uint256 oldLevel, uint256 newLevel) |
用户等级变更 |
ActiveCycleRewardValueReset |
event ActiveCycleRewardValueReset(address indexed user) |
owner 重置用户本周期加速奖励价值 |
9. 错误索引
| 错误 | 触发条件 |
|---|---|
ZeroAddress() |
关键参数为零地址(initialize 时 owner / stakingToken / usdtToken 任一为 0) |
NotConfigured() |
stakingToken == 0,合约未初始化或 initialize 失败 |
AmountZero() |
质押数量为 0 |
StakingStopped() |
已到 STAKING_STOP_TIMESTAMP |
ActiveStakeExists() |
用户已有有效质押 |
BelowMin() |
USDT 价值 < 最小质押价值 |
AboveMax() |
USDT 价值 > 最大质押价值 |
BadSplit() |
分配比例求和超过总额(防御性) |
NoActiveStake() |
无有效质押 |
CycleStarted() |
周期已开始 |
CycleClaimed() |
本周期已领取 |
MinZero() |
setStakeValueLimits 时 min == 0 |
BadLimits() |
setStakeValueLimits 时 max < min |
ThresholdZero() |
setLevelThresholds 时 thresholds[0] == 0 |
BadThresholds() |
setLevelThresholds 时门槛不单调递增 |
IntervalZero() |
setRewardInterval 时 rewardInterval == 0 |
NothingToClaim() |
无任何奖励可领取 |
AlreadyBurned() |
已销毁过剩余池子 |
TooEarly() |
未到 POOL_BURN_TIMESTAMP |
NoLiquidity() |
价格预言机交易对储备为 0 |
PairTokenMismatch() |
价格预言机配置错误 |
ReferrerExists() |
已绑定过推荐人 |
ReferrerZero() |
推荐人为零地址 |
ReferrerSelf() |
推荐人为自己 |
BadLevel() |
`level < 1 |
BnbNotAccepted() |
receive() 时 msg.value != 0 |
BadRankIndex() |
销毁排行榜 index >= BURN_RANK_TOP_N |
AlreadyDistributed() |
今日已分配过该奖励 |
ReentrancyGuardReentrantCall() |
重入调用(OZ 自带) |
InvalidInitialization() |
重复初始化(OZ 自带) |
⚠️
PairMismatch()已在旧版本移除(用于configure()校验交易对 token,现由PancakeLibrary.pairFor自动算 Pair,无需人工校验)。
10. 关键时间常量
| 常量 | 值 | UTC 时刻 | 说明 |
|---|---|---|---|
STATIC_RATE_DROP_TIMESTAMP |
1801872000 |
2027-02-06 00:00:00 | 静态利率从 1%/天降为 0.5%/天 |
STAKING_STOP_TIMESTAMP |
1830297600 |
2028-01-01 00:00:00 | 停止接受新质押 |
POOL_BURN_TIMESTAMP |
1843430400 |
2028-06-01 00:00:00 | 允许销毁剩余奖池 |
北京日计算:
(block.timestamp + 8 hours) / 1 days,即北京时间 00:00 切换日。
11. 质押分配流程示例
假设用户 Alice 质押 1000 GOAT27(USDT 价值 = 1000 USDT,推荐人 = Bob):
| 分配项 | 比例 | 数量 | 接收方 |
|---|---|---|---|
| 销毁 | 85% | 850 GOAT27 | burnAddress (0x…dEaD,硬编码) |
| 销毁排行榜 | 1% | 10 GOAT27 | burnRankPool(明日分配) |
| 直推奖励 | 5% | 50 GOAT27 | accounts[Bob].pendingDirectReward |
| L1 奖池 | 3% | 30 GOAT27 | levelPools[0] |
| L2 奖池 | 2% | 20 GOAT27 | levelPools[1] |
| L3 奖池 | 2% | 20 GOAT27 | levelPools[2] |
| L4 奖池 | 1% | 10 GOAT27 | levelPools[3] |
| L5 奖池 | 1% | 10 GOAT27 | levelPools[4] |
| 合计 | 100% | 1000 GOAT27 | — |
副作用:
accounts[Alice].stakeValue = 1000e18 USDTaccounts[Bob].totalTeamValue += 1000e18(沿链继续向上传播)accounts[Bob].branchValue[Alice] += 1000e18_syncLevel(Bob)检查是否晋升
12. 销毁排行榜奖励流程示例
假设某日有 3 名用户销毁:Alice 500、Bob 300、Carol 200(总计 1000),且销毁排行榜池累积 100 GOAT27:
| 用户 | 当日销毁量 | 占比 | 奖励(100 × stake / 1000) |
|---|---|---|---|
| Alice | 500 | 50% | 50 GOAT27 |
| Bob | 300 | 30% | 30 GOAT27 |
| Carol | 200 | 20% | 20 GOAT27 |
| 合计 | 1000 | 100% | 100 GOAT27 |
排名按每日累计销毁量降序排列,仅取前 20 名参与分配;若不足 20 名则按实际数量分配。
13. 静态收益与退出倍数示例
假设 Alice 在 2026-06-01 质押 1000 USDT 等值的 GOAT27,在 2026-09-01(90 天后)领取:
- 90 天均在利率下降前(2027-02-06 前),每日利率 = 1%
- 累计静态收益 USDT 价值 =
1000 × 0.01 × 90 = 900 USDT(已接近退出倍数 200% = 2000 USDT) - 退出倍数价值上限 =
1000 × 200% = 2000 USDT,剩余可分配加速奖励 =2000 - 900 = 1100 USDT
加速奖励沿链向上 5 代分配:每代 10%,因此理论可向上分配 900 × 50% = 450 USDT 给 5 个上级。
14. 部署后必做
- 通过 ERC1967 代理调用
initialize(initialOwner, stakingToken, usdtToken)(由部署脚本DeployStakingProxy.s.sol完成):stakingToken/usdtToken必须已经部署,且质押代币已在 Pancake V2 上和 USDT 形成 Pair(参见部署流程 Step 3)。- Pair 地址由合约内部
PancakeLibrary.pairFor(stakingToken, usdtToken)自动计算,部署者无需传入。 burnAddress硬编码为0x000000000000000000000000000000000000dEaD,部署者无法自定义。- 部署脚本示例:必填环境变量:
1
2
3
4forge script script/Goat27/DeployStakingProxy.s.sol \
--rpc-url <network> \
--broadcast \
--sig "run()" \PRIVATE_KEY、STAKING_TOKEN、USDT_TOKEN;可选:INITIAL_OWNER、IMPLEMENTATION。
owner 调用(已废弃,合并入configure(...)配置预言机initialize,无需二次配置)。- 用户先调用
bindReferrer(referrer)绑定推荐人(若不绑定,5% 直推奖励会销毁)。 - 用户再调用
stake(amount)完成质押。 - owner 推荐把代理地址加入 Goat27 的双白名单(
addToWhitelist),避免被 5% 税污染。