feat: make RewardsRegistry keep a history of roots and claim status (#106)

# Description
This PR implements a comprehensive overhaul of the `RewardsRegistry`
contract to maintain complete history of reward merkle roots while
providing index-based claim tracking for operators. The new architecture
enables operators to claim rewards from any historical merkle root
instead of only the latest one. To do so, it:
- Adds the `merkleRootHistory` storage array to the contract, in which
we keep all rewards roots that ever came from the DataHaven side.
- Adds the `operatorClaimedByIndex` storage map to the contract, in
which we keep track, for each validator and root index, if it has
claimed it or not.
- This works even for new validators, since theoretically with this
system you could argue they could claim older roots that they were not a
part of which would be catastrophic, but they could never draft a
correct proof for those to claim them.
- Keeps some of the interface from before the overhaul, to have quick
access to the latest rewards merkle root through `getLatestMerkleRoot()`
and to claim rewards for it with `claimRewards()`. This is because the
expected behaviour is for validators to claim their rewards every era.
- Adds a way to batch claim rewards with `claimRewardsBatch()`. This
function allows a validator to claim rewards for multiple root indices
in one call by providing multiple proofs, useful if the validator has
fallen behind claims and has to catch up, although special care will
have to be taken by it to avoid reaching the gas limit of a transaction.

## Storage Efficiency Analysis
One might think this solution is not as storage-efficient as other
solutions that we can think of (I even had two other alternatives in
mind as well), but a simple back-of-the-envelope calculation gives us
peace of mind that the impact of this solution on the overal state size
of the chain is negligible:

### Assumptions (Worst Case Scenario):
- 1,000 validators (actual estimate for DataHaven: ~50/100 validators)
- 6-hour eras (most-likely scenario, following what Polkadot does:
~24-hour eras)
  - Which means 4 merkle root updates per day

### Annual Storage Requirements:
- Merkle Root History: **46,720 bytes/year**
  - 4 roots/day × 32 bytes/root × 365 days/year = 46,720 bytes/year
- Operator Claim Tracking: **~1.46 MB/year**
- 1,000 operators × 1 boolean/(operator * root index) × 1 byte/boolean ×
4 root indices/day × 365 days/year = 1,460,000 bytes/year
- **Total: ~1.5 MB/year**

This represents negligible storage overhead compared to the significant
operational benefits gained.

## TODO
Since we want to allow the operators/validators to only have to interact
with the AVS contract (that's why the `claimRewards` functions have the
`onlyAVS` modifier), we still have to:
- [x] Add the required functions to the AVS to allow operators to claim
their rewards.
- [x] Adds comprehensive unit tests for them.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
This commit is contained in:
Tobi Demeco 2025-07-10 03:47:39 -03:00 committed by GitHub
parent 6ad4e07ed8
commit e8970a2b5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 1390 additions and 108 deletions

View file

@ -9,14 +9,18 @@ interface IRewardsRegistryErrors {
error OnlyAVS();
/// @notice Thrown when a function is called by an address that is not the RewardsAgent.
error OnlyRewardsAgent();
/// @notice Thrown when rewards have already been claimed for the current merkle root.
error RewardsAlreadyClaimed();
/// @notice Thrown when a provided merkle proof is invalid.
error InvalidMerkleProof();
/// @notice Thrown when rewards transfer fails.
error RewardsTransferFailed();
/// @notice Thrown when the rewards merkle root is not set.
error RewardsMerkleRootNotSet();
/// @notice Thrown when trying to access a merkle root index that doesn't exist.
error InvalidMerkleRootIndex();
/// @notice Thrown when trying to claim rewards for a root index that has already been claimed.
error RewardsAlreadyClaimedForIndex();
/// @notice Thrown when the arrays provided to the batch claim function have mismatched lengths.
error ArrayLengthMismatch();
}
/**
@ -27,16 +31,37 @@ interface IRewardsRegistryEvents {
* @notice Emitted when a new merkle root is set
* @param oldRoot The previous merkle root
* @param newRoot The new merkle root
* @param newRootIndex The index of the new root in the history
*/
event RewardsMerkleRootUpdated(bytes32 oldRoot, bytes32 newRoot);
event RewardsMerkleRootUpdated(bytes32 oldRoot, bytes32 newRoot, uint256 newRootIndex);
/**
* @notice Emitted when rewards are claimed
* @param operatorAddress Address of the operator receiving rewards
* @notice Emitted when rewards are claimed for a specific root index
* @param operatorAddress Address of the operator that received the rewards
* @param rootIndex Index of the merkle root that the operator claimed rewards from
* @param points Points earned by the operator
* @param rewardsAmount Amount of rewards transferred
*/
event RewardsClaimed(address indexed operatorAddress, uint256 points, uint256 rewardsAmount);
event RewardsClaimedForIndex(
address indexed operatorAddress,
uint256 indexed rootIndex,
uint256 points,
uint256 rewardsAmount
);
/**
* @notice Emitted when rewards are claimed for multiple root indices in a batch
* @param operatorAddress Address of the operator that received the rewards
* @param rootIndices Array of merkle root indices that the operator claimed rewards from
* @param points Array of points earned by the operator for each root index
* @param totalRewardsAmount Total amount of rewards transferred to the operator
*/
event RewardsBatchClaimedForIndices(
address indexed operatorAddress,
uint256[] rootIndices,
uint256[] points,
uint256 totalRewardsAmount
);
}
/**
@ -54,18 +79,48 @@ interface IRewardsRegistry is IRewardsRegistryErrors, IRewardsRegistryEvents {
) external;
/**
* @notice Claim rewards for an operator
* @notice Claim rewards for an operator from a specific merkle root index
* @param operatorAddress Address of the operator to receive rewards
* @param rootIndex Index of the merkle root to claim from
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
* @dev Only callable by the AVS (Service Manager)
*/
function claimRewards(
address operatorAddress,
uint256 rootIndex,
uint256 operatorPoints,
bytes32[] calldata proof
) external;
/**
* @notice Claim rewards for an operator from the latest merkle root
* @param operatorAddress Address of the operator to receive rewards
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
* @dev Only callable by the AVS (Service Manager)
*/
function claimLatestRewards(
address operatorAddress,
uint256 operatorPoints,
bytes32[] calldata proof
) external;
/**
* @notice Claim rewards for an operator from multiple merkle root indices
* @param operatorAddress Address of the operator to receive rewards
* @param rootIndices Array of merkle root indices to claim from
* @param operatorPoints Array of points earned by the operator for each root
* @param proofs Array of merkle proofs to validate the operator's rewards
* @dev Only callable by the AVS (Service Manager)
*/
function claimRewardsBatch(
address operatorAddress,
uint256[] calldata rootIndices,
uint256[] calldata operatorPoints,
bytes32[][] calldata proofs
) external;
/**
* @notice Sets the rewards agent address in the RewardsRegistry contract
* @param rewardsAgent New rewards agent address
@ -74,4 +129,42 @@ interface IRewardsRegistry is IRewardsRegistryErrors, IRewardsRegistryEvents {
function setRewardsAgent(
address rewardsAgent
) external;
/**
* @notice Get the merkle root at a specific index
* @param index Index of the merkle root to retrieve
* @return The merkle root at the specified index
*/
function getMerkleRootByIndex(
uint256 index
) external view returns (bytes32);
/**
* @notice Get the latest merkle root index
* @return The index of the latest merkle root (returns 0 if no roots exist)
*/
function getLatestMerkleRootIndex() external view returns (uint256);
/**
* @notice Get the latest merkle root
* @return The latest merkle root (returns bytes32(0) if no roots exist)
*/
function getLatestMerkleRoot() external view returns (bytes32);
/**
* @notice Get the total number of merkle roots in history
* @return The total count of merkle roots
*/
function getMerkleRootHistoryLength() external view returns (uint256);
/**
* @notice Check if an operator has claimed rewards for a specific root index
* @param operatorAddress Address of the operator
* @param rootIndex Index of the merkle root to check
* @return True if the operator has claimed rewards for this root index, false otherwise
*/
function hasClaimedByIndex(
address operatorAddress,
uint256 rootIndex
) external view returns (bool);
}

