本文档介绍了EigenLayer协议中RewardsCoordinator合约的功能和机制。该合约负责接收来自AVS的ERC20奖励,分配给他们的Operators和delegated Stakers,并允许Stakers和Operators申领他们的累计收益。文档涵盖了提交奖励请求、分配和申领奖励、系统配置、奖励 Merkle 树结构和链下计算等关键概念和流程。
| 文件 | 类型 | 代理 | 
|---|---|---|
RewardsCoordinator.sol | 
单例 | 透明代理 | 
<!-- RewardsCoordinator 合约的主要功能是 (i) 接受 AVS(主动验证服务)向其运营商和委托的 Staker 在给定的时间范围内提供的 ERC20 奖励;(ii) 使协议能够在指定的时间范围内向所有 staker 提供 ERC20 代币;(iii) 允许 staker 和运营商领取他们积累的收益。 -->
RewardsCoordinator 接受来自 AVS 的 ERC20,以及向运营商发出的奖励提交请求,这些运营商在指定的时间范围内已在核心 AllocationManager 合约中向 AVS 注册。
有两种形式的奖励:
链下,受信任的 rewards updater 根据奖励提交的时间范围(取决于奖励类型)计算奖励分配。对于 v1 奖励提交,它基于:(i)每个运营商的 Staker 的相对 stake 权重和(ii)分配给运营商的默认拆分。对于 v2 奖励提交,它基于:(i)AVS 的自定义奖励逻辑,(ii)每个运营商的拆分。
链上,rewards updater 向 RewardsCoordinator 发送每个收益者累积收益的 Merkle 根。收益者向 RewardsCoordinator 提供 Merkle 证明,以根据这些根来领取奖励。
典型的用户流程如下:
RewardsCoordinator 合约提交 rewards submission,可以是 RewardsSubmission (v1) 或 OperatorDirectedRewardsSubmission (v2),其中指定时间范围(startTimestamp 和 duration)和 token。rewards submission 还指定策略的相对奖励权重(即“将 80% 分配给 X 策略的持有者,将 20% 分配给 Y 策略的持有者”)。
amount,而 v2 奖励 指定每个运营商的奖励(由于可自定义的奖励逻辑)。v2 奖励 还允许添加对 rewards submission 目的的 description。DistributionRoot)由 rewards updater 发布在链上。DistributionRoot 在某个全局配置的 activationDelay 之后变为可声明状态。DistributionRoot 的 Merkle 证明来申领其累积的收益。整个流程将定期重复,因为 AVS 提交 rewards submission,提交 DistributionRoot,并且 Staker/运营商申领其累积的收益。请注意,DistributionRoot 包含 cumulative earnings,这意味着 Staker/运营商不需要针对每个根进行申领 - 只需针对最新的根进行申领即可申领任何尚未申领的内容。
注意:在使用不严格符合 ERC20 标准的奖励代币时,请务必谨慎。如果你的代币不符合 ERC20 规范,请进行 DYOR。 需要注意的具体事项包括(但不限于):异国情调的 rebase 代币、transfer 收费代币、支持重入行为的代币(如 ERC-777)以及其他非标准 ERC20 衍生品。
本文档按照以下主题组织(单击每个主题以转到相关部分):
DistributionRoot[] public distributionRoots:
distributionRoots 存储由 rewards updater 提交的历史奖励 Merkle 树根。对于每个收益者,奖励 Merkle 树存储每个 ERC20 奖励代币的累积收益。有关 Merkle 树结构的更多详细信息,请参见下面的 奖励 Merkle 树结构。mapping(address => address) public claimerFor: earner => claimer
processClaim 代表他们申领奖励。如果在 claimerFor 中未设置 claimer,则 earner 必须自己调用 processClaim。processClaim 时指定接收者。mapping(address => mapping(IERC20 => uint256)) public cumulativeClaimed: earner => token => 累计申领的总金额
uint16 public defaultOperatorSplitBips:链下使用,rewards updater 用于计算运营商特定奖励的拆分。
1000。mapping(address => mapping(address => OperatorSplit)) internal _operatorAVSSplitBips: operator => AVS => OperatorSplit
OperatorDirectedRewardsSubmission 指定其给定 AVS 的自定义拆分,其中 Staker 接收剩余金额的相对比例(按 stake 权重)。mapping(address => OperatorSplit) internal _operatorPISplitBips: operator => OperatorSplit
mapping(address operator => mapping(bytes32 operatorSetKey => OperatorSplit split)) internal _operatorSetSplitBips: operator => 运营商集合 Key => OperatorSplit
RewardsCoordinator 的合约实体。
ServiceManager 合约,它与 EigenLayer 协议进行交互。请参阅此处的 ServiceManagerBase 文档:eigenlayer-middleware/docs/ServiceManagerBase.md。RewardsSubmission 和 v2 OperatorDirectedRewardsSubmission 类型。_checkClaim(RewardsMerkleClaim calldata claim, DistributionRoot memory root) 检查声明是否包含在 DistributionRoot 的 Merkle 树中
_verifyEarnerClaimProof 时恢复的 earner 证明_verifyTokenClaimProof 时恢复的任何 token 证明奖励最初提交给合约,以便通过以下函数分配给运营商和 Staker:
RewardsCoordinator.createAVSRewardsSubmissionRewardsCoordinator.createRewardsForAllSubmissionRewardsCoordinator.createRewardsForAllEarnersRewardsCoordinator.createOperatorDirectedAVSRewardsSubmissionRewardsCoordinator.createOperatorDirectedOperatorSetRewardsSubmissioncreateAVSRewardsSubmissionfunction createAVSRewardsSubmission(
    RewardsSubmission[] calldata RewardsSubmissions
)
    external
    onlyWhenNotPaused(PAUSED_AVS_REWARDS_SUBMISSION)
    nonReentrant
