关于 auto-voting-relayers
auto-voting-relayers skill 为 VeBetterDAO 的合约、relayer 节点、仪表板和文档提供领域知识,涵盖自动投票与 Navigator 委托投票。它说明投票、奖励领取、费用扣除和 relayer 奖励池的运作流程。
主要功能
- 01说明自动投票与偏好设置
- 02涵盖 Navigator 委托投票
- 03介绍 relayer 注册及奖励分配
- 04详述合约职责与投票流程
- 05说明奖励费用扣除方式
适用场景
- 01开发或维护 VeBetterDAO 自动投票组件
- 02理解 Navigator 公民投票流程
- 03排查 relayer 操作与奖励领取
- 04查阅合约函数及各轮预期操作
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vechain/ vechain-ai-skills --skill auto-voting-relayers安装后在 Claude Code 中输入/auto-voting-relayers确认已加载。
详细介绍
VeBetterDAO 自动投票与中继系统
自动投票与中继生态系统的完整领域知识。本技能为处理系统的任何组件提供背景信息:智能合约、中继节点、中继仪表板或文档。
系统概览
VeBetterDAO 的自动投票系统允许用户自动完成每周的 X Allocation 投票。用户只需选择一次喜爱的应用,并开启自动投票;中继(链下服务)便会处理后续事项:投票、领取奖励,全程免 Gas。中继从奖励池中赚取手续费。代币始终留在用户的钱包中。
借助 Navigators 功能,中继还会为委托给 Navigator 的公民提供服务,代表他们参与分配轮次投票和治理提案投票。计算预期操作总数时,也会将这些公民与自动投票用户一并计入。
架构
Users (toggle auto-voting, set preferences)
Citizens (delegate VOT3 to a navigator)
|
v
Smart Contracts (on-chain logic)
- XAllocationVoting (v9): auto-voting state, vote execution, castNavigatorVote
- B3TRGovernor (v10): governance castNavigatorVote, getActiveProposals
- VoterRewards (v7): reward claiming, fee deduction (relayer + navigator fees)
- RelayerRewardsPool (v3): relayer registration, reward distribution, per-user skip tracking
- NavigatorRegistry: delegation state, preferences, governance decisions
|
v
Relayer Nodes (off-chain execution)
- relayer-node/ (standalone CLI, no monorepo dependency)
- Monitor rounds, batch vote/claim, loop every 5 min
- Serve both auto-voting users and navigator citizens
|
v
Relayer Dashboard (monitoring/analytics)
- apps/relayer-dashboard/ (static Next.js, GitHub Pages)
- Round analytics, relayer stats, ROI tracking
工作原理(非技术版)
用户(自动投票)
- 持有至少 1 个 VOT3,完成至少 3 项可持续行动,并通过 VeBetterPassport 验证
- 最多选择 15 个应用,并开启自动投票
- 下一轮开始生效(不适用于当前轮次)
- 每周:中继代表你投票,并将奖励领取到你的钱包
- 手续费:奖励的 10%(每周最多 100 B3TR),用于支付所有 Gas 费用
- 自动投票处于启用状态期间:不可手动投票或领取奖励
- 手动领取备用方案:如果中继尚未处理,可在轮次结束 5 天后使用
公民(Navigator 委托)
- 将指定数量的 VOT3 委托给 Navigator
- 委托后会自动关闭自动投票
- 中继使用 Navigator 的应用偏好为分配轮次投票
- 中继根据 Navigator 的决定(赞成/反对/弃权)为治理提案投票
- 中继领取奖励——先扣除 Navigator 手续费,再扣除中继手续费
- 剩余奖励将发放至公民的钱包
自动关闭触发条件(自动投票)
- VOT3 低于 1 个
- 所有选中的应用都不再符合资格
- 未达到可持续行动门槛
- 被应用所有者检测为机器人
中继
- 在链上调用 RelayerRewardsPool 的 registerRelayer() 进行注册(任何人均可注册)
- 使用钱包运行 relayer-node(MNEMONIC 或 RELAYER_PRIVATE_KEY)
- 节点会自动发现自动投票用户和 Navigator 委托公民
- 获得加权积分:投票 = 3 分,领取奖励 = 1 分(自动投票和公民投票均适用)
- 为公民投治理提案也可获得投票权重(每次 3 分)
- 完成(或跳过)所有预期操作后,领取奖励池中的按比例分成
应用作为中继
应用可以注册成为中继,请求用户将其设为偏好应用,并运行节点。这样应用无需向 veDelegate 支付投票费用,反而可以赚取中继手续费。重要提示:应用应将自己添加到偏好列表中,而不是替换其他应用。
与 veDelegate 的对比
| 功能 | veDelegate | VeBetterDAO 自动投票 |
|---|---|---|
| X Allocation 投票 | 是 | 是 |
| 治理投票 | 是(始终为“弃权”) | 是(公民投票:采用 Navigator 的决定) |
| 复投(B3TR->VOT3) | 自动 | 手动 |
| 代币托管 | 离开钱包 | 留在钱包中 |
| 中心化程度 | 单一实体 | 多个中继 |
| 应用成本 | 应用向 veDelegate 付费 | 应用赚取手续费 |
veDelegate:docs.vedelegate.vet / github.com/vechain-energy/vedelegate-for-dapps
智能合约详情
XAllocationVoting.sol (v9)
v8 新增自动投票,v9 新增 Navigator 投票。现在采用外部库架构。
存储(位于 AutoVotingLogic / XAllocationVotingStorageTypes 中):
_autoVotingEnabled:按用户记录的检查点状态(更改在下一轮生效)_userVotingPreferences:每位用户的应用 ID 数组(最多 15 个,经过验证且无重复)_totalAutoVotingUsers:记录检查点的总数navigatorRegistry:INavigatorRegistry 引用(V9)
关键函数:
toggleAutoVoting(address user) // Enable/disable
setUserVotingPreferences(bytes32[] memory appIds) // Set apps (1-15)
castVoteOnBehalfOf(address voter, uint256 roundId) // Relayer executes auto-vote
castNavigatorVote(address citizen, uint256 roundId) // Relayer executes citizen vote (vote-or-skip)
disableAutoVotingFor(address user) // Privileged, called by NavigatorRegistry on delegation
getUserVotingPreferences(address) // View preferences
isUserAutoVotingEnabled(address) // Current status
isUserAutoVotingEnabledForRound(address, uint256) // Status at round snapshot
getTotalAutoVotingUsersAtRoundStart() // Count at last emission
getTotalAutoVotingUsersAtTimepoint(uint48) // Historical count
投票执行(castVoteOnBehalfOf — 自动投票用户):
- 验证提前访问资格(窗口期内的已注册中继者)
- 获取用户偏好设置,筛选符合条件的应用
- 将投票权平均分配给符合条件的应用
- 通过内部
_countVote()投票 - 在 RelayerRewardsPool 中登记 VOTE 操作(3 个权重积分)
Navigator 投票执行(castNavigatorVote — 公民):
跳过或投票决策流程(合并到函数中,没有单独的跳过调用):
- 快照时 Navigator 已失效 → 回退并报错
NotDelegatedToNavigator - Navigator 现在已失效(快照后退出/停用)→ 通过
pool.reduceUserAllocationVote跳过,发出NavigatorVoteSkipped - Navigator 有效 + 已设置偏好 → 使用 Navigator 自定义百分比(基点,总和必须为 10000)正常投票,并登记
RelayerAction.VOTE - Navigator 有效 + 未设置偏好 + 已到跳过窗口期 → 跳过,发出
NavigatorVoteSkipped - Navigator 有效 + 未设置偏好 + 未到跳过窗口期 → 回退并报错
SkipWindowNotReached
跳过窗口期:XAllocationVoting.citizenSkipWindowBlocks() — 可通过存储配置(V9 迁移),默认约 720 个区块(距本轮截止时间约 2 小时)。通过 getter 从链上读取;不要硬编码——治理可以通过 setCitizenSkipWindowBlocks 更新该值,且不同网络的值可能不同。
startNewRound() — 设置预期操作:
计算本轮的预期操作:
allocationUsers = autoVotingUsers + totalDelegatedCitizensgovernanceUsers = totalDelegatedCitizens(仅公民——中继者不会代表自动投票用户进行治理投票)- 获取
governor.getActiveProposals() - 调用
pool.setTotalActionsForRoundWithGovernance(roundId, allocationUsers, governanceUsers, activeProposals)
B3TRGovernor.sol (v10)
v10 新增 Navigator 治理投票和中继者集成。
castNavigatorVote(proposalId, citizen) — 公民治理投票:
与 XAllocationVoting 的跳过或投票模式相同:
- 快照时 Navigator 已失效 → 回退并报错
NotDelegatedToNavigator - Navigator 现在已失效 → 通过
pool.reduceUserGovernanceVote跳过,发出NavigatorGovernanceVoteSkipped - Navigator 有效 + 已设置决策 → 正常投票,并在 RelayerRewardsPool 中登记
RelayerAction.VOTE - Navigator 有效 + 未设置决策 + 已到跳过窗口期 → 跳过,发出
NavigatorGovernanceVoteSkipped - Navigator 有效 + 未设置决策 + 未到跳过窗口期 → 回退并报错
GovernanceSkipWindowNotReached
跳过窗口期:B3TRGovernor.governanceSkipWindowBlocks() — 可通过存储配置(V10 迁移),默认约 720 个区块(距提案截止时间约 2 小时)。通过 getter 从链上读取;不要硬编码——治理可以通过 setGovernanceSkipWindowBlocks 更新该值,且不同网络的值可能不同。
映射 Navigator 决策:1=反对,2=赞成,3=弃权 → governor 支持值 0、1、2。 奖励采用意图乘数。
getActiveProposals() — 返回当前活跃的提案 ID(从 proposalsForRound 映射中过滤,该映射在创建提案时填充)。供 XAllocationVoting.startNewRound 用于计算治理预期操作。
VoterRewards.sol (v7)
V6 添加了中继者费用集成。V7 添加了导航员费用扣除和奖励乘数。
V7 存储新增项: navigatorRegistry
claimReward(uint256 cycle, address voter) 中的费用流程:
- 检查用户在轮次开始时是否启用了自动投票或委托(已记录检查点)
- 计算原始奖励(投票奖励 + GM 奖励)
- 如果是公民(已委托):先从总奖励中扣除导航员费用 → 存入 NavigatorRegistry 费用托管账户
- 如果启用了自动投票或已委托:从剩余金额中扣除中继者费用 →
RelayerRewardsPool.deposit(fee, cycle) registerRelayerAction(msg.sender, voter, cycle, CLAIM)— 为调用者记入 1 个权重点- 将净奖励转入投票者钱包
费用公式:
Navigator fee = gross * navigatorFeePercentage / 10000 (citizens only, goes to NavigatorRegistry escrow)
Relayer fee = min((gross - navigatorFee) * relayerFeePercentage / 100, feeCap) (auto-voters AND citizens)
重要: 调用 claimReward() 的 msg.sender 就是获得 CLAIM 操作记账的中继者。
提前访问: 在此窗口期间,如果调用者是投票者本人或不是已注册的中继者,则调用会回退。自动投票用户和公民均适用。
RelayerRewardsPool.sol (v3)
管理注册、操作跟踪、奖励分配和逐用户跳过跟踪。
核心存储:
totalRewards[roundId] // Pool amount (funded by fees)
relayerWeightedActions[roundId][relayer] // Per-relayer weighted work
totalWeightedActions[roundId] // Expected weighted total
completedWeightedActions[roundId] // Completed weighted total
registeredRelayers[address] // Registration mapping
relayerAddresses[] // All registered addresses
voteWeight = 3 // Points per vote action
claimWeight = 1 // Points per claim action
earlyAccessBlocks = 432,000 // ~5 days on VeChain
relayerFeePercentage = 10 // 10%
feeCap = 100 ether // 100 B3TR
V3 存储新增项:
activeProposalsForRound[roundId] // Cached governance proposal IDs
userAllocationVoteReduced[roundId][user] // Per-user allocation skip flag
userGovernanceVoteReduced[roundId][user][proposalId] // Per-user/proposal governance skip flag
userClaimReduced[roundId][user] // Per-user claim auto-reduction flag
奖励公式:
relayerShare = (relayerWeightedActions / completedWeightedActions) * totalRewards
可领取条件: isRewardClaimable(roundId) 要求:
- 轮次已结束(
emissions.isCycleEnded(roundId)) - 所有工作均已完成(
completedWeightedActions >= totalWeightedActions) - 逐用户跳过会自动减少
totalWeightedActions,因此被跳过的公民不会阻碍奖励领取
注册(任何人均可注册):
registerRelayer()— 自行注册unregisterRelayer(address)— 可由管理员或中继者本人调用
预期操作设置:
setTotalActionsForRound(roundId, userCount)— 旧接口,调用setTotalActionsForRoundWithGovernance,并将 governanceUsers 设为 0setTotalActionsForRoundWithGovernance(roundId, allocationUsers, governanceUsers, activeProposalIds)— V3:设置预期操作数。总数 =allocationUsers * 2(投票 + 领取)+governanceUsers * activeProposals(治理投票)。缓存 activeProposalIds
操作数减少:
reduceExpectedActionsForRound(roundId, userCount)— 批量减少不符合资格的自动投票用户对应的操作数reduceUserAllocationVote(roundId, user)— 逐用户跳过分配投票。如果该用户的所有投票都被跳过,则自动减少领取操作数reduceUserGovernanceVote(roundId, user, proposalId)— 逐用户、逐提案跳过治理投票。采用相同的自动减少逻辑- 防止重复跳过:如果同一用户、轮次或提案的操作数已减少,则调用会回退
操作登记:
registerRelayerAction(relayer, voter, roundId, action)— 记录投票或领取工作deposit(amount, roundId)— 将费用扣款存入资金池
提前访问:
- 投票窗口:
roundSnapshot + earlyAccessBlocks - 领取窗口:
roundDeadline + earlyAccessBlocks - 窗口期间:仅已注册的中继者可操作,用户不能自行操作
- 窗口结束后:任何人均可操作
每轮中继者生命周期
Round N: Users enable auto-voting / citizens delegate to navigators (checkpointed)
Round N+1:
1. startNewRound() - snapshot locks status
- allocationUsers = autoVotingUsers + totalDelegatedCitizens
- governanceUsers = totalDelegatedCitizens
- fetches activeProposals from B3TRGovernor
- calls setTotalActionsForRoundWithGovernance(roundId, allocationUsers, governanceUsers, activeProposals)
2. ALLOCATION VOTING:
a. Relayers call castVoteOnBehalfOf(voter, roundId) for auto-voting users
- Ineligible users: reduceExpectedActionsForRound()
- Each successful vote: +3 weighted points
b. Relayers call castNavigatorVote(citizen, roundId) for citizens
- Navigator dead/no-prefs + skip window reached: skip (reduceUserAllocationVote)
- Navigator alive + prefs set: vote, +3 weighted points
3. GOVERNANCE VOTING (concurrent with allocation):
For each active proposal:
Relayers call B3TRGovernor.castNavigatorVote(proposalId, citizen) for each citizen
- Navigator dead/no-decision + skip window reached: skip (reduceUserGovernanceVote)
- Navigator alive + decision set: vote, +3 weighted points
4. Round ends (deadline block)
5. CLAIMS:
Relayers call VoterRewards.claimReward(cycle, user) for auto-voters and citizens
- Navigator fee deducted first (citizens only), then relayer fee
- Each successful claim: +1 weighted point
- If all votes for a citizen were skipped, claim is auto-reduced (no claim needed)
6. All expected actions completed (or reduced via skips) -> pool unlocks
7. Relayers call RelayerRewardsPool.claimRewards(roundId)
导航员公民集成
中继者服务于委托给导航员的公民,以及启用了自动投票的用户。这是在现有自动投票逻辑基础上的新增功能。
与自动投票的主要区别
| 方面 | 自动投票用户 | Navigator 公民 |
|---|---|---|
| 投票函数(分配) | castVoteOnBehalfOf(voter, roundId) | castNavigatorVote(citizen, roundId) |
| 投票函数(治理) | 不适用 | B3TRGovernor.castNavigatorVote(proposalId, citizen) |
| 发现方式 | AutoVotingToggled 事件 | DelegationCreated/Removed 事件 |
| 偏好设置 | 用户自己的应用列表(均分) | Navigator 的 AllocationPreferencesSet(自定义百分比) |
| 治理 | 不适用 | Navigator 的 GovernanceDecisionSet |
| 投票权 | 快照时的全部 VOT3 余额 | 快照时委托的数量(已记录检查点) |
| 身份验证 | 是 | 否 |
| 是否计入预期操作? | 是(仅分配) | 是(分配 + 治理) |
| 跳过机制 | reduceExpectedActionsForRound(批量) | 按用户跳过,并设有跳过窗口 |
| 领取函数 | claimReward(cycle, voter) | 相同——claimReward(cycle, citizen) |
| 领取时的费用 | 仅中继者费用 | 先扣 Navigator 费用,再扣中继者费用 |
计入预期操作的公民
公民会计入预期操作。每轮开始时:
allocationUsers = autoVotingUsers + totalDelegatedCitizens(用于分配投票 + 领取)governanceUsers = totalDelegatedCitizens(用于治理投票——仅公民,不包括自动投票用户)governanceUsers单独计算,因为中继者不会替自动投票用户投治理票
跳过窗口(可按网络通过存储配置,默认为截止时间前约 720 个区块 / 约 2 小时——从 XAllocationVoting.citizenSkipWindowBlocks() / B3TRGovernor.governanceSkipWindowBlocks() 读取)和按用户跟踪跳过状态可避免死锁:如果 Navigator 未设置偏好,中继者可在跳过窗口开启后跳过该公民的投票,按比例减少预期操作。
跳过或投票流程(适用于分配和治理)
castNavigatorVote 在同一个函数中处理投票和跳过:
- 快照时 Navigator 已失效 → 回滚(快照时该公民未委托)
- Navigator 目前已失效(自快照后退出 / 停用)→ 立即跳过,并减少预期操作
- Navigator 仍有效,且已设置偏好 / 决策 → 正常投票
- Navigator 仍有效,尚未设置偏好 / 决策,且已到达跳过窗口 → 跳过
- Navigator 仍有效,尚未设置偏好 / 决策,且尚未到达跳过窗口 → 回滚
SkipWindowNotReached/GovernanceSkipWindowNotReached(中继者稍后重试)
按用户跟踪跳过状态
跳过某位公民的投票时:
- 分配:
reduceUserAllocationVote(roundId, citizen)— 减少预期操作 - 治理:
reduceUserGovernanceVote(roundId, citizen, proposalId)— 按提案分别减少 - 某位公民的所有投票(分配 + 所有治理提案)都被跳过后,领取操作会自动减少
- 每次跳过均按用户、轮次和提案分别记录,以防重复减少
治理投票登记
B3TRGovernor.castNavigatorVote 会针对每次治理投票,在 RelayerRewardsPool 中登记 RelayerAction.VOTE。中继者每次治理投票可获得与分配投票相同的 3 个权重积分。
公民费用扣除顺序
- Navigator 费用:从奖励总额中扣除(转入 NavigatorRegistry 费用托管账户)
- 中继者费用:从剩余金额中扣除(百分比和上限与自动投票相同,转入 RelayerRewardsPool)
- 公民获得剩余金额
委托时禁用自动投票
用户委托给 Navigator 时,其自动投票会通过 XAllocationVoting.disableAutoVotingFor(citizen) 自动禁用,以防止重复计数和投票路径冲突。取消委托后,用户必须手动重新启用自动投票。
首选中继者
- 公民通过
relayerRewardsPool.setPreferredRelayer(relayer)手动设置preferredRelayer - 在早期访问期间,如果已设置首选中继者且该中继者已注册,则只有该中继者可以代表公民操作
- 委托时不会自动设置——公民必须手动设置
- 公民可随时更改或清除首选中继者
中继节点 (relayer-node/)
独立的 CLI 工具。无单仓库依赖。
依赖项: @vechain/sdk-core、@vechain/sdk-network、@vechain/vebetterdao-contracts
relayer-node/src/
index.ts # Entry, env parsing, main loop, SIGINT
config.ts # Mainnet + testnet-staging addresses
contracts.ts # 26 view functions + event pagination
relayer.ts # Batch vote/claim with isolation/retry
display.ts # Terminal UI (box drawing + chalk)
types.ts # Shared interfaces
环境变量: MNEMONIC / RELAYER_PRIVATE_KEY、RELAYER_NETWORK、RUN_ONCE、DRY_RUN
周期: 从事件中发现用户 -> 筛选已投票用户 -> 批量调用 castVoteOnBehalfOf + castNavigatorVote -> 批量调用治理 castNavigatorVote -> 批量调用 claimReward -> 每 5 分钟循环一次
中继仪表板 (apps/relayer-dashboard/)
静态 Next.js 14(输出:“export”)、Chakra UI v3、VeChain Kit、Recharts。托管于 GitHub Pages 的 /b3tr 路径下。
数据: 静态 report.json(每小时由 GH Action 临时生成)+ 通过 useCallClause 读取链上数据
页面(基于状态的导航,非文件路由):
- 首页:StatsCards(2x2)、RoundsChart、RoundsList、信息卡片
- 我的中继器:ConnectedWallet 视图
- 信息:BecomeRelayer + AppsAsRelayers
- 轮次详情:
/round?roundId=X- 双栏布局,包含摘要/操作/财务信息
钩子:
contracts.ts- 来自@repo/config的 ABI + 地址useCurrentRoundId- XAllocationVoting.currentRoundId()useTotalAutoVotingUsers- getTotalAutoVotingUsersAtTimepoint()useRegisteredRelayers- getRegisteredRelayers()useRoundRewardStatus- isRewardClaimable() + getTotalRewards()useReportData- 获取 /data/report.jsonuseB3trToVthoRate- 预言机汇率
命令: yarn relayer:dev:staging、yarn relayer:dev:mainnet、yarn relayer:build:staging、yarn relayer:build:mainnet
Gas 成本分析
| 操作 | Gas | VTHO | 等值 B3TR |
|---|---|---|---|
| 投票(5-8 个应用) | ~441K | ~4.41 | ~0.075 |
| 领取 | ~208K | ~2.08 | ~0.035 |
| 每位用户/每轮总计 | ~6.49 | ~0.11 |
平均用户:约 10.8k-22.6k VOT3,每轮可获得约 90-190 B3TR。 按 10% 的费用计算:每位用户约有 9-19 B3TR 进入奖励池。中继器成本:0.11 B3TR。利润:每位用户约 8.9-18.9 B3TR。
外部资源
- 文档:https://docs.vebetterdao.org/vebetter/automation
- 治理提案:https://governance.vebetterdao.org/proposals/93450486232994296830196736391400835825360450263361422145364815974754963306849
- Discourse:https://vechain.discourse.group/t/vebetterdao-proposal-auto-voting-for-x-allocation-with-gasless-voting-and-relayer-rewards/559
- NPM:
@vechain/vebetterdao-contracts - 合约源码:https://github.com/vechain/vebetterdao-contracts