View file

@ -134,17 +134,45 @@ interface IServiceManager is IServiceManagerUI, IServiceManagerErrors, IServiceM
function setRewardsRegistry(uint32 operatorSetId, IRewardsRegistry rewardsRegistry) external;
/**
* @notice Claim rewards for an operator from the specified operator set
* @notice Claim rewards for an operator from a specific merkle root index
* @param operatorSetId The ID of the operator set
* @param rootIndex Index of the merkle root to claim from
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
*/
function claimOperatorRewards(
uint32 operatorSetId,
uint256 rootIndex,
uint256 operatorPoints,
bytes32[] calldata proof
) external;
/**
* @notice Claim rewards for an operator from the latest merkle root
* @param operatorSetId The ID of the operator set
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
*/
function claimLatestOperatorRewards(
uint32 operatorSetId,
uint256 operatorPoints,
bytes32[] calldata proof
) external;
/**
* @notice Claim rewards for an operator from multiple merkle root indices
* @param operatorSetId The ID of the operator set
* @param rootIndices Array of merkle root indices to claim from
* @param operatorPoints Array of points earned by the operator for each root
* @param proofs Array of merkle proofs to validate the operator's rewards
*/
function claimOperatorRewardsBatch(
uint32 operatorSetId,
uint256[] calldata rootIndices,
uint256[] calldata operatorPoints,
bytes32[][] calldata proofs
) external;
/**
* @notice Sets the rewards agent address in the RewardsRegistry contract
* @param rewardsAgent New rewards agent address

View file

@ -44,9 +44,17 @@ contract RewardsRegistry is RewardsRegistryStorage {
function updateRewardsMerkleRoot(
bytes32 newMerkleRoot
) external override onlyRewardsAgent {
bytes32 oldRoot = lastRewardsMerkleRoot;
lastRewardsMerkleRoot = newMerkleRoot;
emit RewardsMerkleRootUpdated(oldRoot, newMerkleRoot);
// Get the old root (bytes32(0) if no roots exist)
bytes32 oldRoot = merkleRootHistory.length > 0
? merkleRootHistory[merkleRootHistory.length - 1]
: bytes32(0);
// Add the new root to the history
uint256 newRootIndex = merkleRootHistory.length;
merkleRootHistory.push(newMerkleRoot);
// Emit the corresponding event
emit RewardsMerkleRootUpdated(oldRoot, newMerkleRoot, newRootIndex);
}
/**
@ -61,47 +69,190 @@ contract RewardsRegistry is RewardsRegistryStorage {
}
/**
* @notice Claim rewards for an operator
* @notice Claim rewards for an operator from a specific merkle root index
* @param operatorAddress Address of the operator to receive rewards
* @param rootIndex Index of the merkle root to claim from
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
* @dev Only callable by the AVS (Service Manager)
*/
function claimRewards(
address operatorAddress,
uint256 rootIndex,
uint256 operatorPoints,
bytes32[] calldata proof
) external override onlyAVS {
// Check that the lastRewardsMerkleRoot is not the default value
if (lastRewardsMerkleRoot == bytes32(0)) {
// Validate the claim and calculate rewards
uint256 rewardsAmount = _validateClaim(operatorAddress, rootIndex, operatorPoints, proof);
_transferRewards(operatorAddress, rewardsAmount);
// Emit the corresponding event
emit RewardsClaimedForIndex(operatorAddress, rootIndex, operatorPoints, rewardsAmount);
}
/**
* @notice Claim rewards for an operator from the latest merkle root
* @param operatorAddress Address of the operator to receive rewards
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
* @dev Only callable by the AVS (Service Manager)
*/
function claimLatestRewards(
address operatorAddress,
uint256 operatorPoints,
bytes32[] calldata proof
) external override onlyAVS {
// Check that we have at least one merkle root
if (merkleRootHistory.length == 0) {
revert RewardsMerkleRootNotSet();
}
// Check if operator has already claimed for this merkle root
if (operatorToLastClaimedRoot[operatorAddress] == lastRewardsMerkleRoot) {
revert RewardsAlreadyClaimed();
// Claim from the latest root index
uint256 latestIndex = merkleRootHistory.length - 1;
uint256 rewardsAmount = _validateClaim(operatorAddress, latestIndex, operatorPoints, proof);
_transferRewards(operatorAddress, rewardsAmount);
// Emit the corresponding event
emit RewardsClaimedForIndex(operatorAddress, latestIndex, operatorPoints, rewardsAmount);
}
/**
* @notice Claim rewards for an operator from multiple merkle root indices
* @param operatorAddress Address of the operator to receive rewards
* @param rootIndices Array of merkle root indices to claim from
* @param operatorPoints Array of points earned by the operator for each root
* @param proofs Array of merkle proofs to validate the operator's rewards
* @dev Only callable by the AVS (Service Manager)
*/
function claimRewardsBatch(
address operatorAddress,
uint256[] calldata rootIndices,
uint256[] calldata operatorPoints,
bytes32[][] calldata proofs
) external override onlyAVS {
// Check that the arrays have the same length
if (rootIndices.length != operatorPoints.length || rootIndices.length != proofs.length) {
revert ArrayLengthMismatch();
}
// Validate all claims and accumulate the total rewards
uint256 totalRewards = 0;
for (uint256 i = 0; i < rootIndices.length; i++) {
totalRewards +=
_validateClaim(operatorAddress, rootIndices[i], operatorPoints[i], proofs[i]);
}
// Transfer the total rewards in a single transaction
_transferRewards(operatorAddress, totalRewards);
// Emit the corresponding event
emit RewardsBatchClaimedForIndices(
operatorAddress, rootIndices, operatorPoints, totalRewards
);
}
/**
* @notice Internal function to validate a claim and calculate rewards
* @param operatorAddress Address of the operator to receive rewards
* @param rootIndex Index of the merkle root to claim from
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
* @return rewardsAmount The amount of rewards calculated
*/
function _validateClaim(
address operatorAddress,
uint256 rootIndex,
uint256 operatorPoints,
bytes32[] calldata proof
) internal returns (uint256 rewardsAmount) {
// Check that the root index to claim from exists
if (rootIndex >= merkleRootHistory.length) {
revert InvalidMerkleRootIndex();
}
// Check if operator has already claimed for this merkle root index
if (operatorClaimedByIndex[operatorAddress][rootIndex]) {
revert RewardsAlreadyClaimedForIndex();
}
// Verify the merkle proof
bytes32 leaf = keccak256(abi.encode(operatorAddress, operatorPoints));
if (!MerkleProof.verify(proof, lastRewardsMerkleRoot, leaf)) {
if (!MerkleProof.verify(proof, merkleRootHistory[rootIndex], leaf)) {
revert InvalidMerkleProof();
}
// Calculate rewards - currently 1 point = 1 wei (placeholder)
// TODO: Update the reward calculation formula with the proper relationship
uint256 rewardsAmount = operatorPoints;
rewardsAmount = operatorPoints;
// Update the operator's last claimed root
operatorToLastClaimedRoot[operatorAddress] = lastRewardsMerkleRoot;
// Mark as claimed for this specific index
operatorClaimedByIndex[operatorAddress][rootIndex] = true;
}
/**
* @notice Internal function to transfer rewards to an operator
* @param operatorAddress Address of the operator to receive rewards
* @param rewardsAmount Amount of rewards to transfer
*/
function _transferRewards(address operatorAddress, uint256 rewardsAmount) internal {
// Transfer rewards to the operator
(bool success,) = operatorAddress.call{value: rewardsAmount}("");
if (!success) {
revert RewardsTransferFailed();
}
}
emit RewardsClaimed(operatorAddress, operatorPoints, rewardsAmount);
/**
* @notice Get the merkle root at a specific index
* @param index Index of the merkle root to retrieve
* @return The merkle root at the specified index
*/
function getMerkleRootByIndex(
uint256 index
) external view override returns (bytes32) {
if (index >= merkleRootHistory.length) {
revert InvalidMerkleRootIndex();
}
return merkleRootHistory[index];
}
/**
* @notice Get the latest merkle root index
* @return The index of the latest merkle root (returns 0 if no roots exist)
*/
function getLatestMerkleRootIndex() external view override returns (uint256) {
uint256 length = merkleRootHistory.length;
return length == 0 ? 0 : length - 1;
}
/**
* @notice Get the latest merkle root
* @return The latest merkle root (returns bytes32(0) if no roots exist)
*/
function getLatestMerkleRoot() external view override returns (bytes32) {
uint256 length = merkleRootHistory.length;
return length == 0 ? bytes32(0) : merkleRootHistory[length - 1];
}
/**
* @notice Get the total number of merkle roots in history
* @return The total count of merkle roots
*/
function getMerkleRootHistoryLength() external view override returns (uint256) {
return merkleRootHistory.length;
}
/**
* @notice Check if an operator has claimed rewards for a specific root index
* @param operatorAddress Address of the operator
* @param rootIndex Index of the merkle root to check
* @return True if the operator has claimed rewards for this root index
*/
function hasClaimedByIndex(
address operatorAddress,
uint256 rootIndex
) external view override returns (bool) {
return operatorClaimedByIndex[operatorAddress][rootIndex];
}
/**

View file

@ -26,11 +26,11 @@ abstract contract RewardsRegistryStorage is IRewardsRegistry {
/// @notice Address of the rewards agent contract
address public rewardsAgent;
/// @notice Last rewards merkle root
bytes32 public lastRewardsMerkleRoot;
/// @notice History of all merkle roots, accessible by index
bytes32[] public merkleRootHistory;
/// @notice Mapping from operator ID to the last claimed merkle root
mapping(address => bytes32) public operatorToLastClaimedRoot;
/// @notice Mapping from operator to merkle root index to claimed status
mapping(address => mapping(uint256 => bool)) public operatorClaimedByIndex;
/**
* @notice Constructor to set up the immutable AVS address

View file

@ -303,12 +303,38 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage, IAVSRegistrar
}
/**
* @notice Claim rewards for an operator from the specified operator set
* @notice Claim rewards for an operator from a specific merkle root index
* @param operatorSetId The ID of the operator set
* @param rootIndex Index of the merkle root to claim from
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
*/
function claimOperatorRewards(
uint32 operatorSetId,
uint256 rootIndex,
uint256 operatorPoints,
bytes32[] calldata proof
) external virtual override {
// Get the rewards registry for this operator set
IRewardsRegistry rewardsRegistry = operatorSetToRewardsRegistry[operatorSetId];
if (address(rewardsRegistry) == address(0)) {
revert NoRewardsRegistryForOperatorSet();
}
// Ensure the operator is part of the operator set
_ensureOperatorIsPartOfOperatorSet(msg.sender, operatorSetId);
// Forward the claim to the rewards registry
rewardsRegistry.claimRewards(msg.sender, rootIndex, operatorPoints, proof);
}
/**
* @notice Claim rewards for an operator from the latest merkle root
* @param operatorSetId The ID of the operator set
* @param operatorPoints Points earned by the operator
* @param proof Merkle proof to validate the operator's rewards
*/
function claimLatestOperatorRewards(
uint32 operatorSetId,
uint256 operatorPoints,
bytes32[] calldata proof
@ -323,7 +349,33 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage, IAVSRegistrar
_ensureOperatorIsPartOfOperatorSet(msg.sender, operatorSetId);
// Forward the claim to the rewards registry
rewardsRegistry.claimRewards(msg.sender, operatorPoints, proof);
rewardsRegistry.claimLatestRewards(msg.sender, operatorPoints, proof);
}
/**
* @notice Claim rewards for an operator from multiple merkle root indices
* @param operatorSetId The ID of the operator set
* @param rootIndices Array of merkle root indices to claim from
* @param operatorPoints Array of points earned by the operator for each root
* @param proofs Array of merkle proofs to validate the operator's rewards
*/
function claimOperatorRewardsBatch(
uint32 operatorSetId,
uint256[] calldata rootIndices,
uint256[] calldata operatorPoints,
bytes32[][] calldata proofs
) external virtual override {
// Get the rewards registry for this operator set
IRewardsRegistry rewardsRegistry = operatorSetToRewardsRegistry[operatorSetId];
if (address(rewardsRegistry) == address(0)) {
revert NoRewardsRegistryForOperatorSet();
}
// Ensure the operator is part of the operator set
_ensureOperatorIsPartOfOperatorSet(msg.sender, operatorSetId);
// Forward the claim to the rewards registry
rewardsRegistry.claimRewardsBatch(msg.sender, rootIndices, operatorPoints, proofs);
}
/**

View file

@ -22,8 +22,19 @@ contract RewardsRegistryTest is AVSDeployer {
bytes32[] public invalidProof;
// Events
event RewardsMerkleRootUpdated(bytes32 oldRoot, bytes32 newRoot);
event RewardsClaimed(address indexed operatorAddress, uint256 points, uint256 rewardsAmount);
event RewardsMerkleRootUpdated(bytes32 oldRoot, bytes32 newRoot, uint256 newRootIndex);
event RewardsClaimedForIndex(
address indexed operatorAddress,
uint256 indexed rootIndex,
uint256 points,
uint256 rewardsAmount
);
event RewardsBatchClaimedForIndices(
address indexed operatorAddress,
uint256[] rootIndices,
uint256[] points,
uint256 totalRewardsAmount
);
function setUp() public {
_deployMockEigenLayerAndAVS();
@ -97,13 +108,11 @@ contract RewardsRegistryTest is AVSDeployer {
vm.prank(mockRewardsAgent);
vm.expectEmit(true, true, true, true);
emit RewardsMerkleRootUpdated(bytes32(0), merkleRoot);
emit RewardsMerkleRootUpdated(bytes32(0), merkleRoot, 0);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
assertEq(
rewardsRegistry.lastRewardsMerkleRoot(), merkleRoot, "Merkle root should be updated"
);
assertEq(rewardsRegistry.getLatestMerkleRoot(), merkleRoot, "Merkle root should be updated");
}
function test_updateRewardsMerkleRoot_NotRewardsAgent() public {
@ -123,7 +132,7 @@ contract RewardsRegistryTest is AVSDeployer {
vm.prank(mockRewardsAgent);
vm.expectEmit(true, true, true, true);
emit RewardsMerkleRootUpdated(merkleRoot, newMerkleRoot);
emit RewardsMerkleRootUpdated(merkleRoot, newMerkleRoot, 1);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
}
@ -155,7 +164,7 @@ contract RewardsRegistryTest is AVSDeployer {
* claimRewards Tests *
*
*/
function test_claimRewards() public {
function test_claimLatestRewards() public {
// First update merkle root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
@ -168,15 +177,14 @@ contract RewardsRegistryTest is AVSDeployer {
vm.prank(address(serviceManager));
vm.expectEmit(true, true, true, true);
emit RewardsClaimed(operatorAddress, operatorPoints, operatorPoints);
emit RewardsClaimedForIndex(operatorAddress, 0, operatorPoints, operatorPoints);
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
// Verify state changes
assertEq(
rewardsRegistry.operatorToLastClaimedRoot(operatorAddress),
merkleRoot,
"Operator's last claimed root should be updated"
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Operator should have claimed from the latest root index"
);
assertEq(
operatorAddress.balance,
@ -185,7 +193,7 @@ contract RewardsRegistryTest is AVSDeployer {
);
}
function test_claimRewards_NotAVS() public {
function test_claimLatestRewards_NotAVS() public {
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
@ -193,10 +201,10 @@ contract RewardsRegistryTest is AVSDeployer {
vm.expectRevert(abi.encodeWithSelector(IRewardsRegistryErrors.OnlyAVS.selector));
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
}
function test_claimRewards_AlreadyClaimed() public {
function test_claimLatestRewards_AlreadyClaimed() public {
// First update merkle root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
@ -206,35 +214,35 @@ contract RewardsRegistryTest is AVSDeployer {
// First claim succeeds
vm.prank(address(serviceManager));
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
// Second claim fails
vm.prank(address(serviceManager));
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimed.selector)
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimedForIndex.selector)
);
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
}
function test_claimRewards_InvalidProof() public {
function test_claimLatestRewards_InvalidProof() public {
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.prank(address(serviceManager));
vm.expectRevert(abi.encodeWithSelector(IRewardsRegistryErrors.InvalidMerkleProof.selector));
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, invalidProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, invalidProof);
}
function test_claimRewards_NoMerkleRoot() public {
// lastRewardsMerkleRoot is not set
function test_claimLatestRewards_NoMerkleRoot() public {
// No merkle roots exist yet
vm.prank(address(serviceManager));
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsMerkleRootNotSet.selector)
);
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
}
function test_claimRewards_DifferentRoot() public {
function test_claimLatestRewards_DifferentRoot() public {
// First merkle root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
@ -244,7 +252,7 @@ contract RewardsRegistryTest is AVSDeployer {
// First claim succeeds
vm.prank(address(serviceManager));
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
// Update to new merkle root
vm.prank(mockRewardsAgent);
@ -257,16 +265,20 @@ contract RewardsRegistryTest is AVSDeployer {
// Operator can claim again with new merkle root
vm.prank(address(serviceManager));
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, newProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, newProof);
assertEq(
rewardsRegistry.operatorToLastClaimedRoot(operatorAddress),
newMerkleRoot,
"Operator's last claimed root should be updated to new root"
// Verify both indices are now claimed
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Operator should have claimed from first root index"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 1),
"Operator should have claimed from second root index"
);
}
function test_claimRewards_InsufficientBalance() public {
function test_claimLatestRewards_InsufficientBalance() public {
// Set merkle root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
@ -278,7 +290,7 @@ contract RewardsRegistryTest is AVSDeployer {
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsTransferFailed.selector)
);
rewardsRegistry.claimRewards(operatorAddress, operatorPoints, validProof);
rewardsRegistry.claimLatestRewards(operatorAddress, operatorPoints, validProof);
}
function test_receive() public {
@ -290,4 +302,303 @@ contract RewardsRegistryTest is AVSDeployer {
assertTrue(success, "Contract should be able to receive ETH");
assertEq(address(rewardsRegistry).balance, amount, "Contract balance should increase");
}
/**
*
* Merkle Root History Tests *
*
*/
function test_getMerkleRootByIndex() public {
// Add first root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
// Add second root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
// Test accessing by index
assertEq(
rewardsRegistry.getMerkleRootByIndex(0),
merkleRoot,
"First root should be accessible by index 0"
);
assertEq(
rewardsRegistry.getMerkleRootByIndex(1),
newMerkleRoot,
"Second root should be accessible by index 1"
);
}
function test_getMerkleRootByIndex_InvalidIndex() public {
// Add one root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
// Try to access invalid index
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.InvalidMerkleRootIndex.selector)
);
rewardsRegistry.getMerkleRootByIndex(1);
}
function test_getLatestMerkleRootIndex() public {
// Initially should return 0 when no roots exist
assertEq(
rewardsRegistry.getLatestMerkleRootIndex(), 0, "Should return 0 when no roots exist"
);
// Add first root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
assertEq(rewardsRegistry.getLatestMerkleRootIndex(), 0, "Should return 0 for first root");
// Add second root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
assertEq(rewardsRegistry.getLatestMerkleRootIndex(), 1, "Should return 1 for second root");
}
function test_getMerkleRootHistoryLength() public {
// Initially should be 0
assertEq(rewardsRegistry.getMerkleRootHistoryLength(), 0, "Should be 0 initially");
// Add first root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
assertEq(rewardsRegistry.getMerkleRootHistoryLength(), 1, "Should be 1 after first root");
// Add second root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
assertEq(rewardsRegistry.getMerkleRootHistoryLength(), 2, "Should be 2 after second root");
}
function test_historyPreservesQuickAccess() public {
// Add multiple roots
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
// Latest root should be accessible directly without index
assertEq(
rewardsRegistry.getLatestMerkleRoot(),
newMerkleRoot,
"getLatestMerkleRoot should return latest root"
);
// But we should also be able to access by index
assertEq(
rewardsRegistry.getMerkleRootByIndex(1),
newMerkleRoot,
"Latest root should also be accessible by index"
);
assertEq(
rewardsRegistry.getMerkleRootByIndex(0),
merkleRoot,
"Previous root should be accessible by index"
);
}
/**
*
* Index-based Claim Tests *
*
*/
function test_claimRewards() public {
// Add multiple roots
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
// Add ETH to contract for rewards
vm.deal(address(rewardsRegistry), 1000 ether);
uint256 initialBalance = operatorAddress.balance;
// Claim from first root (index 0)
vm.prank(address(serviceManager));
vm.expectEmit(true, true, true, true);
emit RewardsClaimedForIndex(operatorAddress, 0, operatorPoints, operatorPoints);
rewardsRegistry.claimRewards(operatorAddress, 0, operatorPoints, validProof);
// Verify state changes
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Operator should have claimed from index 0"
);
assertEq(
operatorAddress.balance,
initialBalance + operatorPoints,
"Operator should receive correct rewards"
);
}
function test_claimRewards_InvalidIndex() public {
vm.deal(address(rewardsRegistry), 1000 ether);
vm.prank(address(serviceManager));
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.InvalidMerkleRootIndex.selector)
);
rewardsRegistry.claimRewards(operatorAddress, 0, operatorPoints, validProof);
}
function test_claimRewards_AlreadyClaimed() public {
// Add root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.deal(address(rewardsRegistry), 1000 ether);
// First claim succeeds
vm.prank(address(serviceManager));
rewardsRegistry.claimRewards(operatorAddress, 0, operatorPoints, validProof);
// Second claim fails
vm.prank(address(serviceManager));
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimedForIndex.selector)
);
rewardsRegistry.claimRewards(operatorAddress, 0, operatorPoints, validProof);
}
function test_hasClaimedByIndex() public {
// Add root
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.deal(address(rewardsRegistry), 1000 ether);
// Initially not claimed
assertFalse(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Should not have claimed initially"
);
// Claim
vm.prank(address(serviceManager));
rewardsRegistry.claimRewards(operatorAddress, 0, operatorPoints, validProof);
// Now claimed
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0), "Should have claimed after claim"
);
}
/**
*
* Batch Claim Tests *
*
*/
function test_claimRewardsBatch() public {
// Add multiple roots
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
vm.deal(address(rewardsRegistry), 1000 ether);
// Prepare batch claim data
uint256[] memory rootIndices = new uint256[](2);
rootIndices[0] = 0;
rootIndices[1] = 1;
uint256[] memory points = new uint256[](2);
points[0] = operatorPoints;
points[1] = operatorPoints;
bytes32[][] memory proofs = new bytes32[][](2);
proofs[0] = validProof;
// Create proof for second root
bytes32[] memory newProof = new bytes32[](1);
bytes32 newSiblingLeaf = keccak256(abi.encodePacked("new sibling"));
newProof[0] = newSiblingLeaf;
proofs[1] = newProof;
uint256 initialBalance = operatorAddress.balance;
// Batch claim
vm.prank(address(serviceManager));
vm.expectEmit(true, true, true, true);
emit RewardsBatchClaimedForIndices(operatorAddress, rootIndices, points, operatorPoints * 2);
rewardsRegistry.claimRewardsBatch(operatorAddress, rootIndices, points, proofs);
// Verify both indices are claimed
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Should have claimed from index 0"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 1),
"Should have claimed from index 1"
);
// Verify total rewards received
assertEq(
operatorAddress.balance,
initialBalance + (operatorPoints * 2),
"Should receive rewards from both claims"
);
}
function test_claimRewardsBatch_ArrayLengthMismatch() public {
uint256[] memory rootIndices = new uint256[](2);
uint256[] memory points = new uint256[](1); // Wrong length
bytes32[][] memory proofs = new bytes32[][](2);
vm.prank(address(serviceManager));
vm.expectRevert(abi.encodeWithSelector(IRewardsRegistryErrors.ArrayLengthMismatch.selector));
rewardsRegistry.claimRewardsBatch(operatorAddress, rootIndices, points, proofs);
}
function test_claimRewardsBatch_PartialClaimFailure() public {
// Add roots
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(newMerkleRoot);
vm.deal(address(rewardsRegistry), 1000 ether);
// Claim from index 0 first
vm.prank(address(serviceManager));
rewardsRegistry.claimRewards(operatorAddress, 0, operatorPoints, validProof);
// Now try batch claim that includes already claimed index 0
uint256[] memory rootIndices = new uint256[](2);
rootIndices[0] = 0; // Already claimed
rootIndices[1] = 1;
uint256[] memory points = new uint256[](2);
points[0] = operatorPoints;
points[1] = operatorPoints;
bytes32[][] memory proofs = new bytes32[][](2);
proofs[0] = validProof;
bytes32[] memory newProof = new bytes32[](1);
bytes32 newSiblingLeaf = keccak256(abi.encodePacked("new sibling"));
newProof[0] = newSiblingLeaf;
proofs[1] = newProof;
// Should fail because index 0 is already claimed
vm.prank(address(serviceManager));
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimedForIndex.selector)
);
rewardsRegistry.claimRewardsBatch(operatorAddress, rootIndices, points, proofs);
}
}