由 AVS 调用,以提交要分配给所有已注册运营商(以及委托给每个运营商的 Staker)的 RewardsSubmission 列表。RewardsSubmission 包含以下字段:
IERC20 token:用于 rewards submission 的 ERC20 代币的地址uint256 amount:要转移到 RewardsCoordinator 的 token 金额uint32 startTimestamp:提交时间范围的开始时间uint32 duration:提交时间范围的持续时间,以秒为单位StrategyAndMultiplier[] strategiesAndMultipliers:StrategyAndMultiplier 结构的数组,用于定义 AVS 认为有资格获得奖励的 EigenLayer 策略的线性组合。每个 StrategyAndMultiplier 包含:
IStrategy strategy:策略的地址,用于对 Staker/运营商的相对份额进行加权,以确定其奖励金额uint96 multiplier:线性组合中策略的相对权重。(建议在此处使用 1e18 作为基本乘数,并相应地调整相对权重)对于每个提交的 RewardsSubmission,此方法执行 transferFrom 以将指定的奖励 token 和 amount 从调用者转移到 RewardsCoordinator。
资格:
为了有资格申领 createAVSRewardsSubmission 奖励,运营商应在奖励发放的时间段内在 AVSDirectory 中为 AVS 注册(请参阅 AVSDirectory.registerOperatorToAVS 的文档)。如果运营商没有资格,则委托给运营商的任何 Staker 也没有资格。
此外,AVS ServiceManager 合约还必须实现接口 ServiceManager.getRestakeableStrategies 和 ServiceManager.getOperatorRestakedStrategies,以便成功分配其奖励,因为这些视图函数在链下作为奖励分配过程的一部分被调用。这默认在 ServiceManagerBase 合约中实现,但如果未从基本合约继承,则需要注意。
请参阅此处的 ServiceManagerBase 抽象合约:ServiceManagerBase.sol
奖励分配:
AVS 的运营商和委托的 Staker 之间的奖励分配在链下确定,使用 RewardsSubmission 结构中提供的策略和乘数以及这些已定义策略在 RewardsSubmission 时间范围内的实际份额。这些份额从 EigenPodManager(在 Beacon Chain ETH 策略的情况下)或 StrategyManager 中读取,对于任何其他策略。请注意,Staker 的份额专门用于确定奖励分配;运营商根据他们自己的 deposited 份额和配置的 defaultOperatorSplitBips 的组合获得收益。
效果:
RewardsSubmission 元素
amount 的 token 从 msg.sender (AVS) 转移到 RewardsCoordinatorRewardsSubmission 结构进行哈希处理,以创建唯一的奖励哈希,并将此值设置为 isAVSRewardsSubmissionHash 映射中的 truesubmissionNonce[msg.sender]AVSRewardsSubmissionCreated 事件要求:
PAUSED_AVS_REWARDS_SUBMISSIONRewardsSubmission 元素
_validateRewardsSubmission() 的要求
rewardsSubmission.strategiesAndMultipliers.length > 0rewardsSubmission.amount > 0rewardsSubmission.amount <= MAX_REWARDS_AMOUNTrewardsSubmission.duration <= MAX_REWARDS_DURATIONrewardsSubmission.duration % calculationIntervalSeconds == 0rewardsSubmission.duration > 0rewardsSubmission.startTimestamp % calculationIntervalSeconds == 0block.timestamp - MAX_RETROACTIVE_LENGTH <= rewardsSubmission.startTimestampGENESIS_REWARDS_TIMESTAMP <= rewardsSubmission.startTimestamprewardsSubmission.startTimestamp <= block.timestamp + MAX_FUTURE_LENGTHrewardsSubmission.strategiesAndMultipliers 的要求
strategy 都被列入 StrategyManager 的存款白名单,或者是 beaconChainETHStrategyrewardsSubmission.strategiesAndMultipliers 按升序策略地址排序,以防止重复策略transferFrom 必须 成功地将 msg.sender 的 amount 的 token 转移到 RewardsCoordinator下面的文本图更好地可视化了 RewardsSubmission 的有效开始时间戳
RewardsSubmission 有效 startTimestamp 的滑动窗口
场景 A:GENESIS_REWARDS_TIMESTAMP 在范围内
        <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH--->
            <--------------------startTimestamp 的有效范围------------------------>
            ^
        GENESIS_REWARDS_TIMESTAMP
