本文档介绍了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.createAVSRewardsSubmission
RewardsCoordinator.createRewardsForAllSubmission
RewardsCoordinator.createRewardsForAllEarners
RewardsCoordinator.createOperatorDirectedAVSRewardsSubmission
RewardsCoordinator.createOperatorDirectedOperatorSetRewardsSubmission
createAVSRewardsSubmission
function 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) 转移到 RewardsCoordinator
RewardsSubmission
结构进行哈希处理,以创建唯一的奖励哈希,并将此值设置为 isAVSRewardsSubmissionHash
映射中的 true
submissionNonce[msg.sender]
AVSRewardsSubmissionCreated
事件要求:
PAUSED_AVS_REWARDS_SUBMISSION
RewardsSubmission
元素
_validateRewardsSubmission()
的要求
rewardsSubmission.strategiesAndMultipliers.length > 0
rewardsSubmission.amount > 0
rewardsSubmission.amount <= MAX_REWARDS_AMOUNT
rewardsSubmission.duration <= MAX_REWARDS_DURATION
rewardsSubmission.duration % calculationIntervalSeconds == 0
rewardsSubmission.duration > 0
rewardsSubmission.startTimestamp % calculationIntervalSeconds == 0
block.timestamp - MAX_RETROACTIVE_LENGTH <= rewardsSubmission.startTimestamp
GENESIS_REWARDS_TIMESTAMP <= rewardsSubmission.startTimestamp
rewardsSubmission.startTimestamp <= block.timestamp + MAX_FUTURE_LENGTH
rewardsSubmission.strategiesAndMultipliers
的要求
strategy
都被列入 StrategyManager 的存款白名单,或者是 beaconChainETHStrategy
rewardsSubmission.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
createRewardsForAllSubmission
function createRewardsForAllSubmission(
RewardsSubmission[] calldata RewardsSubmissions
)
external
onlyWhenNotPaused(PAUSED_REWARDS_FOR_ALL_SUBMISSION)
onlyRewardsForAllSubmitter
nonReentrant
此方法在功能上与上面的 createAVSRewardsSubmission
相同,除了:
效果:
createAVSRewardsSubmission
。唯一的区别是:
isRewardsSubmissionForAllHash
映射中RewardsSubmissionForAllCreated
事件要求:
createAVSRewardsSubmission
。唯一的区别是,每个计算出的 rewards submission 哈希 必须 尚未存在于 isRewardsSubmissionForAllHash
映射中。createRewardsForAllEarners
function createRewardsForAllEarners(
RewardsSubmission[] calldata RewardsSubmissions
)
external
onlyWhenNotPaused(PAUSED_REWARDS_FOR_ALL_SUBMISSION)
onlyRewardsForAllSubmitter
nonReentrant
此方法在功能上与上面的 createAVSRewardsSubmission
相同,除了:
效果:
createAVSRewardsSubmission
。唯一的区别是:
isRewardsSubmissionForAllEarnersHash
映射中RewardsSubmissionForAllEarnersCreated
事件要求:
createAVSRewardsSubmission
。唯一的区别是,每个计算出的 rewards submission 哈希 必须 尚未存在于 isRewardsSubmissionForAllEarnersHash
映射中。createOperatorDirectedAVSRewardsSubmission
function 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
转移到 RewardsCoordinator
AVS
、nonce
和 OperatorDirectedRewardsSubmission
结构进行哈希处理,以创建唯一的奖励哈希,并将此值设置为 isOperatorDirectedAVSRewardsSubmissionHash
映射中的 true
submissionNonce[avs]
OperatorDirectedAVSRewardsSubmissionCreated
事件要求:
PAUSED_OPERATOR_DIRECTED_AVS_REWARDS_SUBMISSION
PermissionController.md
)OperatorDirectedRewardsSubmission
元素:
_validateOperatorDirectedRewardsSubmission()
的要求operatorDirectedRewardsSubmission.strategiesAndMultipliers.length > 0
operatorDirectedRewardsSubmission.duration <= MAX_REWARDS_DURATION
operatorDirectedRewardsSubmission.duration % calculationIntervalSeconds == 0
operatorDirectedRewardsSubmission.duration > 0
operatorDirectedRewardsSubmission.startTimestamp % calculationIntervalSeconds == 0
block.timestamp - MAX_RETROACTIVE_LENGTH <= operatorDirectedRewardsSubmission.startTimestamp
GENESIS_REWARDS_TIMESTAMP <= operatorDirectedRewardsSubmission.startTimestamp
operatorDirectedRewardsSubmission.strategiesAndMultipliers
元素:
strategy
都被列入 StrategyManager 的存款白名单,或者是 beaconChainETHStrategy
rewardsSubmission.strategiesAndMultipliers
按升序策略地址排序,以防止重复策略operatorDirectedRewardsSubmission.operatorRewards.length > 0
operatorDirectedRewardsSubmission.operatorRewards
元素:
operatorReward.operator != address(0)
currOperatorAddress < operatorReward.operator
operatorReward.amount > 0
totalAmount <= MAX_REWARDS_AMOUNT
,其中 totalAmount
是每个 operatorReward.amount
的总和operatorDirectedRewardsSubmission.startTimestamp + operatorDirectedRewardsSubmission.duration < block.timestamp
,强制执行严格的追溯奖励提交transferFrom
必须 成功地将 msg.sender
的 amount
的 token
转移到 RewardsCoordinator
createOperatorDirectedOperatorSetRewardsSubmission
function 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_SUBMISSION
rewards updater 计算奖励分配,并通过以下函数提交可申领的根:submitRoot
。他们还可以禁用尚未激活的根:
收益者可以使用以下函数配置和申领这些奖励:
submitRoot
function 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
设置为参数 rewardsCalculationEndTimestamp
DistributionRootSubmitted
事件要求:
PAUSED_SUBMIT_DISABLE_ROOTS
msg.sender
必须 是 rewardsUpdater
rewardsCalculationEndTimestamp > currRewardsCalculationEndTimestamp
rewardsCalculationEndTimestamp < block.timestamp
disableRoot
function disableRoot(
uint32 rootIndex
)
external
onlyWhenNotPaused(PAUSED_SUBMIT_DISABLE_ROOTS)
onlyRewardsUpdater
仅由 rewardsUpdater
地址调用,以禁用 RewardsCoordinator 中尚未激活的挂起的 DistributionRoot
(尚未达到 activatedAt 时间戳)。一旦达到 activatedAt 时间戳,根将无法再被禁用,并被视为最终确定且可针对其进行申领。
这是为了添加额外的措施来防止发布到合约的无效根,无论是由于错误还是可能发布的恶意根。
效果:
DistributionRoot
的 disabled
字段设置为 TrueprocessClaim
中针对 DistributionRoot
进行申领DistributionRootDisabled
事件要求:
PAUSED_SUBMIT_DISABLE_ROOTS
msg.sender
必须 是 rewardsUpdater
rootIndex < distributionRoots.length
root.disabled == False
block.timestamp < root.activatedAt
rewardsCalculationEndTimestamp < block.timestamp
setClaimerFor
function setClaimerFor(address claimer) external
由 earner (Staker/运营商) 调用,以设置可以代表他们调用 processClaim
的申领人地址。如果未设置申领人(claimerFor[earner] == address(0)
),则 earner 本身可以直接调用 processClaim
。
效果:
claimerFor[msg.sender]
设置为输入参数 claimer
ClaimerForSet
事件processClaim
function 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
转移到指定的 recipient
cumulativeClaimed
映射RewardsClaimed
事件要求:
PAUSED_PROCESS_CLAIM
claim
必须具有针对有效 DistributionRoot
的有效证明:
claim.rootIndex
给定的 DistributionRoot
,根 必须 处于活动状态(block.timestamp >= root.activatedAt
)claim.tokenIndices
必须 等于 claim.TokenTreeProofs
和 claim.tokenLeaves
的长度claim.earnerTreeProof
必须 针对 DistributionRoot
验证 claim.earnerLeaf
claim.tokenIndices[i]
:
claim.tokenTreeProofs[i]
必须 针对 claim.earnerLeaf.earnerTokenRoot
验证 claim.tokenLeaves[i]
claim.earnerLeaf.earner
中指定的 earner
在 claimerFor[earner]
中有指定的 claimer
,则 msg.sender
必须 是 claimer
msg.sender
必须 是 earner
TokenTreeMerkleLeaf
,
tokenLeaf.cumulativeEarnings > cumulativeClaimed[earner][token]
: cumulativeEarnings 必须大于 cumulativeClaimed. 尝试使用相同的证明重新申领将恢复,因为申领的和收入值将相等,从而打破此要求。tokenLeaf.token.safeTransfer(recipient, claimAmount)
必须 成功processClaims
function processClaims(
RewardsMerkleClaim[] calldata claims,
address recipient
)
external
onlyWhenNotPaused(PAUSED_PROCESS_CLAIM)
nonReentrant
processClaims
是围绕 _processClaim
的简单包装函数,对于每个提供的声明调用一次。
效果:
RewardsMerkleClaim
元素:请参阅上面的 processClaim
。要求
processClaim
。---### 系统配置
RewardsCoordinator.setActivationDelay
RewardsCoordinator.setDefaultOperatorSplit
RewardsCoordinator.setRewardsUpdater
RewardsCoordinator.setRewardsForAllSubmitter
RewardsCoordinator.setOperatorAVSSplit
RewardsCoordinator.setOperatorPISplit
RewardsCoordinator.setOperatorSetSplit
setActivationDelay
function setActivationDelay(uint32 _activationDelay) external onlyOwner
允许 Owner 设置全局的 activationDelay
。激活延迟是指在提交 DistributionRoot
后,可以对其进行声明的时间(以秒为单位)。此延迟允许相关方在开始声明之前对根执行验证。
效果:
activationDelay
ActivationDelaySet
事件要求:
setDefaultOperatorSplit
function setDefaultOperatorSplit(uint16 split) external onlyOwner
允许 Owner 以 basis points 为单位设置默认的 operator split。
此 split 在计算给定奖励分配的 Operator 收益时链下使用。Operator split 计算为分配给每个 Operator 的奖励金额的百分比。此 split 从奖励金额中扣除,之后剩余部分用于计算分配给委托给 Operator 的任何 Staker 的奖励。
效果:
defaultOperatorSplitBips
DefaultOperatorSplitBipsSet
事件要求:
setRewardsUpdater
function setRewardsUpdater(address _rewardsUpdater) external onlyOwner
允许 Owner 设置 rewardsUpdater
地址。rewardsUpdater
是一个单例地址,可以将新的 DistributionRoots
提交到 RewardsCoordinator
。rewardsUpdater
是一个受信任的实体,它执行本文档中描述的大部分计算和 Merkle 树结构。
效果:
rewardsUpdater
地址RewardsUpdaterSet
事件要求:
setRewardsForAllSubmitter
function setRewardsForAllSubmitter(address _submitter, bool _newValue) external onlyOwner
允许 Owner 更新 isRewardsForAllSubmitter
映射中 _submitter
的权限。此映射用于确定给定地址是否是 createRewardsForAllSubmission
方法的有效提交者。
效果:
_submitter
的 isRewardsForAllSubmitter
映射设置为布尔值 _newValue
RewardsForAllSubmitterSet
事件要求:
setOperatorAVSsplit
function 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 + activationDelay
operatorSplit.oldSplitBips
设置为 defaultOperatorSplitBips
。否则,将 operatorSplit.oldSplitBips
设置为当前的 newSplitBips
operatorSplit.newSplitBips
更新为 split
OperatorAVSSplitBipsSet
事件要求:
PermissionController.md
)block.timestamp
必须大于当前的 operatorSplit.activatedAt
。
setOperatorPIsplit
function 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_SPLIT
setOperatorSetSplit
function 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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!