View file

@ -22,12 +22,29 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
// Test data
uint32 public operatorSetId;
bytes32 public merkleRoot;
bytes32 public secondMerkleRoot;
bytes32 public thirdMerkleRoot;
uint256 public operatorPoints;
uint256 public secondOperatorPoints;
uint256 public thirdOperatorPoints;
bytes32[] public validProof;
bytes32[] public secondValidProof;
bytes32[] public thirdValidProof;
// Events
event RewardsRegistrySet(uint32 indexed operatorSetId, address indexed rewardsRegistry);
event RewardsClaimed(address indexed operatorAddress, uint256 points, uint256 rewardsAmount);
event RewardsClaimedForIndex(
address indexed operatorAddress,
uint256 indexed rootIndex,
uint256 points,
uint256 rewardsAmount
);
event RewardsBatchClaimedForIndices(
address indexed operatorAddress,
uint256[] rootIndices,
uint256[] points,
uint256 totalRewardsAmount
);
function setUp() public {
_deployMockEigenLayerAndAVS();
@ -39,28 +56,65 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
// Configure test data
operatorSetId = 1;
operatorPoints = 100;
secondOperatorPoints = 200;
thirdOperatorPoints = 150;
// Create a merkle tree where we know what the root should be based on our leaf
bytes32 leaf = keccak256(abi.encode(operatorAddress, operatorPoints));
bytes32 siblingLeaf = keccak256(abi.encodePacked("sibling"));
(bytes32 leftLeaf, bytes32 rightLeaf) =
leaf < siblingLeaf ? (leaf, siblingLeaf) : (siblingLeaf, leaf);
merkleRoot = keccak256(abi.encodePacked(leftLeaf, rightLeaf));
validProof = new bytes32[](1);
validProof[0] = siblingLeaf;
// Create multiple merkle trees for comprehensive batch testing
_createFirstMerkleTree();
_createSecondMerkleTree();
_createThirdMerkleTree();
// Set up the rewards registry for the operator set
vm.prank(avsOwner);
serviceManager.setRewardsRegistry(operatorSetId, IRewardsRegistry(address(rewardsRegistry)));
// Set the merkle root
// Set all three merkle roots to create a history
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(merkleRoot);
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(secondMerkleRoot);
vm.prank(mockRewardsAgent);
rewardsRegistry.updateRewardsMerkleRoot(thirdMerkleRoot);
// Add funds to the registry for rewards
vm.deal(address(rewardsRegistry), 1000 ether);
}
function _createFirstMerkleTree() internal {
// Create first merkle tree
bytes32 leaf = keccak256(abi.encode(operatorAddress, operatorPoints));
bytes32 siblingLeaf = keccak256(abi.encodePacked("sibling1"));
(bytes32 leftLeaf, bytes32 rightLeaf) =
leaf < siblingLeaf ? (leaf, siblingLeaf) : (siblingLeaf, leaf);
merkleRoot = keccak256(abi.encodePacked(leftLeaf, rightLeaf));
validProof = new bytes32[](1);
validProof[0] = siblingLeaf;
}
function _createSecondMerkleTree() internal {
// Create second merkle tree with different points
bytes32 leaf = keccak256(abi.encode(operatorAddress, secondOperatorPoints));
bytes32 siblingLeaf = keccak256(abi.encodePacked("sibling2"));
(bytes32 leftLeaf, bytes32 rightLeaf) =
leaf < siblingLeaf ? (leaf, siblingLeaf) : (siblingLeaf, leaf);
secondMerkleRoot = keccak256(abi.encodePacked(leftLeaf, rightLeaf));
secondValidProof = new bytes32[](1);
secondValidProof[0] = siblingLeaf;
}
function _createThirdMerkleTree() internal {
// Create third merkle tree with different points
bytes32 leaf = keccak256(abi.encode(operatorAddress, thirdOperatorPoints));
bytes32 siblingLeaf = keccak256(abi.encodePacked("sibling3"));
(bytes32 leftLeaf, bytes32 rightLeaf) =
leaf < siblingLeaf ? (leaf, siblingLeaf) : (siblingLeaf, leaf);
thirdMerkleRoot = keccak256(abi.encodePacked(leftLeaf, rightLeaf));
thirdValidProof = new bytes32[](1);
thirdValidProof[0] = siblingLeaf;
}
function test_setRewardsRegistry() public {
uint32 newOperatorSetId = 2;
RewardsRegistry newRewardsRegistry =
@ -94,6 +148,65 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
);
}
function test_claimLatestOperatorRewards() public {
uint256 initialBalance = operatorAddress.balance;
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
vm.prank(operatorAddress);
vm.expectEmit(true, true, true, true);
emit RewardsClaimedForIndex(operatorAddress, 2, thirdOperatorPoints, thirdOperatorPoints);
serviceManager.claimLatestOperatorRewards(
operatorSetId, thirdOperatorPoints, thirdValidProof
);
assertEq(
operatorAddress.balance,
initialBalance + thirdOperatorPoints,
"Operator should receive correct rewards"
);
}
function test_claimLatestOperatorRewards_NoRewardsRegistry() public {
uint32 invalidSetId = 999;
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IServiceManagerErrors.NoRewardsRegistryForOperatorSet.selector)
);
serviceManager.claimLatestOperatorRewards(invalidSetId, operatorPoints, validProof);
}
function test_claimLatestOperatorRewards_AlreadyClaimed() public {
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
// First claim (uses latest merkle root - index 2)
vm.prank(operatorAddress);
serviceManager.claimLatestOperatorRewards(
operatorSetId, thirdOperatorPoints, thirdValidProof
);
// Second claim should fail
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimedForIndex.selector)
);
serviceManager.claimLatestOperatorRewards(
operatorSetId, thirdOperatorPoints, thirdValidProof
);
}
function test_claimOperatorRewards() public {
uint256 initialBalance = operatorAddress.balance;
@ -105,9 +218,9 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
vm.prank(operatorAddress);
vm.expectEmit(true, true, true, true);
emit RewardsClaimed(operatorAddress, operatorPoints, operatorPoints);
emit RewardsClaimedForIndex(operatorAddress, 0, operatorPoints, operatorPoints);
serviceManager.claimOperatorRewards(operatorSetId, operatorPoints, validProof);
serviceManager.claimOperatorRewards(operatorSetId, 0, operatorPoints, validProof);
assertEq(
operatorAddress.balance,
@ -116,15 +229,61 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
);
}
function test_claimOperatorRewards_NoRewardsRegistry() public {
uint32 invalidSetId = 999;
function test_claimOperatorRewards_DifferentIndices() public {
uint256 initialBalance = operatorAddress.balance;
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
// Claim from index 1 (second merkle root)
vm.prank(operatorAddress);
serviceManager.claimOperatorRewards(
operatorSetId, 1, secondOperatorPoints, secondValidProof
);
assertEq(
operatorAddress.balance,
initialBalance + secondOperatorPoints,
"Operator should receive rewards from second root"
);
// Claim from index 2 (third merkle root)
vm.prank(operatorAddress);
serviceManager.claimOperatorRewards(operatorSetId, 2, thirdOperatorPoints, thirdValidProof);
assertEq(
operatorAddress.balance,
initialBalance + secondOperatorPoints + thirdOperatorPoints,
"Operator should receive rewards from both roots"
);
// Verify claim status
assertFalse(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0), "Index 0 should not be claimed"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 1), "Index 1 should be claimed"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 2), "Index 2 should be claimed"
);
}
function test_claimOperatorRewards_InvalidIndex() public {
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IServiceManagerErrors.NoRewardsRegistryForOperatorSet.selector)
abi.encodeWithSelector(IRewardsRegistryErrors.InvalidMerkleRootIndex.selector)
);
serviceManager.claimOperatorRewards(invalidSetId, operatorPoints, validProof);
serviceManager.claimOperatorRewards(operatorSetId, 999, operatorPoints, validProof);
}
function test_claimOperatorRewards_AlreadyClaimed() public {
@ -136,15 +295,190 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
// First claim
vm.prank(operatorAddress);
serviceManager.claimOperatorRewards(operatorSetId, operatorPoints, validProof);
serviceManager.claimOperatorRewards(operatorSetId, 0, operatorPoints, validProof);
// Second claim should fail
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimed.selector)
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimedForIndex.selector)
);
serviceManager.claimOperatorRewards(operatorSetId, 0, operatorPoints, validProof);
}
function test_claimOperatorRewardsBatch() public {
// Test claiming from multiple different merkle root indices
uint256[] memory rootIndices = new uint256[](3);
rootIndices[0] = 0; // First merkle root
rootIndices[1] = 1; // Second merkle root
rootIndices[2] = 2; // Third merkle root
uint256[] memory points = new uint256[](3);
points[0] = operatorPoints;
points[1] = secondOperatorPoints;
points[2] = thirdOperatorPoints;
bytes32[][] memory proofs = new bytes32[][](3);
proofs[0] = validProof;
proofs[1] = secondValidProof;
proofs[2] = thirdValidProof;
uint256 expectedTotalRewards = operatorPoints + secondOperatorPoints + thirdOperatorPoints;
uint256 initialBalance = operatorAddress.balance;
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
serviceManager.claimOperatorRewards(operatorSetId, operatorPoints, validProof);
vm.prank(operatorAddress);
vm.expectEmit(true, true, true, true);
emit RewardsBatchClaimedForIndices(
operatorAddress, rootIndices, points, expectedTotalRewards
);
serviceManager.claimOperatorRewardsBatch(operatorSetId, rootIndices, points, proofs);
// Verify final balance includes all rewards
assertEq(
operatorAddress.balance,
initialBalance + expectedTotalRewards,
"Operator should receive rewards from all three claims"
);
// Verify all indices are now claimed
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Operator should have claimed from index 0"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 1),
"Operator should have claimed from index 1"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 2),
"Operator should have claimed from index 2"
);
}
function test_claimOperatorRewardsBatch_PartialBatch() public {
// Test claiming from only some of the available merkle roots
uint256[] memory rootIndices = new uint256[](2);
rootIndices[0] = 0; // First merkle root
rootIndices[1] = 2; // Third merkle root (skipping second)
uint256[] memory points = new uint256[](2);
points[0] = operatorPoints;
points[1] = thirdOperatorPoints;
bytes32[][] memory proofs = new bytes32[][](2);
proofs[0] = validProof;
proofs[1] = thirdValidProof;
uint256 expectedTotalRewards = operatorPoints + thirdOperatorPoints;
uint256 initialBalance = operatorAddress.balance;
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
vm.prank(operatorAddress);
serviceManager.claimOperatorRewardsBatch(operatorSetId, rootIndices, points, proofs);
// Verify balance and claim status
assertEq(
operatorAddress.balance,
initialBalance + expectedTotalRewards,
"Operator should receive rewards from claimed indices"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 0),
"Operator should have claimed from index 0"
);
assertFalse(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 1),
"Operator should NOT have claimed from index 1"
);
assertTrue(
rewardsRegistry.hasClaimedByIndex(operatorAddress, 2),
"Operator should have claimed from index 2"
);
}
function test_claimOperatorRewardsBatch_ArrayLengthMismatch() public {
uint256[] memory rootIndices = new uint256[](2);
uint256[] memory points = new uint256[](1); // Wrong length
bytes32[][] memory proofs = new bytes32[][](2);
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
vm.prank(operatorAddress);
vm.expectRevert(abi.encodeWithSelector(IRewardsRegistryErrors.ArrayLengthMismatch.selector));
serviceManager.claimOperatorRewardsBatch(operatorSetId, rootIndices, points, proofs);
}
function test_claimOperatorRewardsBatch_AlreadyClaimedIndex() public {
// First claim from index 1
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
vm.prank(operatorAddress);
serviceManager.claimOperatorRewards(
operatorSetId, 1, secondOperatorPoints, secondValidProof
);
// Now try to batch claim including the already claimed index 1
uint256[] memory rootIndices = new uint256[](2);
rootIndices[0] = 0;
rootIndices[1] = 1; // Already claimed
uint256[] memory points = new uint256[](2);
points[0] = operatorPoints;
points[1] = secondOperatorPoints;
bytes32[][] memory proofs = new bytes32[][](2);
proofs[0] = validProof;
proofs[1] = secondValidProof;
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IRewardsRegistryErrors.RewardsAlreadyClaimedForIndex.selector)
);
serviceManager.claimOperatorRewardsBatch(operatorSetId, rootIndices, points, proofs);
}
function test_claimOperatorRewardsBatch_EmptyBatch() public {
uint256[] memory rootIndices = new uint256[](0);
uint256[] memory points = new uint256[](0);
bytes32[][] memory proofs = new bytes32[][](0);
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(true)
);
uint256 initialBalance = operatorAddress.balance;
vm.prank(operatorAddress);
serviceManager.claimOperatorRewardsBatch(operatorSetId, rootIndices, points, proofs);
// Balance should remain unchanged
assertEq(
operatorAddress.balance,
initialBalance,
"Balance should remain unchanged for empty batch"
);
}
function test_integration_multipleOperatorSets() public {
@ -165,11 +499,11 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
(bytes32 leftLeaf, bytes32 rightLeaf) = secondLeaf < secondSiblingLeaf
? (secondLeaf, secondSiblingLeaf)
: (secondSiblingLeaf, secondLeaf);
bytes32 secondMerkleRoot = keccak256(abi.encodePacked(leftLeaf, rightLeaf));
bytes32 secondRegistryMerkleRoot = keccak256(abi.encodePacked(leftLeaf, rightLeaf));
// Set the merkle root in the second registry
vm.prank(mockRewardsAgent);
secondRegistry.updateRewardsMerkleRoot(secondMerkleRoot);
secondRegistry.updateRewardsMerkleRoot(secondRegistryMerkleRoot);
// Fund the second registry
vm.deal(address(secondRegistry), 1000 ether);
@ -178,7 +512,7 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
bytes32[] memory secondProof = new bytes32[](1);
secondProof[0] = secondSiblingLeaf;
// Claim from first registry
// Claim from first registry (uses latest merkle root - index 2)
uint256 initialBalance = operatorAddress.balance;
vm.mockCall(
address(allocationManager),
@ -186,19 +520,21 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
abi.encode(true)
);
vm.prank(operatorAddress);
serviceManager.claimOperatorRewards(operatorSetId, operatorPoints, validProof);
serviceManager.claimLatestOperatorRewards(
operatorSetId, thirdOperatorPoints, thirdValidProof
); // Use latest root
// Verify balance after first claim
assertEq(
operatorAddress.balance,
initialBalance + operatorPoints,
initialBalance + thirdOperatorPoints,
"Operator should receive correct rewards from first registry"
);
// Claim from second registry
uint256 balanceAfterFirstClaim = operatorAddress.balance;
vm.prank(operatorAddress);
serviceManager.claimOperatorRewards(secondOperatorSetId, operatorPoints, secondProof);
serviceManager.claimLatestOperatorRewards(secondOperatorSetId, operatorPoints, secondProof);
// Verify balance after second claim
assertEq(
@ -207,4 +543,53 @@ contract ServiceManagerRewardsRegistryTest is AVSDeployer {
"Operator should receive correct rewards from second registry"
);
}
function test_claimLatestOperatorRewards_NotInOperatorSet() public {
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(false) // Operator is NOT in the set
);
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IServiceManagerErrors.OperatorNotInOperatorSet.selector)
);
serviceManager.claimLatestOperatorRewards(operatorSetId, operatorPoints, validProof);
}
function test_claimOperatorRewards_NotInOperatorSet() public {
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(false) // Operator is NOT in the set
);
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IServiceManagerErrors.OperatorNotInOperatorSet.selector)
);
serviceManager.claimOperatorRewards(operatorSetId, 0, operatorPoints, validProof);
}
function test_claimOperatorRewardsBatch_NotInOperatorSet() public {
uint256[] memory rootIndices = new uint256[](1);
rootIndices[0] = 0;
uint256[] memory points = new uint256[](1);
points[0] = operatorPoints;
bytes32[][] memory proofs = new bytes32[][](1);
proofs[0] = validProof;
vm.mockCall(
address(allocationManager),
abi.encodeWithSelector(IAllocationManager.isMemberOfOperatorSet.selector),
abi.encode(false) // Operator is NOT in the set
);
vm.prank(operatorAddress);
vm.expectRevert(
abi.encodeWithSelector(IServiceManagerErrors.OperatorNotInOperatorSet.selector)
);
serviceManager.claimOperatorRewardsBatch(operatorSetId, rootIndices, points, proofs);
}
}