场景 B:GENESIS_REWARDS_TIMESTAMP 超出范围
        <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH--->
        <------------------------startTimestamp 的有效范围------------------------>
    ^
GENESIS_REWARDS_TIMESTAMP
createRewardsForAllSubmissionfunction createRewardsForAllSubmission(
    RewardsSubmission[] calldata RewardsSubmissions
)
    external
    onlyWhenNotPaused(PAUSED_REWARDS_FOR_ALL_SUBMISSION)
    onlyRewardsForAllSubmitter
    nonReentrant
此方法在功能上与上面的 createAVSRewardsSubmission 相同,除了:
效果:
createAVSRewardsSubmission。唯一的区别是:
isRewardsSubmissionForAllHash 映射中RewardsSubmissionForAllCreated 事件要求:
createAVSRewardsSubmission。唯一的区别是,每个计算出的 rewards submission 哈希 必须 尚未存在于 isRewardsSubmissionForAllHash 映射中。createRewardsForAllEarnersfunction createRewardsForAllEarners(
    RewardsSubmission[] calldata RewardsSubmissions
)
    external
    onlyWhenNotPaused(PAUSED_REWARDS_FOR_ALL_SUBMISSION)
    onlyRewardsForAllSubmitter
    nonReentrant
此方法在功能上与上面的 createAVSRewardsSubmission 相同,除了:
效果:
createAVSRewardsSubmission。唯一的区别是:
isRewardsSubmissionForAllEarnersHash 映射中RewardsSubmissionForAllEarnersCreated 事件要求:
createAVSRewardsSubmission。唯一的区别是,每个计算出的 rewards submission 哈希 必须 尚未存在于 isRewardsSubmissionForAllEarnersHash 映射中。createOperatorDirectedAVSRewardsSubmissionfunction createOperatorDirectedAVSRewardsSubmission(
    address avs,
    OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_DIRECTED_AVS_REWARDS_SUBMISSION)
    checkCanCall(avs)
    nonReentrant
