关于 vebetterdao-navigators
vebetterdao-navigators 为开发 VeBetterDAO 投票与奖励合约的人整理 Navigator 功能领域知识,涵盖委托投票、奖励乘数、费用和 Relayer 行为。
主要功能
- 01分配与治理奖励乘数
- 02Navigator 注册与公民委托
- 03基于快照的委托投票
- 04Relayer 投票与跳过处理
- 05奖励费用与费用托管
适用场景
- 01实现 Navigator 合约和投票流程
- 02核对委托规则与投票权
- 03检查奖励乘数和费用行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vechain/ vechain-ai-skills --skill vebetterdao-navigators安装后在 Claude Code 中输入/vebetterdao-navigators确认已加载。
详细介绍
VeBetterDAO 导航者功能
导航者功能的领域知识:奖励乘数和导航者委托系统。两个阶段在同一版本中一并发布。
阶段 1:奖励乘数
通过 FreshnessUtils 库应用于 RoundVotesCountingUtils.countVote()——仅修改奖励权重,不修改链上投票权。
| 行为 | 乘数 |
|---|---|
| 本轮(每周)有更新 | x3 (30000 bp) |
| 最近 2 轮(每两周)内有更新 | x2 (20000 bp) |
| >= 3 轮未更新 | x1 (10000 bp) |
- “更新” = 更改所投票的应用集合(添加/移除/替换)。在相同应用之间更改权重不算
- 通过对已投票应用 ID 进行 XOR 指纹计算得出(与顺序无关,O(n),节省 gas)
- 首次投票的用户始终获得 x3(通过
lastFingerprint == bytes32(0)检测) - 可通过
VoterRewards.setFreshnessMultipliers(tier1, tier2, tier3)由治理配置 - 数值以
Checkpoints.Trace208的形式存储在 VoterRewards 中(在轮次开始时快照) - 由导航者代为委托投票的公民继承导航者的新鲜度(投票时使用导航者的偏好更新公民指纹)
治理意向乘数(提案投票)
在调用 registerVote() 前应用于 GovernorVotesLogic。在注册时按提案计算——每个提案相互独立。
| 投票类型 | 乘数 |
|---|---|
| 赞成/反对 | x1 (10000 bp) |
| 弃权 | x0.30 (3000 bp) |
- 可通过
VoterRewards.setIntentMultipliers(forAgainst, abstain)由治理配置 - 由导航者代为委托投票的公民继承导航者的决策乘数
乘数适用范围
- 新鲜度仅缩放分配奖励的奖励权重(在
RoundVotesCountingUtils.countVote()中) - 意向仅缩放治理奖励的奖励权重(在
GovernorVotesLogic中、调用registerVote前) - GM 不会与新鲜度/意向叠加。 当 GM 奖励池已注资(v5+ 实时配置)时,GM 会在
cycleToVoterToGMWeight中累积各投票者独立的权重,并从 GM 奖励池领取——它不会乘以分配/治理奖励权重。VoterRewards.registerVote()中旧版的weight * GM路径仅在gmPoolAmount == 0时运行 - 比例:基点(10000 = 1x)
安全升级
VoterRewards.initializeV7 为每个乘数创建两个检查点:
- 在轮次开始时间点设为 → 10000(中性),使当前轮次不受影响
- 在当前区块设为 → 实际配置值(下一轮生效)
阶段 2:导航者系统
核心概念
- 导航者:质押 B3TR 以代表委托公民投票的用户
- 公民:将特定数量的 VOT3 委托给导航者的用户
- 角色互斥——导航者不能成为另一位导航者的公民
- 公民保留 VOT3 的完整所有权,但已委托部分会被锁定
注册
- 无需许可——在 NavigatorRegistry 中至少质押 50,000 B3TR
- 最高质押额:流通中 VOT3 的 1%(仅在存入时强制执行,现有质押不受影响)
- 10:1 委托比例:质押额必须 >= 已委托 VOT3 总量的 10%。若减少质押会导致违反此比例,则禁止减少
- 用于个人资料的链上元数据 URI(与 X2EarnApps 使用相同模式)
- 链上报告每轮可选,但至少每 2 轮必须提交一次,通过
submitReport(metadataURI)提交 - 导航者不能启用自动投票(角色互斥)
- 导航者不能向其他导航者委托(角色互斥)
委托
- Citizens 委托特定数量的 VOT3(明确选择,而非全部余额),最低为 1 VOT3
- VOT3 保留在 citizen 的钱包中,但已委托的部分会被锁定(无法转账或转换为 B3TR)
balanceOf()返回 全部 VOT3 余额(包括已委托部分)。使用unlockedBalance()获取可转账数量- VOT3
_update()强制执行:balance - delegatedAmount >= transferAmount - VOT3 通过
INavigatorRegistry.getDelegatedAmount(from)从 NavigatorRegistry 读取委托数量——无需授予角色,VOT3 上也没有映射;VOT3 仅存储注册表地址 - 每位 citizen 只能委托给一位 navigator
- 在轮次开始时创建快照——轮次中途的变更将在下一轮生效
- 允许部分取消委托(在下一轮生效)
- 委托无需进行 personhood 检查。在投票时(快照时)验证 personhood;非 person 的 citizens 将被跳过
- 已委托的 Citizens 无法手动投票——必须先退出委托
- 委托时会禁用自动投票(NavigatorRegistry 调用
XAllocationVoting.disableAutoVotingFor) - 未委托的 VOT3 处于闲置状态(不产生收益)
- 如果委托会超出 navigator 的容量(10:1 比例),则会拒绝委托
委托函数
| 函数 | 用途 | 事件 |
|---|---|---|
delegate(navigator, amount) | 仅限首次委托。自动清除已失效 navigator 的过期委托。 | DelegationCreated |
increaseDelegation(amount) | 向现有委托增加更多 VOT3 | DelegationIncreased |
reduceDelegation(amount) | 部分或全部减少委托(全部减少 = 移除委托) | DelegationDecreased 或 DelegationRemoved |
undelegate() | 完全移除委托 | DelegationRemoved |
全局 Citizen 数量
NavigatorRegistry 会全局追踪已委托的 citizens:
Checkpoints.Trace208 totalDelegatedCitizens——委托时递增,取消委托时递减mapping(address => uint256) navigatorCitizenCount——按 navigator 统计的数量getTotalDelegatedCitizensAtTimepoint(timepoint)——供XAllocationVoting.startNewRound用于计算governanceUsers- 在
announceExit()/deactivateNavigator()时:navigatorCitizenCount 清零,且 totalDelegatedCitizens 立即递减
投票机制
Allocation Voting(XAllocationVoting)
两个独立函数(不合并):
castVoteOnBehalfOf(voter, roundId)——现有的自动投票流程(不变)castNavigatorVote(citizen, roundId)——navigator 委托的 citizens:- 在快照时验证 personhood——非 person 的 citizens 会被跳过(不会回滚),与
castVoteOnBehalfOf的行为一致 - 投票权重 = 轮次快照时的委托数量(使用检查点记录,而非全部余额)
- Navigator 的应用偏好和百分比(自定义权重分配,以基点表示,总和必须为 10000;最多 15 个应用)
- 每位 citizen = 单独一笔交易
- 包含内置的跳过或投票逻辑(参见下文的 Relayer 集成)
- 为调用者在 RelayerRewardsPool 中注册
RelayerAction.VOTE
- 在快照时验证 personhood——非 person 的 citizens 会被跳过(不会回滚),与
Navigator 设置偏好即为其本人投票(个人 VOT3 余额 + 抵押的 B3TR 转换为 VOT3)
治理提案投票(B3TRGovernor)
castNavigatorVote(proposalId, citizen)——独立函数:
- 在提案快照时,citizen 必须已委托给某个 navigator
- Navigator 必须已设置决定(1=反对,2=赞成,3=弃权)
- 投票权重 = 提案快照时的委托数量(使用检查点记录)
- 应用意向乘数以计算奖励
- 在 RelayerRewardsPool 中注册
RelayerAction.VOTE - 包含内置的跳过或投票逻辑(参见下文的 Relayer 集成)
getVotes 反映委托
两个治理合约上的 getVotes(account, timepoint):
- 如果公民在
timepoint时有有效的导航者:仅返回委托金额(这是该公民的投票权) - 如果没有导航者:返回完整的
VOT3.getPastVotes(在 XAllocationVoting 中还会加上getDepositVotingPower) - 当
navigatorRegistry == address(0)时返回完整余额(向后兼容) getQuadraticVotingPower内部会调用getVotes,因此委托会传递为sqrt(委托金额) * 1e9- 仅用于查询:如果公民在快照时有导航者,实际调用
castVote会回退并报错DelegatedToNavigator。导航者投票时会直接使用getDelegatedAmountAtTimepoint
中继器集成
计入中继器预期操作的公民:
- 在轮次开始时,
XAllocationVoting.startNewRound会计算:allocationUsers = autoVotingUsers + totalDelegatedCitizensgovernanceUsers = totalDelegatedCitizens(仅公民——中继器不会代表自动投票用户投治理票)
- 通过
B3TRGovernor.getActiveProposals()获取有效的治理提案 - 将所有数据传递给
RelayerRewardsPool.setTotalActionsForRoundWithGovernance(roundId, allocationUsers, governanceUsers, activeProposalIds) - 每轮预期操作数:
allocationUsers * 2(投票 + 领取)+governanceUsers * activeProposals(治理投票)
跳过或投票流程:
castNavigatorVote(分配投票和治理投票)将投票和跳过操作合并到单个函数中:
- 快照时导航者已失效 → 回退并报错
NotDelegatedToNavigator(该公民从未委托) - 快照时该公民不是人员 → 立即跳过,并减少预期操作数(
NavigatorVoteSkipped/NavigatorGovernanceVoteSkipped) - 导航者目前已失效(在快照后退出/停用)→ 立即跳过,并减少预期操作数
- 导航者有效 + 已设置偏好/决策 → 正常投票
- 导航者有效 + 未设置偏好/决策 + 已到跳过时段 → 跳过,并减少预期操作数
- 导航者有效 + 未设置偏好/决策 + 尚未到跳过时段 → 回退(中继器稍后重试)
跳过时段 = 距离轮次/提案截止时间 2 小时(约 720 个区块)。常量:CITIZEN_SKIP_WINDOW_BLOCKS(分配投票)、GOVERNANCE_SKIP_WINDOW_BLOCKS(治理投票)。
按用户跟踪跳过操作(RelayerRewardsPool V3):
reduceUserAllocationVote(roundId, user)— 减少特定用户的一项分配投票操作reduceUserGovernanceVote(roundId, user, proposalId)— 减少每位用户针对每项提案的一项治理投票操作- 当用户的所有投票操作(分配投票 + 所有治理提案)都被跳过时,会自动减少领取操作
- 防止重复跳过:如果同一用户/轮次/提案的操作已减少,则回退
有效提案缓存:
- 创建提案时填充
B3TRGovernor.proposalsForRound映射 getActiveProposals()从当前轮次的提案中筛选处于 Active 状态的提案- 在轮次开始时缓存到
RelayerRewardsPool.activeProposalsForRound
偏好设置过晚违规:
- 导航者必须至少在轮次截止前
preferenceCutoffPeriod个区块(约 24 小时)设置偏好 - 允许稍后设置,但会将该轮次标记为轻微违规
委托时禁用自动投票:
NavigatorRegistry.delegate()会调用XAllocationVoting.disableAutoVotingFor(citizen)(特权调用)- 取消委托后,用户必须手动重新启用自动投票
首选中继器:
- 公民通过
relayerRewardsPool.setPreferredRelayer(relayer)手动设置preferredRelayer - 在早期访问期间,只有首选中继器(如果已设置且已注册)才能代表该公民操作
- 委托时不会自动设置——公民必须手动设置
公民的费用扣除顺序(领取时):
- 导航者费用:先从总奖励中扣除(进入 NavigatorRegistry 费用托管账户)
- 中继器费用:从剩余金额中扣除(进入 RelayerRewardsPool)
- 公民获得剩余部分
- 中继器费用适用于自动投票用户和公民
- 两者都会登记 CLAIM 操作
奖励
奖励仅按快照时的委托金额按比例发放(而非全部余额)。适用公民自身的 GM 等级。
费用扣除顺序:
1. Navigator fee = gross reward * navigatorFeePercentage / 10000 (goes to NavigatorRegistry escrow)
2. Relayer fee = relayerRewardsPool.calculateRelayerFee(gross - navigatorFee) (goes to RelayerRewardsPool)
3. Citizen receives the rest
- 中继者费用适用于自动投票者和导航者公民
- 导航者费用仅适用于公民
- 在领取奖励时通过 VoterRewards.claimReward() 扣除费用
- 投票时,公民的时效性指纹会更新为导航者的应用偏好(继承时效性)
- 公民继承导航者的治理意图乘数
费用托管
在 NavigatorRegistry 中:mapping(navigator => mapping(round => amount))
- 每轮费用在 4 轮后可领取(滚动解锁)
- 重大惩罚会收缴所有未领取的锁定费用
惩罚
轻微惩罚(自动执行,当前剩余质押的 5%——复利计算)
任何人都可以通过公开函数举报。罚没资金转入金库。
六种违规类型:
- 错过分配投票——拥有公民,却未设置某一轮的偏好(需要有委托)
- 错过治理提案投票——拥有公民,却未在提案投票期间设置决定(需要有委托)
- 分配偏好过期——至少 3 轮未更新(与时效性乘数使用相同阈值)(需要有委托)
- 错过报告——连续 2 轮未提交报告(需要有委托)
- 偏好设置超时——在轮次截止时间前的
preferenceCutoffPeriod(约 24 小时)之后才设置分配偏好(需要有委托) - 质押低于最低要求——轮次开始时和结束时的质押均低于
minStake(无论是否有委托均适用)
违规标志(NavigatorSlashingUtils 中的位标志):
FLAG_MISSED_ALLOCATION = 1 << 0(1)FLAG_LATE_PREFERENCES = 1 << 1(2)FLAG_STALE_PREFERENCES = 1 << 2(4)FLAG_MISSED_REPORT = 1 << 3(8)FLAG_MISSED_GOVERNANCE = 1 << 4(16)FLAG_BELOW_MIN_STAKE = 1 << 5(32)
举报机制:
- 轻微违规按轮次举报,而非按违规类型举报
- 轮次结束后,任何人都可以调用
reportRoundInfractions(navigator, roundId, proposalIds) - 合约会在链上评估该轮次的所有六种违规类型
- 仅当导航者在该轮次快照时有委托,才会检查违规类型 1-5
- 违规类型 6(质押低于最低要求)会无论是否有委托都进行检查——导航者必须始终保持质押高于最低要求
- 质押低于最低要求的检查采用两个检查点:
stakeAtRoundStart < minStake AND stakeAtRoundEnd < minStake。这样,导航者在其质押因惩罚而降低后,有整整一轮的时间进行补足。 - 如果存在任何违规,该轮次只会施加一次轻微惩罚
- 如果轮次仍在进行中,举报会回滚并报错
RoundStillActive
如果质押低于最低要求:导航者仍保持活跃状态,但不能接受新的委托;如果到轮次结束时仍未补足,将受到惩罚。
质押低于最低要求的时间示例(minStake = 50K):
| 轮次 | 开始质押 | 事件 | 结束质押 | belowMinStake? |
|---|---|---|---|---|
| R3 | 50K | 因错过职责被罚没 5% → 47.5K | 47.5K | 否(开始时达到最低要求) |
| R4 | 47.5K | 导航者在轮次中途补足 3K | 50.5K | 否(开始时低于要求,结束前已恢复) |
| R5 | 50.5K | — | 50.5K | 否(高于最低要求) |
如果导航者未恢复,则情况如下:
| 轮次 | 开始质押 | 事件 | 结束质押 | belowMinStake? |
|---|---|---|---|---|
| R3 | 50K | 被罚没 5% → 47.5K | 47.5K | 否(开始时达到最低要求) |
| R4 | 47.5K | 未采取行动 | 47.5K | 是(开始和结束时均低于要求) |
| R5 | 45.125K | 再次受到惩罚 | 42.87K | 是(持续累积) |
重大惩罚(治理流程,最高可罚没 100% 的质押 + 锁定费用 + 移除)
适用情形:操纵、贿赂、买票、未披露报酬。 流程:5 名导航者锁定质押以触发流程 → 4 轮内公布调查结果 → 进行治理投票。
停用
- 通过治理提案(任何人都可以提出),并在链上执行
- 提案包含罚没金额(质押额 + 锁定费用的 0–100%)
- 下一轮生效(当前轮正常结束)
- 无法重新激活——必须重新注册
退出
announceExit()— 发出事件,通知期为 1 轮(可由治理配置)- Navigator 必须在通知期内继续投票
- 延迟失效:宣布退出后,所有公民委托在视图层面立即作废
isDelegated()返回 false,getDelegatedAmount()返回 0- VOT3 转账锁自动解除(公民无需采取任何操作)
castNavigatorVote失败并返回NotDelegatedToNavigator- 不再接受新公民委托(
NavigatorCannotAcceptDelegations)
- 公民可以直接重新委托给新的 Navigator,无需先调用
undelegate()(过期委托会自动清除) - 退出通知期结束后(达到停用检查点),即可调用
withdrawStake()——质押额立即返还。锁定费用仍按照各自的 4 轮计划,通过claimFee(roundId)领取 - 重新加入 = 重新注册
- 没有
finalizeExit()——退出通过检查点时间自动生效
Navigator 退出/停用时的公民体验
公民无需采取任何操作——VOT3 通过延迟失效自动解锁:
getDelegatedAmount(citizen)检查 Navigator 是否已失效 → 返回 0- VOT3._update() 读取
getDelegatedAmount→ 发现返回值为 0 → 允许转账 - 公民可以直接调用
delegate(newNavigator, amount)——旧的过期检查点会自动清除 - 保留历史委托数据用于奖励计算(不影响
getDelegatedAmountAtTimepoint)
索引器:隐式移除委托
Navigator 退出或停用时,不会发出 DelegationRemoved 事件。索引器必须将 ExitAnnounced / NavigatorDeactivated 事件视为移除该 Navigator 的所有公民委托。
质押和投票权
Navigator 质押 B3TR,底层会将其转换为 VOT3。质押金额由 NavigatorRegistry 以 VOT3 形式持有,并计入 Navigator 的个人投票权(通过 stakedAmount Trace208 创建检查点)。NavigatorRegistry 在初始化时会在 VOT3 上进行自我委托,以确保总投票供应量准确。
质押的 VOT3 仅计入投票权——不能用于支持提案,也不能降低权力。它不会直接产生奖励。
| 角色 | 投票权来源 | 不计入的内容 |
|---|---|---|
| Navigator(自行投票) | 个人 VOT3 余额 + 质押的 B3TR(以 VOT3 形式) | 公民委托的 VOT3 |
| 公民(由 Navigator 代投) | 快照时其委托的 VOT3 数量 | 未委托的剩余 VOT3 |
委托的 VOT3 始终留在公民的钱包中。Navigator 决定投票方向;每位公民的委托金额作为单独的一票投出。
**B3TR↔VOT3 转换流程:**调用 register()/addStake() 时:B3TR 转入合约 → 通过 VOT3.convertToVOT3() 转换为 VOT3。调用 reduceStake()/withdrawStake()/罚没时:VOT3 通过 VOT3.convertToB3TR() 转回 B3TR → 转出。
**经济机制:**质押的 B3TR 为 Navigator 提供投票权。Navigator 还必须吸引委托才能赚取费用。例如:质押 50K B3TR(= 50K VOT3 投票权)→ 最多获得 500K VOT3 委托 → Navigator 从这 500K VOT3 产生的奖励中赚取 20% 的费用。
合约级保护措施
| 保护措施 | 合约 | 错误 |
|---|---|---|
| 阻止自我委托 | NavigatorDelegationUtils | SelfDelegationNotAllowed |
| 委托者不能注册为导航者 | NavigatorStakingUtils | DelegatorCannotRegister |
| 公民不能手动投票(分配) | XAllocationVoting | DelegatedToNavigator |
| 公民不能手动投票(治理) | GovernorVotesLogic | DelegatedToNavigator |
| 导航者不能启用自动投票 | XAllocationVoting | NavigatorCannotEnableAutoVoting |
| 公民不能启用自动投票 | XAllocationVoting | DelegatedToNavigator |
| 仅限 VoterRewards 存入费用 | NavigatorRegistry | UnauthorizedCaller |
| 退出/停用时延迟失效 | NavigatorDelegationUtils | _isNavigatorDead() |
关键合约
新增
NavigatorRegistry— UUPS 可升级门面,包含 6 个库:NavigatorStakingUtils、NavigatorDelegationUtils、NavigatorVotingUtils、NavigatorFeeUtils、NavigatorSlashingUtils、NavigatorLifecycleUtils
已修改
VOT3.sol(V2)— 从 NavigatorRegistry 读取委托锁,不重复存储XAllocationVoting.sol(V9)— 面向公民和治理的castNavigatorVote、disableAutoVotingFor、startNewRoundB3TRGovernor.sol(V10)—castNavigatorVote、getActiveProposals、proposalsForRound、relayerRewardsPoolVoterRewards.sol(V7)— 倍数检查点、导航者费用扣除、意向倍数RelayerRewardsPool.sol(V3)— 按用户跟踪跳过情况、setTotalActionsForRoundWithGovernance、activeProposalsForRound
依赖项(只读)
VOT3.sol— 用于最大质押上限的流通供应量VeBetterPassport— 身份验证检查(对已委托的公民在快照时验证;跳过非自然人)RelayerRewardsPool— 中继者注册检查、preferredRelayer、按用户跟踪跳过情况B3TRGovernor— 在轮次开始时查询活跃提案
代码库参考
导航者合约
/packages/contracts/contracts/navigator/NavigatorRegistry.sol— 主门面/packages/contracts/contracts/navigator/libraries/NavigatorStorageTypes.sol— 单一 ERC-7201 命名空间/packages/contracts/contracts/navigator/libraries/NavigatorStakingUtils.sol/packages/contracts/contracts/navigator/libraries/NavigatorDelegationUtils.sol/packages/contracts/contracts/navigator/libraries/NavigatorVotingUtils.sol/packages/contracts/contracts/navigator/libraries/NavigatorFeeUtils.sol/packages/contracts/contracts/navigator/libraries/NavigatorSlashingUtils.sol/packages/contracts/contracts/navigator/libraries/NavigatorLifecycleUtils.sol/packages/contracts/contracts/interfaces/INavigatorRegistry.sol
已修改的合约
/packages/contracts/contracts/x-allocation-voting-governance/XAllocationVoting.sol/packages/contracts/contracts/x-allocation-voting-governance/libraries/VotesUtils.sol/packages/contracts/contracts/governance/B3TRGovernor.sol/packages/contracts/contracts/governance/libraries/GovernorVotesLogic.sol/packages/contracts/contracts/governance/libraries/GovernorStorageTypes.sol/packages/contracts/contracts/governance/libraries/GovernorConfigurator.sol/packages/contracts/contracts/VoterRewards.sol/packages/contracts/contracts/VOT3.sol/packages/contracts/contracts/RelayerRewardsPool.sol
v1 中未包含
- 导航者可配置的费用率(固定为 20%)