View file

@ -101,10 +101,12 @@ contract SnowbridgeIntegrationTest is SnowbridgeAndAVSDeployer {
);
vm.startPrank(_validatorAddresses[0]);
vm.expectEmit(address(rewardsRegistry));
emit IRewardsRegistryEvents.RewardsClaimed(
_validatorAddresses[0], _validatorPoints[0], uint256(_validatorPoints[0])
emit IRewardsRegistryEvents.RewardsClaimedForIndex(
_validatorAddresses[0], 0, _validatorPoints[0], uint256(_validatorPoints[0])
);
serviceManager.claimLatestOperatorRewards(
0, _validatorPoints[0], rewardsProofFirstValidator
);
serviceManager.claimOperatorRewards(0, _validatorPoints[0], rewardsProofFirstValidator);
vm.stopPrank();
// Check that the validator has received the rewards.
@ -121,10 +123,10 @@ contract SnowbridgeIntegrationTest is SnowbridgeAndAVSDeployer {
// Claim rewards for the last validator.
vm.startPrank(_validatorAddresses[9]);
vm.expectEmit(address(rewardsRegistry));
emit IRewardsRegistryEvents.RewardsClaimed(
_validatorAddresses[9], _validatorPoints[9], uint256(_validatorPoints[9])
emit IRewardsRegistryEvents.RewardsClaimedForIndex(
_validatorAddresses[9], 0, _validatorPoints[9], uint256(_validatorPoints[9])
);
serviceManager.claimOperatorRewards(0, _validatorPoints[9], rewardsProofLastValidator);
serviceManager.claimLatestOperatorRewards(0, _validatorPoints[9], rewardsProofLastValidator);
vm.stopPrank();
// Check that the last validator has received the rewards.

View file

@ -2105,10 +2105,34 @@ export const dataHavenServiceManagerAbi = [
{ name: 'operatorPoints', internalType: 'uint256', type: 'uint256' },
{ name: 'proof', internalType: 'bytes32[]', type: 'bytes32[]' },
],
name: 'claimLatestOperatorRewards',
outputs: [],
stateMutability: 'nonpayable',
},
{
type: 'function',
inputs: [
{ name: 'operatorSetId', internalType: 'uint32', type: 'uint32' },
{ name: 'rootIndex', internalType: 'uint256', type: 'uint256' },
{ name: 'operatorPoints', internalType: 'uint256', type: 'uint256' },
{ name: 'proof', internalType: 'bytes32[]', type: 'bytes32[]' },
],
name: 'claimOperatorRewards',
outputs: [],
stateMutability: 'nonpayable',
},
{
type: 'function',
inputs: [
{ name: 'operatorSetId', internalType: 'uint32', type: 'uint32' },
{ name: 'rootIndices', internalType: 'uint256[]', type: 'uint256[]' },
{ name: 'operatorPoints', internalType: 'uint256[]', type: 'uint256[]' },
{ name: 'proofs', internalType: 'bytes32[][]', type: 'bytes32[][]' },
],
name: 'claimOperatorRewardsBatch',
outputs: [],
stateMutability: 'nonpayable',
},
{
type: 'function',
inputs: [
@ -8113,24 +8137,89 @@ export const rewardsRegistryAbi = [
{ name: 'operatorPoints', internalType: 'uint256', type: 'uint256' },
{ name: 'proof', internalType: 'bytes32[]', type: 'bytes32[]' },
],
name: 'claimLatestRewards',
outputs: [],
stateMutability: 'nonpayable',
},
{
type: 'function',
inputs: [
{ name: 'operatorAddress', internalType: 'address', type: 'address' },
{ name: 'rootIndex', internalType: 'uint256', type: 'uint256' },
{ name: 'operatorPoints', internalType: 'uint256', type: 'uint256' },
{ name: 'proof', internalType: 'bytes32[]', type: 'bytes32[]' },
],
name: 'claimRewards',
outputs: [],
stateMutability: 'nonpayable',
},
{
type: 'function',
inputs: [
{ name: 'operatorAddress', internalType: 'address', type: 'address' },
{ name: 'rootIndices', internalType: 'uint256[]', type: 'uint256[]' },
{ name: 'operatorPoints', internalType: 'uint256[]', type: 'uint256[]' },
{ name: 'proofs', internalType: 'bytes32[][]', type: 'bytes32[][]' },
],
name: 'claimRewardsBatch',
outputs: [],
stateMutability: 'nonpayable',
},
{
type: 'function',
inputs: [],
name: 'lastRewardsMerkleRoot',
name: 'getLatestMerkleRoot',
outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [{ name: '', internalType: 'address', type: 'address' }],
name: 'operatorToLastClaimedRoot',
inputs: [],
name: 'getLatestMerkleRootIndex',
outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [{ name: 'index', internalType: 'uint256', type: 'uint256' }],
name: 'getMerkleRootByIndex',
outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [],
name: 'getMerkleRootHistoryLength',
outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [
{ name: 'operatorAddress', internalType: 'address', type: 'address' },
{ name: 'rootIndex', internalType: 'uint256', type: 'uint256' },
],
name: 'hasClaimedByIndex',
outputs: [{ name: '', internalType: 'bool', type: 'bool' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
name: 'merkleRootHistory',
outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [
{ name: '', internalType: 'address', type: 'address' },
{ name: '', internalType: 'uint256', type: 'uint256' },
],
name: 'operatorClaimedByIndex',
outputs: [{ name: '', internalType: 'bool', type: 'bool' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [],
@ -8166,6 +8255,43 @@ export const rewardsRegistryAbi = [
type: 'address',
indexed: true,
},
{
name: 'rootIndices',
internalType: 'uint256[]',
type: 'uint256[]',
indexed: false,
},
{
name: 'points',
internalType: 'uint256[]',
type: 'uint256[]',
indexed: false,
},
{
name: 'totalRewardsAmount',
internalType: 'uint256',
type: 'uint256',
indexed: false,
},
],
name: 'RewardsBatchClaimedForIndices',
},
{
type: 'event',
anonymous: false,
inputs: [
{
name: 'operatorAddress',
internalType: 'address',
type: 'address',
indexed: true,
},
{
name: 'rootIndex',
internalType: 'uint256',
type: 'uint256',
indexed: true,
},
{
name: 'points',
internalType: 'uint256',
@ -8179,7 +8305,7 @@ export const rewardsRegistryAbi = [
indexed: false,
},
],
name: 'RewardsClaimed',
name: 'RewardsClaimedForIndex',
},
{
type: 'event',
@ -8197,13 +8323,21 @@ export const rewardsRegistryAbi = [
type: 'bytes32',
indexed: false,
},
{
name: 'newRootIndex',
internalType: 'uint256',
type: 'uint256',
indexed: false,
},
],
name: 'RewardsMerkleRootUpdated',
},
{ type: 'error', inputs: [], name: 'ArrayLengthMismatch' },
{ type: 'error', inputs: [], name: 'InvalidMerkleProof' },
{ type: 'error', inputs: [], name: 'InvalidMerkleRootIndex' },
{ type: 'error', inputs: [], name: 'OnlyAVS' },
{ type: 'error', inputs: [], name: 'OnlyRewardsAgent' },
{ type: 'error', inputs: [], name: 'RewardsAlreadyClaimed' },
{ type: 'error', inputs: [], name: 'RewardsAlreadyClaimedForIndex' },
{ type: 'error', inputs: [], name: 'RewardsMerkleRootNotSet' },
{ type: 'error', inputs: [], name: 'RewardsTransferFailed' },
] as const
@ -11288,6 +11422,15 @@ export const writeDataHavenServiceManagerAddValidatorToAllowlist =
functionName: 'addValidatorToAllowlist',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"claimLatestOperatorRewards"`
*/
export const writeDataHavenServiceManagerClaimLatestOperatorRewards =
/*#__PURE__*/ createWriteContract({
abi: dataHavenServiceManagerAbi,
functionName: 'claimLatestOperatorRewards',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"claimOperatorRewards"`
*/
@ -11297,6 +11440,15 @@ export const writeDataHavenServiceManagerClaimOperatorRewards =
functionName: 'claimOperatorRewards',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"claimOperatorRewardsBatch"`
*/
export const writeDataHavenServiceManagerClaimOperatorRewardsBatch =
/*#__PURE__*/ createWriteContract({
abi: dataHavenServiceManagerAbi,
functionName: 'claimOperatorRewardsBatch',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"createAVSRewardsSubmission"`
*/
@ -11672,6 +11824,15 @@ export const simulateDataHavenServiceManagerAddValidatorToAllowlist =
functionName: 'addValidatorToAllowlist',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"claimLatestOperatorRewards"`
*/
export const simulateDataHavenServiceManagerClaimLatestOperatorRewards =
/*#__PURE__*/ createSimulateContract({
abi: dataHavenServiceManagerAbi,
functionName: 'claimLatestOperatorRewards',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"claimOperatorRewards"`
*/
@ -11681,6 +11842,15 @@ export const simulateDataHavenServiceManagerClaimOperatorRewards =
functionName: 'claimOperatorRewards',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"claimOperatorRewardsBatch"`
*/
export const simulateDataHavenServiceManagerClaimOperatorRewardsBatch =
/*#__PURE__*/ createSimulateContract({
abi: dataHavenServiceManagerAbi,
functionName: 'claimOperatorRewardsBatch',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"createAVSRewardsSubmission"`
*/
@ -15954,21 +16124,66 @@ export const readRewardsRegistryAvs = /*#__PURE__*/ createReadContract({
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"lastRewardsMerkleRoot"`
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"getLatestMerkleRoot"`
*/
export const readRewardsRegistryLastRewardsMerkleRoot =
export const readRewardsRegistryGetLatestMerkleRoot =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'lastRewardsMerkleRoot',
functionName: 'getLatestMerkleRoot',
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"operatorToLastClaimedRoot"`
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"getLatestMerkleRootIndex"`
*/
export const readRewardsRegistryOperatorToLastClaimedRoot =
export const readRewardsRegistryGetLatestMerkleRootIndex =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'operatorToLastClaimedRoot',
functionName: 'getLatestMerkleRootIndex',
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"getMerkleRootByIndex"`
*/
export const readRewardsRegistryGetMerkleRootByIndex =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'getMerkleRootByIndex',
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"getMerkleRootHistoryLength"`
*/
export const readRewardsRegistryGetMerkleRootHistoryLength =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'getMerkleRootHistoryLength',
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"hasClaimedByIndex"`
*/
export const readRewardsRegistryHasClaimedByIndex =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'hasClaimedByIndex',
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"merkleRootHistory"`
*/
export const readRewardsRegistryMerkleRootHistory =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'merkleRootHistory',
})
/**
* Wraps __{@link readContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"operatorClaimedByIndex"`
*/
export const readRewardsRegistryOperatorClaimedByIndex =
/*#__PURE__*/ createReadContract({
abi: rewardsRegistryAbi,
functionName: 'operatorClaimedByIndex',
})
/**
@ -15985,6 +16200,15 @@ export const writeRewardsRegistry = /*#__PURE__*/ createWriteContract({
abi: rewardsRegistryAbi,
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"claimLatestRewards"`
*/
export const writeRewardsRegistryClaimLatestRewards =
/*#__PURE__*/ createWriteContract({
abi: rewardsRegistryAbi,
functionName: 'claimLatestRewards',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"claimRewards"`
*/
@ -15994,6 +16218,15 @@ export const writeRewardsRegistryClaimRewards =
functionName: 'claimRewards',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"claimRewardsBatch"`
*/
export const writeRewardsRegistryClaimRewardsBatch =
/*#__PURE__*/ createWriteContract({
abi: rewardsRegistryAbi,
functionName: 'claimRewardsBatch',
})
/**
* Wraps __{@link writeContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"setRewardsAgent"`
*/
@ -16019,6 +16252,15 @@ export const simulateRewardsRegistry = /*#__PURE__*/ createSimulateContract({
abi: rewardsRegistryAbi,
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"claimLatestRewards"`
*/
export const simulateRewardsRegistryClaimLatestRewards =
/*#__PURE__*/ createSimulateContract({
abi: rewardsRegistryAbi,
functionName: 'claimLatestRewards',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"claimRewards"`
*/
@ -16028,6 +16270,15 @@ export const simulateRewardsRegistryClaimRewards =
functionName: 'claimRewards',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"claimRewardsBatch"`
*/
export const simulateRewardsRegistryClaimRewardsBatch =
/*#__PURE__*/ createSimulateContract({
abi: rewardsRegistryAbi,
functionName: 'claimRewardsBatch',
})
/**
* Wraps __{@link simulateContract}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `functionName` set to `"setRewardsAgent"`
*/
@ -16054,12 +16305,21 @@ export const watchRewardsRegistryEvent = /*#__PURE__*/ createWatchContractEvent(
)
/**
* Wraps __{@link watchContractEvent}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `eventName` set to `"RewardsClaimed"`
* Wraps __{@link watchContractEvent}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `eventName` set to `"RewardsBatchClaimedForIndices"`
*/
export const watchRewardsRegistryRewardsClaimedEvent =
export const watchRewardsRegistryRewardsBatchClaimedForIndicesEvent =
/*#__PURE__*/ createWatchContractEvent({
abi: rewardsRegistryAbi,
eventName: 'RewardsClaimed',
eventName: 'RewardsBatchClaimedForIndices',
})
/**
* Wraps __{@link watchContractEvent}__ with `abi` set to __{@link rewardsRegistryAbi}__ and `eventName` set to `"RewardsClaimedForIndex"`
*/
export const watchRewardsRegistryRewardsClaimedForIndexEvent =
/*#__PURE__*/ createWatchContractEvent({
abi: rewardsRegistryAbi,
eventName: 'RewardsClaimedForIndex',
})
/**