AVS 可以通过调用 createOperatorDirectedAVSRewardsSubmission() 来提交 Rewards v2 提交,其中包含任何自定义链上或链下逻辑,以确定其奖励分配策略。这可以是特定于运营商在特定时间段内执行的工作的自定义,可以是一个 flat 奖励率,或者基于 AVS 经济模型的其他一些结构。这将使 AVS 能够灵活地奖励不同运营商的绩效和其他变量,同时保持对委托给同一运营商和策略的 Staker 的相同易于计算的奖励率。AVS 可以提交以不同代币计价的多个基于绩效的奖励,从而实现更大的灵活性。
效果:
OperatorDirectedRewardsSubmission 元素
amount 的 token 从 msg.sender 转移到 RewardsCoordinatorAVS、nonce 和 OperatorDirectedRewardsSubmission 结构进行哈希处理,以创建唯一的奖励哈希,并将此值设置为 isOperatorDirectedAVSRewardsSubmissionHash 映射中的 truesubmissionNonce[avs]OperatorDirectedAVSRewardsSubmissionCreated 事件要求:
PAUSED_OPERATOR_DIRECTED_AVS_REWARDS_SUBMISSIONPermissionController.md)OperatorDirectedRewardsSubmission 元素:
_validateOperatorDirectedRewardsSubmission() 的要求operatorDirectedRewardsSubmission.strategiesAndMultipliers.length > 0operatorDirectedRewardsSubmission.duration <= MAX_REWARDS_DURATIONoperatorDirectedRewardsSubmission.duration % calculationIntervalSeconds == 0operatorDirectedRewardsSubmission.duration > 0operatorDirectedRewardsSubmission.startTimestamp % calculationIntervalSeconds == 0block.timestamp - MAX_RETROACTIVE_LENGTH <= operatorDirectedRewardsSubmission.startTimestampGENESIS_REWARDS_TIMESTAMP <= operatorDirectedRewardsSubmission.startTimestampoperatorDirectedRewardsSubmission.strategiesAndMultipliers 元素:
strategy 都被列入 StrategyManager 的存款白名单,或者是 beaconChainETHStrategyrewardsSubmission.strategiesAndMultipliers 按升序策略地址排序,以防止重复策略operatorDirectedRewardsSubmission.operatorRewards.length > 0operatorDirectedRewardsSubmission.operatorRewards 元素:
operatorReward.operator != address(0)currOperatorAddress < operatorReward.operatoroperatorReward.amount > 0totalAmount <= MAX_REWARDS_AMOUNT,其中 totalAmount 是每个 operatorReward.amount 的总和operatorDirectedRewardsSubmission.startTimestamp + operatorDirectedRewardsSubmission.duration < block.timestamp,强制执行严格的追溯奖励提交transferFrom 必须 成功地将 msg.sender 的 amount 的 token 转移到 RewardsCoordinatorcreateOperatorDirectedOperatorSetRewardsSubmissionfunction createOperatorDirectedOperatorSetRewardsSubmission(
    OperatorSet calldata operatorSet,
    OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION)
    checkCanCall(operatorSet.avs)
    nonReentrant
此函数允许 AVS 向特定的运营商集合提交奖励,从而根据分配给特定运营商集合的任务或任何其他自定义 AVS 逻辑,实现更精细的目标奖励。其功能几乎与 createOperatorDirectedAVSRewardsSubmission 相同,除了某些特定于运营商集合的要求、状态变量和事件。
请注意,AVS 必须指定一个已向 AVS 注册的运营商集合;换句话说,属于不同 AVS 的运营商集合或未注册的运营商集合将导致此函数恢复。
另请注意,使用持续时间延长到 slashing 发布之前的 duration 发出此奖励提交将导致那些在 slashing 发布之前的奖励快照退还给 AVS(这在 Sidecar 奖励计算逻辑中处理)。
效果:
createOperatorDirectedAVSRewardsSubmission。唯一的区别是:
isOperatorDirectedOperatorSetRewardsSubmissionHash 映射中OperatorDirectedOperatorSetRewardsSubmissionCreated 事件要求:
createOperatorDirectedAVSRewardsSubmission。唯一的区别是:
allocationManager.isOperatorSet(),operatorSet 必须 是给定 AVS 的 已注册运营商集合PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSIONrewards updater 计算奖励分配,并通过以下函数提交可申领的根:submitRoot。他们还可以禁用尚未激活的根:
收益者可以使用以下函数配置和申领这些奖励:
submitRootfunction submitRoot(
    bytes32 root,
    uint32 rewardsCalculationEndTimestamp
)
    external
    onlyWhenNotPaused(PAUSED_SUBMIT_DISABLE_ROOTS)
    onlyRewardsUpdater
仅由 rewardsUpdater 地址调用,以在 RewardsCoordinator 中创建一个新的 DistributionRoot。DistributionRoot 结构包含以下字段:
bytes32 root:奖励 Merkle 树的 Merkle 根uint32 rewardsCalculationEndTimestamp:提交 DistributionRoot 的奖励时间范围的结束时间uint32 activatedAt:激活 DistributionRoot 并可以针对其进行申领的时间戳(以秒为单位)submitRoot 将新的 DistributionRoot 推送到 distributionRoots 数组。DistributionRoot.activatedAt 时间戳设置为 block.timestamp + activationDelay(),以便在可以处理申领之前允许延迟。一旦此延迟过去,该根可用于验证向 Staker/运营商发放的奖励的 Merkle 证明。
效果:
DistributionRoot 推送到 distributionRoots 数组currRewardsCalculationEndTimestamp 设置为参数 rewardsCalculationEndTimestampDistributionRootSubmitted 事件要求:
PAUSED_SUBMIT_DISABLE_ROOTSmsg.sender 必须 是 rewardsUpdaterrewardsCalculationEndTimestamp > currRewardsCalculationEndTimestamprewardsCalculationEndTimestamp < block.timestampdisableRootfunction disableRoot(
    uint32 rootIndex
)
    external
    onlyWhenNotPaused(PAUSED_SUBMIT_DISABLE_ROOTS)
    onlyRewardsUpdater
仅由 rewardsUpdater 地址调用,以禁用 RewardsCoordinator 中尚未激活的挂起的 DistributionRoot(尚未达到 activatedAt 时间戳)。一旦达到 activatedAt 时间戳,根将无法再被禁用,并被视为最终确定且可针对其进行申领。
这是为了添加额外的措施来防止发布到合约的无效根,无论是由于错误还是可能发布的恶意根。
效果:
DistributionRoot 的 disabled 字段设置为 TrueprocessClaim 中针对 DistributionRoot 进行申领DistributionRootDisabled 事件要求:
PAUSED_SUBMIT_DISABLE_ROOTSmsg.sender 必须 是 rewardsUpdaterrootIndex < distributionRoots.lengthroot.disabled == Falseblock.timestamp < root.activatedAtrewardsCalculationEndTimestamp < block.timestampsetClaimerForfunction setClaimerFor(address claimer) external
由 earner (Staker/运营商) 调用,以设置可以代表他们调用 processClaim 的申领人地址。如果未设置申领人(claimerFor[earner] == address(0)),则 earner 本身可以直接调用 processClaim。
效果:
claimerFor[msg.sender] 设置为输入参数 claimerClaimerForSet 事件processClaimfunction processClaim(
    RewardsMerkleClaim calldata claim,
    address recipient
)
    external
    onlyWhenNotPaused(PAUSED_PROCESS_CLAIM)
    nonReentrant
由 earner (Staker/运营商) 调用,以通过提供针对已发布的 DistributionRoot 的 Merkle 证明来申领其累积的收益。如果 earner 已配置申领人(通过 setClaimerFor),则申领人必须改为调用此方法。
RewardsMerkleClaim 结构包含以下字段(有关更多详细信息,请参见 奖励 Merkle 树结构):
uint32 rootIndex:distributionRoots 中 DistributionRoot 的索引,用于证明uint32 earnerIndex:Merkle 树中 earner 的帐户根的索引bytes earnerTreeProof:针对 DistributionRoot 的 earner 的 EarnerTreeMerkleLeaf 的证明EarnerTreeMerkleLeaf earnerLeaf:earner 的地址和代币子树根
address earner:earner 的地址bytes32 earnerTokenRoot:earner 的代币 Merkle 树的 Merkle 根uint32[] tokenIndices:earner 的子树中代币叶子的索引bytes[] tokenTreeProofs:针对 earner 的 earnerTokenRoot 的代币叶子的证明TokenTreeMerkleLeaf[] tokenLeaves:要申领的代币叶子:
IERC20 token:要申领的 ERC20 代币uint256 amount:要申领的 ERC20 代币数量processClaim 是一个简单的包装函数,它调用内部函数 _processClaim,该函数包含所有必要的逻辑。
_processClaim 将首先调用 _checkClaim 以验证针对指定 rootIndex 处的 DistributionRoot 的 Merkle 证明。这是通过首先对 DistributionRoot 执行 earner 的 EarnerTreeMerkleLeaf 的 Merkle 证明验证,然后对于每个 tokenIndex,验证每个代币叶子是否与 earner 的 earnerTokenRoot 对齐来完成的。
如果 earner 未设置申领人,则调用者必须是 claimerFor 映射中设置的申领人地址,或者 earner 本身。
在验证了申领之后,对于每个代币叶子,计算 Merkle 树中的累积收入与合约中上次存储的先前申领总额之间的差异,并从 RewardsCoordinator 合约转移到地址 recipient。
效果:
claim.tokenLeaves:
uint claimAmount = tokenLeaf.cumulativeEarnings - cumulativeClaimed[earner][tokenLeaf.token]
tokenLeaf.token 的 claimAmount 转移到指定的 recipientcumulativeClaimed 映射RewardsClaimed 事件要求:
PAUSED_PROCESS_CLAIMclaim 必须具有针对有效 DistributionRoot 的有效证明:
claim.rootIndex 给定的 DistributionRoot,根 必须 处于活动状态(block.timestamp >= root.activatedAt)claim.tokenIndices 必须 等于 claim.TokenTreeProofs 和 claim.tokenLeaves 的长度claim.earnerTreeProof 必须 针对 DistributionRoot 验证 claim.earnerLeafclaim.tokenIndices[i]:
claim.tokenTreeProofs[i] 必须 针对 claim.earnerLeaf.earnerTokenRoot 验证 claim.tokenLeaves[i]claim.earnerLeaf.earner 中指定的 earner 在 claimerFor[earner] 中有指定的 claimer,则 msg.sender 必须 是 claimer
msg.sender 必须 是 earnerTokenTreeMerkleLeaf,
tokenLeaf.cumulativeEarnings > cumulativeClaimed[earner][token]: cumulativeEarnings 必须大于 cumulativeClaimed. 尝试使用相同的证明重新申领将恢复,因为申领的和收入值将相等,从而打破此要求。tokenLeaf.token.safeTransfer(recipient, claimAmount) 必须 成功processClaimsfunction processClaims(
        RewardsMerkleClaim[] calldata claims,
        address recipient
)
    external
    onlyWhenNotPaused(PAUSED_PROCESS_CLAIM)
    nonReentrant
processClaims 是围绕 _processClaim 的简单包装函数,对于每个提供的声明调用一次。
效果:
RewardsMerkleClaim 元素:请参阅上面的 processClaim。要求
processClaim。---### 系统配置
RewardsCoordinator.setActivationDelayRewardsCoordinator.setDefaultOperatorSplitRewardsCoordinator.setRewardsUpdaterRewardsCoordinator.setRewardsForAllSubmitterRewardsCoordinator.setOperatorAVSSplitRewardsCoordinator.setOperatorPISplitRewardsCoordinator.setOperatorSetSplitsetActivationDelayfunction setActivationDelay(uint32 _activationDelay) external onlyOwner
允许 Owner 设置全局的 activationDelay。激活延迟是指在提交 DistributionRoot 后,可以对其进行声明的时间(以秒为单位)。此延迟允许相关方在开始声明之前对根执行验证。
效果:
activationDelayActivationDelaySet 事件要求:
setDefaultOperatorSplitfunction setDefaultOperatorSplit(uint16 split) external onlyOwner
允许 Owner 以 basis points 为单位设置默认的 operator split。
此 split 在计算给定奖励分配的 Operator 收益时链下使用。Operator split 计算为分配给每个 Operator 的奖励金额的百分比。此 split 从奖励金额中扣除,之后剩余部分用于计算分配给委托给 Operator 的任何 Staker 的奖励。
效果:
defaultOperatorSplitBipsDefaultOperatorSplitBipsSet 事件要求:
setRewardsUpdaterfunction setRewardsUpdater(address _rewardsUpdater) external onlyOwner
允许 Owner 设置 rewardsUpdater 地址。rewardsUpdater 是一个单例地址,可以将新的 DistributionRoots 提交到 RewardsCoordinator。rewardsUpdater 是一个受信任的实体,它执行本文档中描述的大部分计算和 Merkle 树结构。
效果:
rewardsUpdater 地址RewardsUpdaterSet 事件要求:
setRewardsForAllSubmitterfunction setRewardsForAllSubmitter(address _submitter, bool _newValue) external onlyOwner
允许 Owner 更新 isRewardsForAllSubmitter 映射中 _submitter 的权限。此映射用于确定给定地址是否是 createRewardsForAllSubmission 方法的有效提交者。
效果:
_submitter 的 isRewardsForAllSubmitter 映射设置为布尔值 _newValueRewardsForAllSubmitterSet 事件要求:
setOperatorAVSsplitfunction setOperatorAVSSplit(
    address operator,
    address avs,
    uint16 split
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_AVS_SPLIT)
    checkCanCall(operator)
对于给定的 AVS,Operator 可以设置一个 split,该 split 将确定其归属奖励中有多少百分比分配给自己。剩余的百分比将分配给 Staker。
该 split 将在合约所有者设置的 activationDelay 之后生效。请注意,一旦 operator 启动 split 更新,必须经过 activationDelay 才能启动新的 split 更新。
效果:
operatorSplit.activatedAt 更新为 block.timestamp + activationDelayoperatorSplit.oldSplitBips 设置为 defaultOperatorSplitBips。否则,将 operatorSplit.oldSplitBips 设置为当前的 newSplitBipsoperatorSplit.newSplitBips 更新为 splitOperatorAVSSplitBipsSet 事件要求:
PermissionController.md)block.timestamp 必须大于当前的 operatorSplit.activatedAt。
setOperatorPIsplitfunction setOperatorPISplit(
    address operator,
    uint16 split
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_PI_SPLIT)
    checkCanCall(operator)
与 setOperatorAVSSplit 类似,Operator 可以为程序化激励设置他们的 split,从而允许他们指定他们将保留这些奖励的百分之多少,以及将分配给他们的 Staker 的百分之多少。allocationDelay 也适用于此处,以及在延迟通过之前无法重新启动 split 更新。
效果:
setOperatorAVSSplit。唯一的区别是:
_operatorPISplitBips 中,而不是 _operatorAVSSplitBips 中。OperatorPISplitBipsSet 事件要求:
setOperatorAVSSplit。唯一的区别是:
PAUSED_OPERATOR_PI_SPLITsetOperatorSetSplitfunction setOperatorSetSplit(
    address operator,
    OperatorSet calldata operatorSet,
    uint16 split
) 
    external 
    onlyWhenNotPaused(PAUSED_OPERATOR_SET_SPLIT) 
    checkCanCall(operator)
效果:
setOperatorAVSSplit。唯一的区别是:
_operatorSetSplitBips 中,而不是 _operatorAVSSplitBips 中OperatorSetSplitBipsSet 事件要求:
setOperatorAVSSplit。唯一的区别在于:
allocationManager.isOperatorSet(),operatorSet 必须是给定 AVS 的注册 operator setPAUSED_OPERATOR_SET_SPLIT此 Merkle 树用于验证针对 DistributionRoot 的声明。
提交新的 DistributionRoot 时,奖励更新者会将自上次提交的 DistributionRoot 以来,由 AVS 提交的所有 RewardsSubmissions 合并到 Merkle 树中,该 Merkle 树由收益者及其各自奖励代币的累计收益组成。
当收益者或其指定的声明者调用 processClaim 时,他们必须提供一个 RewardsMerkleClaim 结构,其中包含验证其针对最新 DistributionRoot 的声明的必要信息。Merkle 证明验证在内部 _checkClaim 辅助函数中完成。此函数针对 DistributionRoot 验证收益者的 EarnerTreeMerkleLeaf 的 Merkle 证明,然后对于每个 tokenIndex,针对收益者的 earnerTokenRoot 验证每个 token leaf。
声明者可以选择性地选择要针对其进行证明并声明累计收益的 token leaf。在 processClaim 调用中声明的每个 token 奖励都会将 token 发送到调用中指定的 recipient 地址。
奖励 Merkle 树的结构如下图所示:
![.] (https://img.learnblockchain.cn/2025/09/01/RewardsCoordinator_Merkle_Tree.png)
奖励通过链下数据管道计算。该管道以 SNAPSHOT_CADENCE 拍摄核心合约状态的快照,目前设置为每天一次。然后,它将这些快照与任何有效的奖励结合起来,以计算收益者单日的奖励是多少。每隔 CALCULATION_INTERVAL_SECONDS,奖励会累积到 lastRewardsTimestamp + CALCULATION_INTERVAL_SECONDS,并由具有 rewardsUpdater 角色的实体在链上发布。
鉴于链下管道的精度界限,MAX_REWARDS_AMOUNT 设置为 1e38-1。有关链下计算的深入概述,请参见此处。
- 原文链接: github.com/Layr-Labs/eig...
 - 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
 
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!