-
Notifications
You must be signed in to change notification settings - Fork 30
AVS Rewards #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
AVS Rewards #24
Conversation
WalkthroughThis update introduces a comprehensive overhaul of the Reclaim AVS (Automated Validator Service) contract suite and its associated deployment, testing, and integration tooling. Major changes include a complete rewrite of the Solidity contracts for service management, slashing coordination, and task management, along with new deployment scripts and supporting libraries. The TypeScript contract bindings and configuration files are regenerated to match the new contract architecture. Legacy deployment scripts, shell utilities, and outdated ABIs are removed. The documentation and package scripts are updated to reflect the new workflow and contract structure. Changes
Sequence Diagram(s)sequenceDiagram
participant Deployer as ReclaimDeployer Script
participant CoreLib as CoreDeploymentLib
participant ProxyAdmin as ProxyAdmin
participant ReclaimLib as ReclaimDeploymentLib
participant ServiceManager as ReclaimServiceManager
participant TaskManager as ReclaimTaskManager
participant SlashingCoordinator as ReclaimSlashingRegistryCoordinator
Deployer->>CoreLib: Read core deployment config (JSON)
Deployer->>ProxyAdmin: Deploy ProxyAdmin
Deployer->>ReclaimLib: Deploy Reclaim contracts (with ProxyAdmin, core config, strategy, setup config)
ReclaimLib->>ServiceManager: Deploy behind proxy
ReclaimLib->>TaskManager: Deploy behind proxy
ReclaimLib->>SlashingCoordinator: Deploy behind proxy
Deployer->>Deployer: Log deployed addresses
Deployer->>Deployer: Write deployment metadata (JSON)
sequenceDiagram
participant User as User
participant TaskManager as ReclaimTaskManager
participant ServiceManager as ReclaimServiceManager
participant RewardsCoordinator as RewardsCoordinator
User->>TaskManager: createNewTask(request, signature)
TaskManager->>TaskManager: Validate fee, signature, timestamp
TaskManager->>TaskManager: Pick random operators
TaskManager->>TaskManager: Store task hash
TaskManager->>User: Emit NewTaskCreated event
User->>TaskManager: taskCompleted(completedTask, referenceTaskIndex)
TaskManager->>TaskManager: Validate task hash and expiry
TaskManager->>TaskManager: Validate claim signatures
TaskManager->>TaskManager: Distribute reward
TaskManager->>RewardsCoordinator: Submit rewards
TaskManager->>User: Emit TaskCompleted event
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 26
🔭 Outside diff range comments (1)
src/avs/client/create-claim-on-avs.ts (1)
3-3
:⚠️ Potential issueFix import error for IReclaimServiceManager
The import statement is causing a pipeline failure as the TypeScript compiler cannot find the IReclaimServiceManager export.
Based on the pipeline failure, the correct import path or interface name needs to be updated:
-import { IReclaimServiceManager, NewTaskCreatedEventObject, TaskCompletedEventObject } from 'src/avs/contracts/ReclaimServiceManager' +import { ReclaimServiceManager, NewTaskCreatedEventObject, TaskCompletedEventObject } from 'src/avs/contracts/ReclaimServiceManager'If the interface still exists but has been moved, update the import path accordingly.
🧰 Tools
🪛 GitHub Check: test
[failure] 3-3:
Module '"src/avs/contracts/ReclaimServiceManager"' has no exported member 'TaskCompletedEventObject'.
[failure] 3-3:
Module '"src/avs/contracts/ReclaimServiceManager"' has no exported member 'NewTaskCreatedEventObject'.
[failure] 3-3:
'"src/avs/contracts/ReclaimServiceManager"' has no exported member named 'IReclaimServiceManager'. Did you mean 'ReclaimServiceManager'?
[failure] 3-3:
Module '"src/avs/contracts/ReclaimServiceManager"' has no exported member 'TaskCompletedEventObject'.
[failure] 3-3:
Module '"src/avs/contracts/ReclaimServiceManager"' has no exported member 'NewTaskCreatedEventObject'.
[failure] 3-3:
'"src/avs/contracts/ReclaimServiceManager"' has no exported member named 'IReclaimServiceManager'. Did you mean 'ReclaimServiceManager'?🪛 GitHub Actions: Test
[error] 3-3: TypeScript error TS2724: 'src/avs/contracts/ReclaimServiceManager' has no exported member named 'IReclaimServiceManager'. Did you mean 'ReclaimServiceManager'?
🧹 Nitpick comments (30)
.gitignore (2)
17-17
: Anchor the ignore rule for the top-level envs directory
Use/envs/
to unambiguously ignore only the rootenvs
folder (and its contents) without matching any nested folders namedenvs
.
18-18
: Use a wildcard or directory rule for mockData
Instead of listingavs/contracts/mockData/payments.json
, consideravs/contracts/mockData/*.json
(or/avs/contracts/mockData/
) to automatically ignore any mock JSON files generated in this folder.avs/contracts/src/utils/Random.sol (1)
7-13
: Explicit Typing and Insecure Randomness Disclaimer
The change to explicitly returnuint256
is consistent with overall type-safety improvements. However, block-based pseudo-randomness (block.prevrandao
+block.timestamp
) is predictable and not suitable for critical on-chain randomness. Consider documenting this limitation or integrating a secure VRF oracle if needed..gitmodules (1)
6-7
: Pin Submodule to Specific Commit
Lockingeigenlayer-middleware
to a fixed commit hash is an excellent practice for reproducibility. For consistency, consider pinning other submodules (e.g.,forge-std
) to specific commit SHAs or tags as well.avs/contracts/deployments/reclaim/31337.json (1)
1-1
: Use Numeric Types for Timestamps and Block Numbers
Currently,timestamp
andblock_number
are stored as string values. Consider using JSON numbers to simplify parsing in deployment scripts and avoid unnecessary type conversions.avs/contracts/script/utils/FundOperator.sol (1)
6-12
: Simple utility function for minting tokens to operatorsThis utility library provides a clean implementation for minting test tokens to operators. The function is appropriately marked as
internal
for use in deployment scripts.Consider adding basic input validation and error handling:
function fund_operator(address erc20, address operator, uint256 amount) internal { + require(erc20 != address(0), "Invalid ERC20 address"); + require(operator != address(0), "Invalid operator address"); + require(amount > 0, "Amount must be greater than zero"); ERC20Mock erc20_contract = ERC20Mock(erc20); erc20_contract.mint(operator, amount); + + // Optional: Return a boolean or emit an event to confirm success }docs/avs.md (1)
138-142
: Replace hard tabs with spaces to satisfy Markdown linting.The lines at 138–142 use hard tabs, triggering MD010 (no-hard-tabs). Converting these to spaces will resolve the markdownlint warnings and improve consistency.
Example diff:
- - `npm run generate:avs-types` to generate the types for the AVS contracts + - `npm run generate:avs-types` to generate the types for the AVS contracts - - before generating types, make sure contracts are built first + - before generating types, make sure contracts are built first - - `npm run start:chain` + - `npm run start:chain` - - This will start the local chain, build the contracts & deploy them on the local chain too. + - This will start the local chain, build the contracts & deploy them on the local chain too.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
138-138: Hard tabs
Column: 1(MD010, no-hard-tabs)
139-139: Hard tabs
Column: 1(MD010, no-hard-tabs)
141-141: Hard tabs
Column: 1(MD010, no-hard-tabs)
142-142: Hard tabs
Column: 1(MD010, no-hard-tabs)
avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh (1)
23-38
: Enhance Anvil readiness check instead of fixed sleep.Relying on a hard-coded
sleep 2
may fail if Anvil takes longer to initialize. Consider polling the JSON-RPC endpoint until it responds, for example:# Wait for Anvil RPC to become available until curl -sSf $RPC_URL/jsonrpc?method=eth_blockNumber >/dev/null; do echo "Waiting for Anvil RPC..." sleep 1 done echo "Anvil is up"This approach makes the script more robust across different environments.
avs/contracts/src/utils/Claims.sol (1)
88-91
: Use OpenZeppelin helper for Ethereum Signed Messages.Instead of manually prefixing and hashing, you can leverage
ECDSA.toEthSignedMessageHash
:- bytes32 signedHash = - keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringUtils.uint2str(content.length), content)); - return ECDSA.recover(signedHash, signature); + bytes32 signedHash = ECDSA.toEthSignedMessageHash(content); + return ECDSA.recover(signedHash, signature);This improves clarity and aligns with best practices.
avs/utils/contract-data-gen.sh (1)
1-13
: Add error handling to the script.The script lacks error checking for the existence of directories and the successful execution of commands. Consider adding basic error handling to make the script more robust.
Here's a suggested improvement:
+#!/bin/bash +set -e # Exit on error + +# Check if typechain is installed +if ! command -v typechain &> /dev/null; then + echo "Error: typechain is not installed. Please install it with 'npm install -g typechain'" + exit 1 +fi + +# Check if destination directory exists and create backup if needed +if [ -d "src/avs/contracts" ]; then + echo "Removing existing contracts directory..." +fi + rm -rf src/avs/contracts mkdir -p src/avs/contracts + +echo "Generating TypeScript contract bindings..." typechain -- --target ethers-v5 --out-dir src/avs/contracts \ avs/contracts/out/ReclaimServiceManager.sol/*.json \ avs/contracts/out/ReclaimTaskManager.sol/*.json \ avs/contracts/out/ReclaimSlashingRegistryCoordinator.sol/*.json \ avs/contracts/out/AllocationManager.sol/*.json \ avs/contracts/out/AVSDirectory.sol/*.json \ avs/contracts/out/DelegationManager.sol/*.json \ avs/contracts/out/ECDSAStakeRegistry.sol/*.json \ avs/contracts/out/ERC20Mock.sol/*.json \ avs/contracts/out/RewardsCoordinator.sol/*.json \ avs/contracts/out/SocketRegistry.sol/*.json + +echo "TypeScript contract bindings generated successfully"🧰 Tools
🪛 Shellcheck (0.10.0)
[error] 13-13: Couldn't parse this escaped char. Fix to allow more checks.
(SC1073)
[error] 13-13: Fix any mentioned problems and try again.
(SC1072)
avs/contracts/src/utils/BytesUtils.sol (1)
8-18
: Add a clarifying comment about value range limitation.Since the function loads 4 bytes into a uint32 variable, it would be helpful to clarify that the returned uint256 value will always be within the uint32 range (0 to 2^32-1).
function bytesToUInt(bytes memory data, uint256 offset) internal pure returns (uint256) { require(offset + 4 <= data.length, "Offset + 4 must be within data bounds"); + // Note: This function returns a uint256, but the value is limited to uint32 range (0 to 2^32-1) uint32 result; assembly { // Load the 32 bits (4 bytes) from the data at the given offset into the result variable result := mload(add(add(data, 0x4), offset)) } return result; }
avs/utils/submit-payments-root.sh (2)
6-13
: Hardcoded sender address without documentationThe script uses a hardcoded sender address without explaining what this address represents. This reduces script maintainability.
Consider using an environment variable for the sender address with a default fallback:
+# Use environment variable for sender address or default to a local development address +SENDER_ADDRESS=${SENDER_ADDRESS:-"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"} + forge script script/SetupPayments.s.sol \ --rpc-url http://localhost:8545 \ --broadcast -v --via-ir \ --revert-strings debug \ - --sender 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 \ + --sender $SENDER_ADDRESS \ --sig "runPaymentRoot(address[], uint32, uint32, uint32)" \ "[$OPERATOR_ADDRESS]" \ $END_TIMESTAMP 1 $PAYMENT
14-20
: Remove or document commented-out codeThere's commented-out code that appears to be an alternative command for executing process claims. Either remove this code if it's no longer needed or add a comment explaining why it's kept.
Either remove the commented code or add documentation:
-# forge script script/SetupPayments.s.sol \ -# --rpc-url http://localhost:8545 \ -# --broadcast -v --via-ir \ -# --revert-strings debug \ -# --sender 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 \ -# --sig "executeProcessClaim(address)" \ -# '0x1234567890123456789012345678901234567890' +# Alternative command to execute process claim (uncomment to use): +# forge script script/SetupPayments.s.sol \ +# --rpc-url http://localhost:8545 \ +# --broadcast -v --via-ir \ +# --revert-strings debug \ +# --sender $SENDER_ADDRESS \ +# --sig "executeProcessClaim(address)" \ +# '0x1234567890123456789012345678901234567890'avs/contracts/script/DeployEigenLayerCore.s.sol (1)
23-32
: Add logging and error handling to deployment processThe deployment process lacks status logging and error handling for key steps. Adding these would improve observability and help diagnose deployment issues.
Consider adding logging and basic validation:
function run() external { + console2.log("Starting EigenLayer Core deployment with deployer:", deployer); vm.startBroadcast(deployer); configData = CoreDeploymentLib.readDeploymentConfigValues("config/core/", block.chainid); + console2.log("Loaded configuration for chain ID:", block.chainid); proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + console2.log("Deployed ProxyAdmin at:", proxyAdmin); deploymentData = CoreDeploymentLib.deployContracts(deployer, proxyAdmin, configData); + console2.log("Deployment completed successfully"); vm.stopBroadcast(); string memory deploymentPath = "script/deployments/core/"; CoreDeploymentLib.writeDeploymentJson(deploymentPath, block.chainid, deploymentData); + console2.log("Deployment data written to:", deploymentPath); }avs/contracts/script/utils/SetupPaymentsLib.sol (2)
87-125
: Claim processing implementation.The function handles claim processing with appropriate structure, but contains a misleading comment about assumptions.
The comment on line 111 states "this workflow assumes a new root submitted for every payment claimed" and uses
getDistributionRootsLength() - 1
to get the latest root index. This approach might be risky in production environments and the comment should be clarified or the code should be more robust.- // this workflow assumes a new root submitted for every payment claimed. So we get the latest rood index to process a claim for - uint256 rootIndex = rewardsCoordinator.getDistributionRootsLength() - 1; + // Get the latest root index for processing this claim + // NOTE: This assumes the latest root is the one we want to claim against + // In production, you may want to pass the specific rootIndex as a parameter + uint256 rootIndex = rewardsCoordinator.getDistributionRootsLength() - 1;
262-301
: Merkle root calculation with commented code.The function contains commented-out code that should be removed or explained.
The function contains commented-out code that duplicates the functionality now moved to the
padLeaves
function. This commented code should be removed for clarity:function merkleizeKeccak(bytes32[] memory leaves) internal pure returns (bytes32) { - // uint256 paddedLength = 2; - // while(paddedLength < leaves.length) { - // paddedLength <<= 1; - // } - - // bytes32[] memory paddedLeaves = new bytes32[](paddedLength); - // for (uint256 i = 0; i < leaves.length; i++) { - // paddedLeaves[i] = leaves[i]; - // } leaves = padLeaves(leaves);avs/contracts/src/test/ContractsRegistry.sol (1)
7-9
: Document deterministic deployment pattern.The contract deployment approach using a specific address to ensure a deterministic contract address is good for testing. Consider adding this information to your documentation for other developers.
src/avs/contracts/ReclaimSlashingRegistryCoordinator.ts (1)
667-671
: Consider addressing the empty interface warning.The static analysis tool flagged an empty interface at this location. While this is auto-generated code and likely a false positive, it's worth reviewing the underlying contract or type generation settings.
🧰 Tools
🪛 Biome (1.9.4)
[error] 667-667: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
avs/contracts/src/IReclaimTaskManager.sol (1)
35-39
: Consider adding fee validation events.The interface includes fee-related fields but lacks events for fee validation or handling, which would be useful for front-end applications to track fee adequacy.
Consider adding events for fee validation:
/** * Emitted when a fee is deemed insufficient for a task. */ event InsufficientFee(uint256 providedFee, uint256 requiredFee, bytes32 claimHash);Also applies to: 104-108
avs/contracts/script/ReclaimDeployer.s.sol (2)
41-74
: Prune unused state variables to avoid lint warnings & accidental storage costsA large block of state variables (
AGGREGATOR_ADDR
,TASK_GENERATOR_ADDR
,CONTRACTS_REGISTRY_ADDR
,erc20MockStrategy
, several…Implementation
vars, etc.) are never referenced elsewhere in the script.
That clutters the code and, if the script contract were ever deployed, each variable would unnecessarily consume storage.- address public AGGREGATOR_ADDR; - address public TASK_GENERATOR_ADDR; - address public CONTRACTS_REGISTRY_ADDR; - ... - StrategyBaseTVLLimits public erc20MockStrategy; - ... - regcoord.IRegistryCoordinator public registryCoordinatorImplementation; - ... - IServiceManager public serviceManagerImplementation;Either delete them or convert them to local variables inside
run()
when/if they become necessary.
100-102
: Provide revert reason on ETH funding failureThe low-level call that transfers 0.1 ETH to
operator_2
is wrapped in a barerequire(s);
.
Adding a helpful error string eases debugging and CI failure triage.- require(s); + require(s, "ReclaimDeployer: funding operator_2 with ETH failed");src/avs/contracts/DelegationManager.ts (1)
1-4
: Generated bindings – consider excluding from lint/testsThis file is clearly auto-generated by
typechain
(or a similar tool). To avoid noisy lint / coverage failures and accidental edits, add it (and the wholesrc/avs/contracts/*
bindings folder) to.eslintignore
,.prettierignore
, your test-coverage exclude list, and any formatting hooks.src/avs/contracts/ReclaimServiceManager.ts (1)
29-93
: Duplication of shared structs – keep a single source of truth
StrategyAndMultiplierStruct
,RewardsSubmissionStruct
, … are re-declared here even though the exact same definitions already exist insrc/avs/contracts/RewardsCoordinator.ts
.
When different bindings carry their own copies, a downstream refactor of the canonical struct can silently diverge and break encoding/decoding at runtime.Where possible, import the canonical definition (or generate all bindings in a single run so they share the same
namespace
). If generation tooling forbids that, at minimum add a comment pointing to the canonical contract to avoid accidental drift.avs/contracts/script/SetupPayments.s.sol (2)
40-41
: Typo & unused variablecumumlativePaymentMultiplier
- Spelling:
cumumlativePaymentMultiplier
➜cumulativePaymentMultiplier
.- The variable is never written to (only logged). Remove it or compute a meaningful value to keep the log useful.
- uint256 cumumlativePaymentMultiplier; + uint256 cumulativePaymentMultiplier; // ← compute & use or removeAlso applies to: 203-204
41-45
:nonceSender
declared but never usedUnless a future change will reference
nonceSender
, please remove it to keep the script lean.avs/contracts/src/ReclaimServiceManager.sol (1)
74-83
: Linear search on dynamic array is O(n); prefer mapping for whitelists
isOperatorWhitelisted
iterates over the wholewhitelistedOperators
array each call, which is gas-heavy and scales poorly.A simple mapping
mapping(address => bool) public isOperatorWhitelisted;
would give O(1) checks and simplify add/remove logic.avs/contracts/src/ReclaimTaskManager.sol (4)
86-107
: Cannot reset metadata fields to zero
updateTaskCreationMetadata
silently refuses to accept a value of0
, preventing the owner from lowering requirements (e.g., turn offminFee
). Consider an explicit “unset” sentinel (e.g.,type(uint256).max
) or pass each field individually.
134-138
: PreferSafeERC20.safeTransferFrom
Directly checking
IERC20.transferFrom
’s boolean return is fragile (non-standard tokens may revert or return no value). Use OpenZeppelin’sSafeERC20
which handles all cases and prevents accidental silent failures.-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; ... IERC20 token = getToken(); -Srequire(token.transferFrom(msg.sender, address(this), request.fee), "Failed to transfer fee"); +SafeERC20.safeTransferFrom(token, msg.sender, address(this), request.fee);
230-234
: Reward remainder is silently burned
perOpReward = reward / operatorAddrs.length
truncates the remainder; any wei left over never reaches operators nor returns to the sender.uint256 perOpReward = reward / operatorAddrs.length; uint256 remainder = reward % operatorAddrs.length; for (uint256 i = 0; i < operatorAddrs.length; i++) { ops[i].operator = operatorAddrs[i]; - ops[i].amount = perOpReward; + ops[i].amount = perOpReward + (i == 0 ? remainder : 0); // first op gets remainder }Alternatively, store the remainder for the next task.
357-367
: Inefficient O(n²) bubble sortFor small arrays (<10) this is fine, but if operator sets grow, consider an O(n log n) sort (e.g., quick-sort) or use
std-sort
-style libraries to save gas.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
📒 Files selected for processing (64)
.gitignore
(1 hunks).gitmodules
(1 hunks)avs/contracts/config/core/31337.json
(1 hunks)avs/contracts/config/reclaim/31337.json
(1 hunks)avs/contracts/deployments/core/31337.json
(1 hunks)avs/contracts/deployments/reclaim/31337.json
(1 hunks)avs/contracts/foundry.toml
(1 hunks)avs/contracts/lib/eigenlayer-middleware
(1 hunks)avs/contracts/lib/forge-std
(1 hunks)avs/contracts/lib/openzeppelin-contracts
(0 hunks)avs/contracts/reclaim.json
(1 hunks)avs/contracts/script/AddNewStrategy.s.sol
(0 hunks)avs/contracts/script/DeployEigenLayerCore.s.sol
(1 hunks)avs/contracts/script/Deployer.s.sol
(0 hunks)avs/contracts/script/HoleskyDeployer.s.sol
(0 hunks)avs/contracts/script/ReclaimDeployer.s.sol
(1 hunks)avs/contracts/script/SetupPayments.s.sol
(1 hunks)avs/contracts/script/deployments/core/31337.json
(1 hunks)avs/contracts/script/deployments/reclaim/31337.json
(1 hunks)avs/contracts/script/output/17000/hello_world_avs_holesky_deployment_output.json
(0 hunks)avs/contracts/script/output/31337/eigenlayer_deployment_output.json
(0 hunks)avs/contracts/script/output/31337/hello_world_avs_deployment_output.json
(0 hunks)avs/contracts/script/output/31337/hello_world_avs_holesky_deployment_output.json
(0 hunks)avs/contracts/script/utils/CoreDeploymentLib.sol
(1 hunks)avs/contracts/script/utils/FundOperator.sol
(1 hunks)avs/contracts/script/utils/ReclaimDeploymentLib.sol
(1 hunks)avs/contracts/script/utils/SetupPaymentsLib.sol
(1 hunks)avs/contracts/script/utils/UpgradeableProxyLib.sol
(1 hunks)avs/contracts/script/utils/Utils.sol
(1 hunks)avs/contracts/src/ERC20Mock.sol
(1 hunks)avs/contracts/src/IReclaimTaskManager.sol
(5 hunks)avs/contracts/src/ReclaimServiceManager.sol
(1 hunks)avs/contracts/src/ReclaimSlashingRegistryCoordinator.sol
(1 hunks)avs/contracts/src/ReclaimTaskManager.sol
(1 hunks)avs/contracts/src/test/ContractsRegistry.sol
(1 hunks)avs/contracts/src/utils/BytesUtils.sol
(1 hunks)avs/contracts/src/utils/Claims.sol
(1 hunks)avs/contracts/src/utils/Random.sol
(1 hunks)avs/contracts/src/utils/StringUtils.sol
(1 hunks)avs/utils/anvil/clean-deployments.sh
(0 hunks)avs/utils/anvil/deploy-all-to-anvil-and-save-state.sh
(0 hunks)avs/utils/anvil/deploy-avs-save-anvil-state.sh
(0 hunks)avs/utils/anvil/deploy-eigenlayer-save-anvil-state.sh
(0 hunks)avs/utils/anvil/deploy-to-chain.sh
(0 hunks)avs/utils/anvil/genesis.json
(0 hunks)avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh
(1 hunks)avs/utils/anvil/utils.sh
(0 hunks)avs/utils/contract-data-gen.sh
(1 hunks)avs/utils/submit-payments-root.sh
(1 hunks)docs/avs.md
(1 hunks)package.json
(1 hunks)src/avs/abis/avsDirectoryABI.ts
(0 hunks)src/avs/abis/delegationABI.ts
(0 hunks)src/avs/abis/registryABI.ts
(0 hunks)src/avs/client/create-claim-on-avs.ts
(4 hunks)src/avs/config.ts
(1 hunks)src/avs/contracts/AVSDirectory.ts
(1 hunks)src/avs/contracts/AllocationManager.ts
(1 hunks)src/avs/contracts/DelegationManager.ts
(1 hunks)src/avs/contracts/ECDSAStakeRegistry.ts
(1 hunks)src/avs/contracts/ERC20Mock.ts
(1 hunks)src/avs/contracts/ReclaimServiceManager.ts
(14 hunks)src/avs/contracts/ReclaimSlashingRegistryCoordinator.ts
(1 hunks)src/avs/contracts/ReclaimTaskManager.ts
(1 hunks)
💤 Files with no reviewable changes (18)
- avs/contracts/script/output/31337/hello_world_avs_holesky_deployment_output.json
- avs/utils/anvil/deploy-all-to-anvil-and-save-state.sh
- avs/utils/anvil/clean-deployments.sh
- avs/contracts/script/output/17000/hello_world_avs_holesky_deployment_output.json
- avs/contracts/script/output/31337/eigenlayer_deployment_output.json
- avs/contracts/script/output/31337/hello_world_avs_deployment_output.json
- avs/utils/anvil/genesis.json
- avs/utils/anvil/deploy-to-chain.sh
- avs/contracts/lib/openzeppelin-contracts
- avs/utils/anvil/utils.sh
- src/avs/abis/delegationABI.ts
- src/avs/abis/registryABI.ts
- avs/contracts/script/Deployer.s.sol
- avs/utils/anvil/deploy-eigenlayer-save-anvil-state.sh
- avs/contracts/script/HoleskyDeployer.s.sol
- avs/utils/anvil/deploy-avs-save-anvil-state.sh
- avs/contracts/script/AddNewStrategy.s.sol
- src/avs/abis/avsDirectoryABI.ts
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/avs/client/create-claim-on-avs.ts (1)
src/utils/generics.ts (1)
unixTimestampSeconds
(46-46)
src/avs/config.ts (1)
src/utils/env.ts (1)
getEnvVariable
(15-21)
src/avs/contracts/ERC20Mock.ts (1)
src/avs/contracts/common.ts (4)
TypedEvent
(7-12)TypedEventFilter
(14-15)TypedListener
(17-19)OnEvent
(23-29)
src/avs/contracts/ReclaimServiceManager.ts (4)
src/avs/contracts/RewardsCoordinator.ts (8)
RewardsSubmissionStruct
(135-141)StrategyAndMultiplierStruct
(125-128)RewardsSubmissionStructOutput
(143-155)StrategyAndMultiplierStructOutput
(130-133)OperatorRewardStruct
(157-157)OperatorRewardStructOutput
(159-162)OperatorDirectedRewardsSubmissionStruct
(164-171)OperatorDirectedRewardsSubmissionStructOutput
(173-187)src/avs/contracts/AVSDirectory.ts (1)
SignatureWithSaltAndExpiryStruct
(30-34)src/avs/contracts/ECDSAStakeRegistry.ts (1)
SignatureWithSaltAndExpiryStruct
(50-54)src/avs/contracts/common.ts (2)
TypedEvent
(7-12)TypedEventFilter
(14-15)
🪛 GitHub Check: test
src/avs/client/create-claim-on-avs.ts
[failure] 114-114:
Property 'taskCreationMetadata' does not exist on type 'ReclaimServiceManager'.
[failure] 114-114:
Property 'taskCreationMetadata' does not exist on type 'ReclaimServiceManager'.
🪛 GitHub Actions: Test
src/avs/client/create-claim-on-avs.ts
[error] 3-3: TypeScript error TS2724: 'src/avs/contracts/ReclaimServiceManager' has no exported member named 'IReclaimServiceManager'. Did you mean 'ReclaimServiceManager'?
🪛 markdownlint-cli2 (0.17.2)
docs/avs.md
138-138: Hard tabs
Column: 1
(MD010, no-hard-tabs)
139-139: Hard tabs
Column: 1
(MD010, no-hard-tabs)
141-141: Hard tabs
Column: 1
(MD010, no-hard-tabs)
142-142: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🪛 Shellcheck (0.10.0)
avs/utils/contract-data-gen.sh
[error] 13-13: Couldn't parse this escaped char. Fix to allow more checks.
(SC1073)
[error] 13-13: Fix any mentioned problems and try again.
(SC1072)
avs/utils/submit-payments-root.sh
[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.
(SC2148)
🪛 Biome (1.9.4)
src/avs/contracts/ReclaimSlashingRegistryCoordinator.ts
[error] 667-667: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
🔇 Additional comments (61)
avs/contracts/lib/forge-std (1)
1-1
: Submodule pointer update is correct.The
forge-std
submodule reference has been advanced to commit3353993420c6e488a2914ce02d88174e80ad80f8
, aligning with the other dependency updates in this PR. No further changes are needed here.src/avs/client/create-claim-on-avs.ts (2)
118-133
: Updated ClaimRequest structure with fee and timestampThe ClaimRequest structure now includes the fee parameter and sets the requestedAt timestamp. This is a good practice for tracking when requests were made.
175-175
: Improved event handling robustnessFinding the event by name instead of assuming it's the first event is a more reliable approach.
src/avs/config.ts (1)
6-13
: Updated contract addresses for local developmentThe configuration for network ID '31337' has been updated with new contract addresses and added reward and slashing coordinator addresses.
avs/contracts/deployments/core/31337.json (1)
1-1
: Deployment metadata looks correct.The JSON file provides the required
lastUpdate
andaddresses
mappings for chain ID 31337. No issues detected.avs/contracts/script/deployments/reclaim/31337.json (1)
1-1
: Reclaim deployment metadata is correctly defined.This JSON file cleanly captures the
lastUpdate
and deployed contract addresses for Reclaim on chain 31337. No changes needed.avs/contracts/src/utils/Claims.sol (1)
12-99
: Stylistic changes only; logic unchanged.All modifications in this library are reformatting (tabs → spaces) and expanded NatSpec comments. No functional changes detected.
avs/contracts/script/deployments/core/31337.json (1)
1-1
: Verify the timestamp value in the deployment record.The timestamp "1747194163" corresponds to May 13, 2025, which is a future date. While this might be intentional for development purposes, it's unusual to have a future timestamp in a deployment record.
Also note that the block_number is set to "0", which is unusual since contracts are typically not deployed at block 0. These values might be placeholders that should be updated with actual deployment information.
avs/contracts/src/utils/BytesUtils.sol (1)
8-9
: LGTM - Type precision improvementThe change from
uint
to explicituint256
for both parameter and return type improves type precision and follows best practices.avs/contracts/script/utils/Utils.sol (5)
13-18
: LGTM - Stylistic improvementsThe condensed function signature is cleaner and follows consistent formatting patterns.
28-42
: LGTM - Consistent function formattingThe reformatted function has improved readability with consistent indentation and condensed conditionals.
45-54
: LGTM - String handling consistencyThe change from single quotes (
''
) to double quotes (""
) for empty strings maintains consistency with the rest of the codebase where double quotes are used.
56-61
: LGTM - Improved string concatenation formattingThe condensed string concatenation improves readability.
63-68
: LGTM - Consistent string formattingThe condensed string concatenation is consistent with other utility functions in this file.
avs/contracts/src/ERC20Mock.sol (2)
4-7
: Good refactoring to leverage OpenZeppelin's standard ERC20The contract has been refactored to inherit from OpenZeppelin's well-audited ERC20 implementation rather than maintaining a custom implementation. This significantly reduces the code size and potential for bugs.
Note that you're initializing with empty strings for name and symbol - this is fine for a mock but would be unusual in production.
9-11
: Public mint function without access controlThe public mint function without access control is appropriate for a mock contract used in testing, but would be a security risk in a production contract.
avs/contracts/src/ReclaimSlashingRegistryCoordinator.sol (1)
13-32
: Good inheritance pattern for custom SlashingRegistryCoordinatorThe contract correctly inherits from SlashingRegistryCoordinator and properly forwards all constructor parameters to the parent. This follows good inheritance practices.
avs/contracts/script/DeployEigenLayerCore.s.sol (1)
9-16
: Well-structured deployment script using utility librariesGood implementation using CoreDeploymentLib and UpgradeableProxyLib for a modular deployment approach. The script is concise and focused on orchestrating the deployment process.
avs/contracts/config/core/31337.json (1)
1-45
:✅ Verification successful
Configuration file review for AVS contract ecosystem.
This configuration file sets up the initial parameters for the AVS (Attestation Verification System) rewards ecosystem. It contains well-structured configuration for various managers in the system, with appropriate initialization parameters.
The configuration includes:
- Common paused status (0) and owner address across all components
- Specific withdrawal delay blocks for strategy and delegation managers
- Allocation and deallocation delay settings
- Comprehensive rewards configuration with appropriate timeframes
Let's verify that the parameters are reasonable for the intended environment:
🏁 Script executed:
#!/bin/bash # Check network ID 31337 (appears to be a local Hardhat/Foundry network based on filename) # Verify rewards coordinator parameters are reasonable # Check calculation intervals echo "Rewards calculation interval: $(( 86400 / 60 / 60 / 24 )) days" echo "Maximum rewards duration: $(( 864000 / 60 / 60 / 24 )) days" # Check if the owner address is a known testing address if [[ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" == "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ]]; then echo "Owner address is the default Hardhat account #0" fi # Verify split configuration is within reasonable bounds split_bips=1000 echo "Default split percentage: $(( split_bips / 100 ))%" echo "Global operator commission: $(( split_bips / 100 ))%" # Check GENESIS_REWARDS_TIMESTAMP represents a reasonable date genesis_timestamp=1672531200 echo "Genesis rewards timestamp: $(date -u -d @$genesis_timestamp)"Length of output: 741
✔️ Configuration parameters validated for local Hardhat network (31337)
All values fall within expected bounds for a development environment:
- Rewards calculation interval: 1 day
- Maximum rewards duration: 10 days
- Genesis rewards timestamp: Sun Jan 1 00:00:00 UTC 2023
- Owner is default Hardhat account #0
- Default split & global operator commission: 10%
No further changes required.
avs/contracts/src/utils/StringUtils.sol (6)
8-18
: Type consistency improvement applied.The function implementation now uses explicit
uint256
instead ofuint
, which is a good practice for code clarity and type safety.
20-31
: Type consistency improvement applied.The function now consistently uses
uint256
type instead ofuint
, improving code clarity and type safety.
33-36
: Type consistency improvement applied.The implementation now uses
uint8
consistently instead of implicit types.
38-44
: Type consistency improvement applied.The function implementation is properly formatted with consistent indentation.
46-66
: Type consistency improvement applied.The function now consistently uses
uint256
instead ofuint
, improving code clarity and type safety.
68-70
: Type consistency improvement applied.The function implementation is properly formatted with consistent indentation and explicit types.
avs/contracts/script/utils/UpgradeableProxyLib.sol (4)
1-17
: Well-structured library for proxy management.This library properly defines constants for storage slots and imports necessary dependencies for proxy management. The use of the Foundry VM cheat code for storage access is appropriate for testing and deployment scripts.
18-25
: Clean implementations for proxy and admin deployment.The functions for deploying proxy admin and setting up empty proxies are concise and follow best practices.
27-35
: Effective proxy upgrade utilities.The upgrade functions properly handle both basic upgrades and upgrades with initialization calls. They retrieve the admin from storage rather than requiring it as a parameter, which is a good practice.
37-45
: Proper storage slot access for proxy information.The functions correctly read storage slots to retrieve implementation and admin addresses from proxy contracts. This approach is aligned with OpenZeppelin's proxy pattern.
avs/contracts/script/utils/SetupPaymentsLib.sol (9)
9-15
: Clear and concise struct definition.The PaymentLeaves struct appropriately encapsulates the leaves data structure for rewards payments.
17-44
: Rewards submissions creation logic.The function correctly constructs rewards submissions with the provided parameters and calls the service manager to create them.
127-160
: Correct root submission implementation.The function properly validates input arrays and constructs the payment root before submitting it.
162-176
: Well-structured earner leaves creation.The function correctly builds earner leaves with token roots for each earner address.
178-180
: Simple token root creation function.This function provides a clean interface to the merkleization function.
182-206
: Token leaves creation with default values.The functions appropriately create token leaves with the specified earnings and strategy address.
208-221
: JSON serialization for leaves storage.The functions leverage the Foundry VM for JSON serialization, which is appropriate for testing and deployment scripts.
223-260
: Merkle proof generation.The function correctly implements Merkle proof generation with appropriate input validation.
303-314
: Leaves padding utility.The function correctly pads the leaves array to the next power of 2, which is necessary for Merkle tree construction.
package.json (5)
21-21
: Enhance build setup with IR-based optimization.Renaming to
build:contracts
follows a more conventional script naming pattern, and adding the--via-ir
flag enables Forge's IR-based optimization pipeline, which can produce more efficient bytecode.
22-23
: Improved deployment workflow with dedicated scripts.Adding specific scripts for deploying EigenLayer core and Reclaim contracts separately provides better modularity and allows for more targeted debugging with the
--revert-strings debug
flag.
30-30
: Better type generation with dedicated script.Using a shell script for type generation (
contract-data-gen.sh
) provides more flexibility than a hardcoded command, allowing for easier updates to the type generation process.
31-31
: Streamlined chain startup process.Moving to a dynamic deployment approach rather than using saved Anvil states improves reliability and maintainability of the test environment.
32-32
: Added rewards payment submission functionality.This new script supports the AVS rewards system, allowing for payments roots to be submitted to the rewards coordinator.
avs/contracts/src/test/ContractsRegistry.sol (1)
1-42
: Well-structured registry contract for testing.The contract efficiently manages contract address registration and test metadata, eliminating the need for external JSON files during testing.
src/avs/contracts/ReclaimSlashingRegistryCoordinator.ts (1)
1-2217
: Comprehensive TypeScript interface for the SlashingRegistryCoordinator.This auto-generated file provides a complete type-safe interface for interacting with the ReclaimSlashingRegistryCoordinator contract, including all functions, events, and complex data structures required for the AVS system.
🧰 Tools
🪛 Biome (1.9.4)
[error] 667-667: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
avs/contracts/src/IReclaimTaskManager.sol (5)
4-4
: Appropriate interface renaming for clearer responsibility definition.Changing from
IReclaimServiceManager
toIReclaimTaskManager
better reflects the focused responsibility of this contract in managing tasks.
35-39
: Fee support added to claim requests.Adding the fee field to ClaimRequest implements the crucial functionality for the AVS rewards system, allowing operators to be compensated for processing claims.
41-50
: Renamed operator struct for clarity.Renaming
Operator
toReclaimOperator
reduces potential naming conflicts and clearly associates it with the Reclaim system.
104-108
: Fee requirement for task creation.Adding
minFee
to TaskCreationMetadata establishes a baseline requirement for task creation, preventing spam and ensuring operators receive adequate compensation.
115-115
: Simplified function declarations.The function declarations have been reformatted for conciseness without changing their signatures, which improves readability.
Also applies to: 123-123, 127-127
src/avs/contracts/ERC20Mock.ts (1)
1-4
: Generated file – safe to skip manual review
ERC20Mock.ts
is clearly generated by TypeChain (/* Autogenerated file. Do not edit manually. */
). Manual tweaks would be clobbered on the next build, so keeping the file exactly as-is is the right call.src/avs/contracts/AllocationManager.ts (1)
1-4
: Generated file – safe to skip manual review
AllocationManager.ts
is also auto-generated. No manual action required and the ABI looks coherent.src/avs/contracts/AVSDirectory.ts (1)
1-4
: Generated file – safe to skip manual review
AVSDirectory.ts
is produced by the same tooling; no issues spotted.avs/contracts/script/ReclaimDeployer.s.sol (2)
105-110
: Verify thatstrategy
deployment succeeded before using it
require(address(strategy) != address(0));
will pass even when the deployment reverted (becausestrategy
would still be the zero address).
Instead, check the boolean returned byStrategyFactory.deployNewStrategy
(it reverts on failure) or emit/console log the address before the require.- strategy = IStrategy(StrategyFactory(configData.strategyFactory).deployNewStrategy(erc20Mock)); - ... - require(address(strategy) != address(0)); + strategy = IStrategy( + StrategyFactory(configData.strategyFactory) + .deployNewStrategy(erc20Mock) + ); + console.log("Strategy deployed at", address(strategy));
112-116
: Token amount hard-coded to 1 ERC20 – intentional?Only
1e18
tokens are minted to theserviceManager
, whereas operators received tens of thousands.
Double-check that this tiny balance is sufficient for subsequent reward flows; otherwise bump to a realistic initial supply.src/avs/contracts/ReclaimTaskManager.ts (1)
1-1327
: Skip review of autogenerated binding.
This TypeScript interface forReclaimTaskManager
is generated by the contract-binding tool (contract-data-gen.sh
). Manual edits will be overwritten; no hand-crafted changes are required here.src/avs/contracts/ECDSAStakeRegistry.ts (1)
1-1197
: Skip review of autogenerated binding.
This TypeScript interface forECDSAStakeRegistry
is generated by the contract-binding tool and should not be modified manually.src/avs/contracts/ReclaimServiceManager.ts (1)
95-108
: Namespace uses an obsolete struct (SignatureWithSaltAndExpiryStruct
)
ISignatureUtilsMixinTypes.SignatureWithSaltAndExpiryStruct
is retained here, but other new bindings (e.g.AVSDirectory
,ECDSAStakeRegistry
) have migrated toSignatureWithSaltAndExpiryStruct
.
Please double-check that the Solidity contract behind this binding still expects thesalt
field. Mismatched structs will produce subtleabi.decode
failures.avs/contracts/script/utils/CoreDeploymentLib.sol (1)
159-167
:ethPOSDeposit
left unset for non-mainnet chainsThe
else
branch isTODO
, which meansethPOSDeposit
remains the zero address on testnets.
EigenPodManager
’s constructor expects a valid contract – passingaddress(0)
will revert at runtime.Either pull the address from config JSON or deploy a mock deposit contract when
chainid != 1
.avs/contracts/script/utils/ReclaimDeploymentLib.sol (1)
165-170
:StakeRegistry
,BLSApkRegistry
,IndexRegistry
are upgraded without initialisationEach of these contracts has an
initialize
function that must be called once.
UsingUpgradeableProxyLib.upgrade
alone leaves them uninitialised and forever unusable (theinitializer
modifier will block future calls).Consider switching to
upgradeAndCall
with the proper encoded init data, mirroring what you did forslashingRegistryCoordinator
,serviceManager
, andtaskManager
.avs/contracts/src/ReclaimServiceManager.sol (1)
58-71
: Reference to_allocationManager
relies on inherited storage – double-check visibility
initialize
calls_allocationManager.setAVSRegistrar(...)
, yet_allocationManager
is not declared in this contract.
IfServiceManagerBase
exposes it as an internal variable this is fine; otherwise the code will not compile.Please verify the member’s name/visibility in the upstream base contract.
@@ -1 +1 @@ | |||
Subproject commit c5b419a82fcca1b64aacd32026ce8d4459b80e35 | |||
Subproject commit 4d63f27247587607beb67f96fdabec4b2c1321ef |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Bump eigenlayer-middleware submodule to commit 4d63f27
The subproject pointer for eigenlayer-middleware
has been updated to 4d63f27247587607beb67f96fdabec4b2c1321ef
. Please verify that this is the intended commit and that any dependent remappings or imports (e.g., in foundry.toml
or Solidity scripts) align with the updated code.
Run this script to confirm the submodule update:
🏁 Script executed:
#!/bin/bash
# Verify eigenlayer-middleware submodule is at the expected commit
git submodule status avs/contracts/lib/eigenlayer-middleware | awk '{print $1}'
# Expected output: 4d63f27247587607beb67f96fdabec4b2c1321ef
Length of output: 123
Improve eigenlayer-middleware submodule verification
The submodule has been bumped to 4d63f27247587607beb67f96fdabec4b2c1321ef
, but the original script outputs a leading “–” status flag. To reliably verify:
- Initialize/update the submodule before checking:
git submodule update --init avs/contracts/lib/eigenlayer-middleware
- Retrieve the exact HEAD commit:
git -C avs/contracts/lib/eigenlayer-middleware rev-parse HEAD # Expected: 4d63f27247587607beb67f96fdabec4b2c1321ef
- Alternatively, strip the status prefix when using
git submodule status
:git submodule status avs/contracts/lib/eigenlayer-middleware \ | awk '{print substr($1,2)}'
Please also confirm that any remappings in foundry.toml
and all Solidity import paths still resolve correctly against the updated middleware code.
🤖 Prompt for AI Agents
In avs/contracts/lib/eigenlayer-middleware at line 1, improve the submodule
verification script by first running 'git submodule update --init
avs/contracts/lib/eigenlayer-middleware' to ensure the submodule is initialized
and updated. Then, retrieve the exact HEAD commit using 'git -C
avs/contracts/lib/eigenlayer-middleware rev-parse HEAD' and confirm it matches
'4d63f27247587607beb67f96fdabec4b2c1321ef'. Alternatively, if using 'git
submodule status', strip the leading status character by adjusting the awk
command to output the commit hash without the prefix. Finally, verify that
remappings in foundry.toml and Solidity import paths remain correct with the
updated submodule commit.
{ | ||
"addresses": { | ||
"rewardsOwner": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", | ||
"rewardsInitiator": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" | ||
}, | ||
"keys": { | ||
"rewardsOwner": "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", | ||
"rewardsInitiator": "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" | ||
} | ||
} No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove and Secure Private Keys
Committing private keys directly in a repository poses a significant security risk. Move these keys to a secure vault or load them via environment variables at runtime, and update scripts to reference non-committed secrets.
🤖 Prompt for AI Agents
In avs/contracts/config/reclaim/31337.json lines 1 to 10, private keys are
committed directly in the file, which is a security risk. Remove the private
keys from this JSON file, store them securely in a vault or environment
variables, and modify any code or scripts that use these keys to load them
dynamically at runtime from the secure source instead of the committed file.
avs/contracts/foundry.toml
Outdated
"@openzeppelin/=lib/eigenlayer-middleware/lib/openzeppelin-contracts/", | ||
"@openzeppelin-upgrades/=lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/", | ||
"forge-std/=lib/forge-std/src/", | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify foundry.toml
Remapping Paths
Ensure that the updated remapping entries actually resolve to existing directories after submodule restructuring.
🏁 Script executed:
#!/usr/bin/env bash
# Verify directory existence for remappings in avs/contracts/foundry.toml
paths=$(sed -n '11,14p' avs/contracts/foundry.toml | sed -E 's/.*"([^"]+)".*/\1/')
for p in $paths; do
if [ ! -d "$p" ]; then
echo "❌ Missing directory: $p"
exit 1
fi
done
echo "✅ All remapped directories exist."
Length of output: 578
Fix remapping paths in avs/contracts/foundry.toml
The current remapping entries point to directories that don’t exist post-restructure. Verifying with a corrected script:
#!/usr/bin/env bash
# Verify actual paths (split alias=path)
sed -n '11,13p' avs/contracts/foundry.toml \
| sed -E 's/.*"([^"]+)".*/\1/' \
| while read -r mapping; do
dir=${mapping#*=}
if [ ! -d "$dir" ]; then
echo "❌ Missing directory: $dir"
fi
done
Results:
❌ Missing directory: lib/eigenlayer-middleware/lib/openzeppelin-contracts/
❌ Missing directory: lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/
❌ Missing directory: lib/forge-std/src/
Please update these remappings to match the new submodule paths or ensure the directories are checked out at the expected locations.
🤖 Prompt for AI Agents
In avs/contracts/foundry.toml lines 11 to 14, the remapping paths point to
directories that no longer exist due to submodule restructuring. Update these
remapping entries to reflect the correct, existing directory paths for
openzeppelin-contracts, openzeppelin-contracts-upgradeable, and forge-std
libraries, ensuring they match the new submodule locations or verify that the
submodules are properly checked out at the expected paths.
{ | ||
"num_quorums": 1, | ||
"operator_params": [10000,15000,100], | ||
"operator_addr": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", | ||
"operator_2_addr":"0x0b065a0423f076a340f37e16e1ce22e23d66caf2", | ||
"contracts_registry_addr": "0x5FbDB2315678afecb367f032d93F642f64180aa3", | ||
"task_generator_addr": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", | ||
"aggregator_addr":"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", | ||
"rewards_owner_addr": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", | ||
"rewards_initiator_addr": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", | ||
"rewards_owner_key": "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", | ||
"rewards_initiator_key": "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" | ||
} No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical security concern: Plaintext private keys in configuration file
This configuration file contains private keys in plaintext (lines 11-12), which presents a significant security risk if committed to version control.
Never store private keys in configuration files that might be committed to a repository. Instead:
- Use environment variables or a secure secrets management solution
- Replace the current implementation with a pattern like:
{
"rewards_owner_key": "${REWARDS_OWNER_KEY}",
"rewards_initiator_key": "${REWARDS_INITIATOR_KEY}"
}
- Update your deployment scripts to inject these values from secure sources
Also consider adding documentation comments explaining what each parameter represents, particularly the meaning of the numeric values in operator_params
.
🤖 Prompt for AI Agents
In avs/contracts/reclaim.json lines 1 to 13, private keys are stored in
plaintext, which is a critical security risk. Remove the actual private key
values and replace them with environment variable placeholders like
"${REWARDS_OWNER_KEY}" and "${REWARDS_INITIATOR_KEY}". Update deployment scripts
to inject these secrets securely from environment variables or a secrets
manager. Additionally, add comments to document the purpose of each parameter,
especially clarifying the numeric values in "operator_params".
// blank for now -- till we figure out the right | ||
// algorithm for this | ||
claimUserId: EMPTY_CLAIM_USER_ID, | ||
claimHash: getIdentifierFromClaimInfo({ | ||
provider: name, | ||
parameters: canonicalStringify(params), | ||
context: context | ||
? canonicalStringify(context) | ||
: '', | ||
}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix taskCreationMetadata property access
Static analysis indicates that taskCreationMetadata
doesn't exist on the ReclaimServiceManager type.
Update the code to access the correct property or method:
if(!fee) {
- const { minFee } = await contract.taskCreationMetadata()
+ // Use the correct method or property to get the minimum fee
+ // For example:
+ const minFee = await contract.getMinimumFee() // Replace with the actual method
fee = minFee
}
Verify the actual method name in the updated contract interface.
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 GitHub Check: test
[failure] 114-114:
Property 'taskCreationMetadata' does not exist on type 'ReclaimServiceManager'.
[failure] 114-114:
Property 'taskCreationMetadata' does not exist on type 'ReclaimServiceManager'.
🤖 Prompt for AI Agents
In src/avs/client/create-claim-on-avs.ts around lines 113 to 116, the code tries
to call taskCreationMetadata on the contract object, but this method does not
exist on the ReclaimServiceManager type. Check the updated contract interface
for the correct method or property name that provides the minimum fee metadata,
then replace taskCreationMetadata with the correct method call or property
access to retrieve minFee.
import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; | ||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; | ||
import {BLSApkRegistry} from "@eigenlayer-middleware/src/BLSApkRegistry.sol"; | ||
import {IndexRegistry} from "@eigenlayer-middleware/src/IndexRegistry.sol"; | ||
import {InstantSlasher} from "@eigenlayer-middleware/src/slashers/InstantSlasher.sol"; | ||
import {StakeRegistry} from "@eigenlayer-middleware/src/StakeRegistry.sol"; | ||
// import {SocketRegistry} from "@eigenlayer-middleware/src/SocketRegistry.sol"; // todo: socket registry not available | ||
import {IAllocationManager} from "@eigenlayer/contracts/interfaces/IAllocationManager.sol"; | ||
import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategyManager.sol"; | ||
import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; | ||
import {IRewardsCoordinator} from "@eigenlayer/contracts/interfaces/IRewardsCoordinator.sol"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate import and wrong interface path break compilation
CoreDeploymentLib
is imported twice (lines 29 and 37).IStrategy
is imported fromIStrategyManager.sol
, but that file exposesIStrategyManager
, notIStrategy
.
-import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategyManager.sol";
+import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategy.sol";
-import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; // duplicate
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; | |
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; | |
import {BLSApkRegistry} from "@eigenlayer-middleware/src/BLSApkRegistry.sol"; | |
import {IndexRegistry} from "@eigenlayer-middleware/src/IndexRegistry.sol"; | |
import {InstantSlasher} from "@eigenlayer-middleware/src/slashers/InstantSlasher.sol"; | |
import {StakeRegistry} from "@eigenlayer-middleware/src/StakeRegistry.sol"; | |
// import {SocketRegistry} from "@eigenlayer-middleware/src/SocketRegistry.sol"; // todo: socket registry not available | |
import {IAllocationManager} from "@eigenlayer/contracts/interfaces/IAllocationManager.sol"; | |
import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategyManager.sol"; | |
import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; | |
import {IRewardsCoordinator} from "@eigenlayer/contracts/interfaces/IRewardsCoordinator.sol"; | |
import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; | |
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; | |
import {BLSApkRegistry} from "@eigenlayer-middleware/src/BLSApkRegistry.sol"; | |
import {IndexRegistry} from "@eigenlayer-middleware/src/IndexRegistry.sol"; | |
import {InstantSlasher} from "@eigenlayer-middleware/src/slashers/InstantSlasher.sol"; | |
import {StakeRegistry} from "@eigenlayer-middleware/src/StakeRegistry.sol"; | |
// import {SocketRegistry} from "@eigenlayer-middleware/src/SocketRegistry.sol"; // todo: socket registry not available | |
import {IAllocationManager} from "@eigenlayer/contracts/interfaces/IAllocationManager.sol"; | |
import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategy.sol"; | |
import {IRewardsCoordinator} from "@eigenlayer/contracts/interfaces/IRewardsCoordinator.sol"; |
🤖 Prompt for AI Agents
In avs/contracts/script/utils/ReclaimDeploymentLib.sol between lines 28 and 38,
remove the duplicate import of CoreDeploymentLib by deleting one of the two
import statements. Also, correct the import path for IStrategy by changing it
from IStrategyManager.sol to the correct file that exports IStrategy, ensuring
the interface path matches the actual exported interface to fix the compilation
errors.
ISlashingRegistryCoordinatorTypes.OperatorSetParam[] memory quorumsOperatorSetParams = | ||
new ISlashingRegistryCoordinatorTypes.OperatorSetParam[](numQuorums); | ||
uint256[] memory operator_params = setupConfig.operatorParams; | ||
|
||
for (uint256 i = 0; i < numQuorums; i++) { | ||
quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ | ||
maxOperatorCount: uint32(operator_params[i]), | ||
kickBIPsOfOperatorStake: uint16(operator_params[i + 1]), | ||
kickBIPsOfTotalStake: uint16(operator_params[i + 2]) | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array indexing will read out-of-bounds for operatorParams
Inside the loop i
increments by 1, but three consecutive indices (i, i+1, i+2
) are consumed each iteration.
For n
quorums you need 3 × n
elements; when i == numQuorums - 1
, i+2
will overflow.
- for (uint256 i = 0; i < numQuorums; i++) {
- quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({
- maxOperatorCount: uint32(operator_params[i]),
- kickBIPsOfOperatorStake: uint16(operator_params[i + 1]),
- kickBIPsOfTotalStake: uint16(operator_params[i + 2])
- });
- }
+ for (uint256 i = 0; i < numQuorums; i++) {
+ uint256 base = 3 * i;
+ quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({
+ maxOperatorCount: uint32(operator_params[base]),
+ kickBIPsOfOperatorStake: uint16(operator_params[base + 1]),
+ kickBIPsOfTotalStake: uint16(operator_params[base + 2])
+ });
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
ISlashingRegistryCoordinatorTypes.OperatorSetParam[] memory quorumsOperatorSetParams = | |
new ISlashingRegistryCoordinatorTypes.OperatorSetParam[](numQuorums); | |
uint256[] memory operator_params = setupConfig.operatorParams; | |
for (uint256 i = 0; i < numQuorums; i++) { | |
quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ | |
maxOperatorCount: uint32(operator_params[i]), | |
kickBIPsOfOperatorStake: uint16(operator_params[i + 1]), | |
kickBIPsOfTotalStake: uint16(operator_params[i + 2]) | |
}); | |
} | |
ISlashingRegistryCoordinatorTypes.OperatorSetParam[] memory quorumsOperatorSetParams = | |
new ISlashingRegistryCoordinatorTypes.OperatorSetParam[](numQuorums); | |
uint256[] memory operator_params = setupConfig.operatorParams; | |
for (uint256 i = 0; i < numQuorums; i++) { | |
uint256 base = 3 * i; | |
quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ | |
maxOperatorCount: uint32(operator_params[base]), | |
kickBIPsOfOperatorStake: uint16(operator_params[base + 1]), | |
kickBIPsOfTotalStake: uint16(operator_params[base + 2]) | |
}); | |
} |
🤖 Prompt for AI Agents
In avs/contracts/script/utils/ReclaimDeploymentLib.sol around lines 211 to 221,
the loop uses three consecutive indices (i, i+1, i+2) on operatorParams per
iteration, but increments i by 1, causing out-of-bounds access. To fix this,
adjust the loop to increment i by 3 each iteration or calculate the correct
indices by multiplying i by 3, ensuring you access operatorParams at positions
3*i, 3*i+1, and 3*i+2 without exceeding array bounds.
@@ -1,370 +1,114 @@ | |||
// SPDX-License-Identifier: UNLICENSED | |||
pragma solidity ^0.8.9; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Compiler pragma differs from the rest of the codebase
Other new files use pragma solidity ^0.8.12;
, but this contract sticks to ^0.8.9
.
Using a lower compiler version may prevent the project from compiling with a single solc
run and can disable newer security checks.
-pragma solidity ^0.8.9;
+pragma solidity ^0.8.12;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
pragma solidity ^0.8.9; | |
pragma solidity ^0.8.12; |
🤖 Prompt for AI Agents
In avs/contracts/src/ReclaimServiceManager.sol at line 2, update the compiler
pragma from ^0.8.9 to ^0.8.12 to align with the rest of the codebase. This
ensures consistent compiler version usage across all files and enables the
latest security checks and features.
constructor( | ||
IStakeRegistry _stakeRegistry, | ||
IRewardsCoordinator _rewardsCoordinator, | ||
IPauserRegistry _pauserRegistry | ||
) Pausable(_pauserRegistry) { | ||
rewardsCoordinator = _rewardsCoordinator; | ||
stakeRegistry = _stakeRegistry; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constructor logic is incompatible with upgradeable pattern
Upgrade-safe contracts must put all state initialisation in initialize
, not the constructor, because the constructor never runs behind a proxy. rewardsCoordinator
, stakeRegistry
, and the pauser registry will stay un-initialised in production.
Recommendation:
- Move the assignments to
initialize
or introduce an__ReclaimTaskManager_init(...)
that chains to__Pausable_init
/__Ownable_init
. - Make the constructor
//solhint-disable-next-line no-empty-blocks
{ } to prevent misuse.
🤖 Prompt for AI Agents
In avs/contracts/src/ReclaimTaskManager.sol around lines 50 to 57, the
constructor contains state variable assignments which are incompatible with the
upgradeable proxy pattern since constructors do not run behind proxies. To fix
this, move the initialization of rewardsCoordinator, stakeRegistry, and
pauserRegistry into an external initializer function named
__ReclaimTaskManager_init that calls the parent initializers like
__Pausable_init, and make the constructor empty with a solhint disable comment
to prevent misuse.
import "@eigenlayer/contracts/core/DelegationManager.sol"; | ||
import {ECDSAServiceManagerBase} from "@eigenlayer-middleware/src/unaudited/ECDSAServiceManagerBase.sol"; | ||
import {IStakeRegistry} from "@eigenlayer-middleware/src/interfaces/IStakeRegistry.sol"; | ||
import "@openzeppelin-upgrades/contracts/utils/cryptography/ECDSAUpgradeable.sol"; | ||
import {OperatorStateRetriever} from "@eigenlayer-middleware/src/OperatorStateRetriever.sol"; | ||
import "@eigenlayer/contracts/permissions/Pausable.sol"; | ||
import {IRegistryCoordinator} from "@eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; | ||
import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategy.sol"; | ||
import {IRewardsCoordinator} from "@eigenlayer/contracts/interfaces/IRewardsCoordinator.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "./IReclaimTaskManager.sol"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing / wrong imports break compilation
Initializable
and OwnableUpgradeable
are used in the inheritance list (l. 22-27) but never imported, and ECDSAUpgradeable
is imported from a non-standard path. Solidity will not compile.
-import "@eigenlayer/contracts/core/DelegationManager.sol";
-import {ECDSAServiceManagerBase} from "@eigenlayer-middleware/src/unaudited/ECDSAServiceManagerBase.sol";
-import {IStakeRegistry} from "@eigenlayer-middleware/src/interfaces/IStakeRegistry.sol";
-import "@openzeppelin-upgrades/contracts/utils/cryptography/ECDSAUpgradeable.sol";
+import "@eigenlayer/contracts/core/DelegationManager.sol"; // ← if really needed
+import {IStakeRegistry} from "@eigenlayer-middleware/src/interfaces/IStakeRegistry.sol";
+
+// OpenZeppelin-upgradeable bases
+import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
+import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
If DelegationManager
/ ECDSAServiceManagerBase
are unused, please drop them to avoid dead-code warnings.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import "@eigenlayer/contracts/core/DelegationManager.sol"; | |
import {ECDSAServiceManagerBase} from "@eigenlayer-middleware/src/unaudited/ECDSAServiceManagerBase.sol"; | |
import {IStakeRegistry} from "@eigenlayer-middleware/src/interfaces/IStakeRegistry.sol"; | |
import "@openzeppelin-upgrades/contracts/utils/cryptography/ECDSAUpgradeable.sol"; | |
import {OperatorStateRetriever} from "@eigenlayer-middleware/src/OperatorStateRetriever.sol"; | |
import "@eigenlayer/contracts/permissions/Pausable.sol"; | |
import {IRegistryCoordinator} from "@eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; | |
import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategy.sol"; | |
import {IRewardsCoordinator} from "@eigenlayer/contracts/interfaces/IRewardsCoordinator.sol"; | |
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "./IReclaimTaskManager.sol"; | |
// Core EigenLayer imports | |
import "@eigenlayer/contracts/core/DelegationManager.sol"; // ← if really needed | |
import {IStakeRegistry} from "@eigenlayer-middleware/src/interfaces/IStakeRegistry.sol"; | |
// OpenZeppelin-upgradeable bases | |
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | |
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | |
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; | |
// Other middleware / permissions / interfaces | |
import {OperatorStateRetriever} from "@eigenlayer-middleware/src/OperatorStateRetriever.sol"; | |
import "@eigenlayer/contracts/permissions/Pausable.sol"; | |
import {IRegistryCoordinator} from "@eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; | |
import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategy.sol"; | |
import {IRewardsCoordinator} from "@eigenlayer/contracts/interfaces/IRewardsCoordinator.sol"; | |
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "./IReclaimTaskManager.sol"; |
🤖 Prompt for AI Agents
In avs/contracts/src/ReclaimTaskManager.sol lines 4 to 14, the imports are
incomplete and incorrect: add imports for Initializable and OwnableUpgradeable
from OpenZeppelin upgradeable contracts to match their usage in the inheritance
list, correct the import path for ECDSAUpgradeable to the standard OpenZeppelin
upgradeable location, and remove unused imports like DelegationManager and
ECDSAServiceManagerBase to prevent dead-code warnings and ensure successful
compilation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
♻️ Duplicate comments (7)
avs/contracts/script/SetupPayments.s.sol (3)
32-35
:amountPerPayment
should beuint256
to eliminate overflow & pointless casts
The value is later multiplied by an ever-growingnonce
; staying inuint32
guarantees a revert once the product exceeds 4 294 967 295.- uint32 amountPerPayment = 100; + uint256 amountPerPayment = 100;
76-80
: Down-casting the product back touint32
brings the overflow back
You widened the math touint256
only to squeeze it back into 32 bits. Remove the cast and keep the variable wide.- amountPerPayment = uint32(amountPerPayment * (nonce + 1)); + amountPerPayment = amountPerPayment * (nonce + 1); // stay in uint256
112-115
: Same unsafe cast here – repeat the fix applied above.avs/contracts/src/ReclaimServiceManager.sol (1)
2-2
: 🛠️ Refactor suggestionAlign compiler version with the rest of the repo
Other new contracts compile with
^0.8.12
; keeping^0.8.9
breaks monolithic compilation and forgoes newer security checks.-pragma solidity ^0.8.9; +pragma solidity ^0.8.12;avs/contracts/src/ReclaimTaskManager.sol (3)
111-162
:⚠️ Potential issuecreateNewTask always reverts
The call chain hits
_getRegisteredOperators()
which currentlyrevert("Not implemented")
(see 340-344). Until that function is implemented or mocked, no task can ever be created, rendering the AVS unusable.
51-58
:⚠️ Potential issueConstructor logic breaks upgradeable pattern
State variables (
rewardsCoordinator
,stakeRegistry
) are initialised in the constructor, which is never executed through a proxy. Behind the proxy those addresses will stay zero, bricking reward distribution & operator-weight checks.Move all state setup to
initialize
, make the constructor empty, e.g.:constructor() Pausable(IPauserRegistry(address(0))) { }
2-15
:⚠️ Potential issueCompilation blockers & dead imports
•
ECDSAUpgradeable
andOwnableUpgradeable
must be imported from the OpenZeppelin upgradeable package:-import "@openzeppelin-upgrades/contracts/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";•
DelegationManager.sol
andECDSAServiceManagerBase.sol
are not used – drop them to keep bytecode slim.•
IPauserRegistry
is referenced but not imported, causing a compiler error.
🧹 Nitpick comments (13)
avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh (3)
25-28
: Pin and validate the Docker image version
You’re correctly usingghcr.io/foundry-rs/foundry:v1.2.2
, but consider failing fast if the image pull fails or version is unavailable. You could wrap this in a check:docker pull ghcr.io/foundry-rs/foundry:v1.2.2 || exit 1
30-32
: Replace fixed sleep with readiness probe
Polling RPC availability is more reliable than a staticsleep 2
. For example:until curl --silent --fail $RPC_URL > /dev/null; do echo "Waiting for Anvil RPC..." sleep 1 done
3-3
: Use theRPC_URL
variable consistently
You defineRPC_URL
at the top but hard-codehttp://localhost:8545
in the Forge call. Swap it out:- --rpc-url http://localhost:8545 \ + --rpc-url $RPC_URL \Also applies to: 35-35
🧰 Tools
🪛 Shellcheck (0.10.0)
[warning] 3-3: RPC_URL appears unused. Verify use (or export if used externally).
(SC2034)
avs/contracts/script/deployments/reclaim/31337.json (1)
1-1
: Address gitleaks false positiveStatic analysis flagged a “Generic API Key” pattern, but these are Ethereum addresses. Consider updating your gitleaks configuration to allowlist hex addresses (e.g., regex
^0x[a-fA-F0-9]{40}$
) and avoid these false positives.🧰 Tools
🪛 Gitleaks (8.26.0)
1-1: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
avs/contracts/src/test/operator.yaml (2)
7-7
: Quote the RPC URL
Unquoted URLs containing://
can be misparsed in YAML. Wrap it in quotes to ensure it’s treated as a string.-eth_rpc_url: http://localhost:8545 +eth_rpc_url: "http://localhost:8545"
10-10
: Add a newline at end of file
YAML parsers expect a trailing newline.+<newline>
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 10-10: no new line character at the end of file
(new-line-at-end-of-file)
avs/contracts/script/SetupPayments.s.sol (4)
27-31
:DURATION
constant is 1 second – typo?
CALCULATION_INTERVAL_SECONDS
is 1 day, yetDURATION
is1
(defaults to seconds). If this was meant to be one day, declare it as1 days
to avoid off-by-86400 errors.
73-74
: Revert message contradicts the check
You revert whenendTimestamp
is in the future but the message says “must be in the future”. Swap wording to “must not be in the future”.
40-40
:cumumlativePaymentMultiplier
is misspelled & unused
Currently it is never updated – only logged as 0. Delete it or implement the intended logic.- uint256 cumumlativePaymentMultiplier; + // uint256 cumulativePaymentMultiplier; // removed until usedAlso applies to: 203-203
57-58
: TODO lingering – config path hard-coded
ParameterisefilePath
via config the same way you do for core/reclaim settings to keep environments self-contained.avs/contracts/src/ReclaimServiceManager.sol (2)
21-29
: Stale error-message copy-pasteThe modifier name is
onlyTaskManager
, but the revert text still references “registry coordinator”, which will confuse operators during debugging.
134-143
: Gas-scaling lookup
isOperatorWhitelisted
does O(n) linear search on every call. Once the operator set grows this becomes expensive.
Consider switching tomapping(address => bool)
for O(1) checks while still keeping the array for enumeration.avs/contracts/src/ReclaimTaskManager.sol (1)
299-337
: Potential infinite loop / early revert while picking operatorsIf many operators fail
operatorHasMinimumWeight
,left
can reach 0 beforecount
operators are chosen, triggering the “Fees too low” revert even when fees are fine.
Consider pre-filtering eligible operators or reverting with a clearer message (“Not enough eligible operators after weight check”).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
.gitmodules
(0 hunks)avs/contracts/.gitmodules
(1 hunks)avs/contracts/foundry.toml
(2 hunks)avs/contracts/lib/eigenlayer-middleware
(1 hunks)avs/contracts/script/SetupPayments.s.sol
(1 hunks)avs/contracts/script/deployments/reclaim/31337.json
(1 hunks)avs/contracts/script/utils/UpgradeableProxyLib.sol
(1 hunks)avs/contracts/src/ReclaimServiceManager.sol
(1 hunks)avs/contracts/src/ReclaimTaskManager.sol
(1 hunks)avs/contracts/src/test/operator.yaml
(1 hunks)avs/contracts/src/test/reclaim_test_operator.ecdsa.key.json
(1 hunks)avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh
(1 hunks)package.json
(1 hunks)
💤 Files with no reviewable changes (1)
- .gitmodules
✅ Files skipped from review due to trivial changes (2)
- avs/contracts/.gitmodules
- avs/contracts/src/test/reclaim_test_operator.ecdsa.key.json
🚧 Files skipped from review as they are similar to previous changes (4)
- avs/contracts/lib/eigenlayer-middleware
- avs/contracts/foundry.toml
- avs/contracts/script/utils/UpgradeableProxyLib.sol
- package.json
🧰 Additional context used
🪛 Gitleaks (8.26.0)
avs/contracts/script/deployments/reclaim/31337.json
1-1: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 YAMLlint (1.37.1)
avs/contracts/src/test/operator.yaml
[error] 10-10: no new line character at the end of file
(new-line-at-end-of-file)
🔇 Additional comments (1)
avs/contracts/script/deployments/reclaim/31337.json (1)
1-1
: Approve: Deployment metadata JSON for chain 31337The file correctly records the deployed contract addresses and update metadata for chain ID 31337. No issues found.
🧰 Tools
🪛 Gitleaks (8.26.0)
1-1: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
set -e | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance script robustness with stricter error flags
Currently you enable set -e
, but it's safer to also include -u
and -o pipefail
to catch undefined variables and pipeline failures.
🤖 Prompt for AI Agents
In avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh around lines 23
to 24, the script only uses 'set -e' which stops execution on errors but does
not catch undefined variables or pipeline failures. Update the script to use
'set -euo pipefail' to enable strict error handling by exiting on errors,
treating unset variables as errors, and failing on any command in a pipeline
that fails.
cd ../../contracts/lib/eigenlayer-middleware/lib/eigenlayer-contracts | ||
forge script script/deploy/devnet/deploy_from_scratch.s.sol \ | ||
--rpc-url http://localhost:8545 \ | ||
--broadcast \ | ||
--private-key $PRIVATE_KEY \ | ||
--sig "run(string memory configFile)" \ | ||
-- local/deploy_from_scratch.slashing.anvil.config.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate and quote PRIVATE_KEY
usage
Before running the deployment, ensure $PRIVATE_KEY
is defined to avoid silent failures:
: "${PRIVATE_KEY:?Environment variable PRIVATE_KEY must be set}"
Also quote it in the command to prevent word splitting:
- --private-key $PRIVATE_KEY \
+ --private-key "$PRIVATE_KEY" \
🤖 Prompt for AI Agents
In avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh around lines 33
to 39, the script uses the environment variable PRIVATE_KEY without validating
its presence or quoting it, which can cause silent failures or word splitting
issues. Add a validation line before the forge script command to ensure
PRIVATE_KEY is set, using : "${PRIVATE_KEY:?Environment variable PRIVATE_KEY
must be set}". Then, in the forge script command, quote the variable as
"$PRIVATE_KEY" to prevent word splitting.
@@ -0,0 +1,10 @@ | |||
operator: | |||
address: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote the hexadecimal address
Unquoted 0x…
values can be interpreted as integers by some YAML parsers, leading to overflow or parse errors.
Apply this diff:
- address: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
address: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 | |
address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" |
🤖 Prompt for AI Agents
In avs/contracts/src/test/operator.yaml at line 2, the hexadecimal address is
unquoted, which can cause YAML parsers to misinterpret it as an integer and lead
to errors. Fix this by enclosing the address value in quotes to ensure it is
treated as a string.
operator: | ||
address: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 | ||
delegation_approver_address: "0x0000000000000000000000000000000000000000" | ||
metadata_url: "https://raw.githubusercontent.com/reclaimprotocol/attestor-core/refs/heads/avs-rewards/avs/contracts/src/test/metadata.json" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix metadata_url
to use the branch name
The raw GitHub URL should reference the branch directly, not refs/heads/
. The current link will 404.
Apply this diff:
- metadata_url: "https://raw.githubusercontent.com/reclaimprotocol/attestor-core/refs/heads/avs-rewards/avs/contracts/src/test/metadata.json"
+ metadata_url: "https://raw.githubusercontent.com/reclaimprotocol/attestor-core/avs-rewards/avs/contracts/src/test/metadata.json"
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
metadata_url: "https://raw.githubusercontent.com/reclaimprotocol/attestor-core/refs/heads/avs-rewards/avs/contracts/src/test/metadata.json" | |
metadata_url: "https://raw.githubusercontent.com/reclaimprotocol/attestor-core/avs-rewards/avs/contracts/src/test/metadata.json" |
🤖 Prompt for AI Agents
In avs/contracts/src/test/operator.yaml at line 4, the metadata_url uses an
incorrect GitHub raw URL format with 'refs/heads/' which causes a 404 error.
Update the URL to directly reference the branch name by removing 'refs/heads/'
so the URL points correctly to the branch path.
function executeProcessClaim(address recipient) public { | ||
uint256 nonce = rewardsCoordinator.getDistributionRootsLength(); | ||
amountPerPayment = uint32(amountPerPayment * nonce); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off-by-one & overflow: nonce
multiplier should be (nonce + 1)
and stay uint256
When nonce == 0
this sets amountPerPayment
to 0, breaking the claim test.
- amountPerPayment = uint32(amountPerPayment * nonce);
+ amountPerPayment = amountPerPayment * (nonce + 1);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function executeProcessClaim(address recipient) public { | |
uint256 nonce = rewardsCoordinator.getDistributionRootsLength(); | |
amountPerPayment = uint32(amountPerPayment * nonce); | |
function executeProcessClaim(address recipient) public { | |
uint256 nonce = rewardsCoordinator.getDistributionRootsLength(); | |
amountPerPayment = amountPerPayment * (nonce + 1); |
🤖 Prompt for AI Agents
In avs/contracts/script/SetupPayments.s.sol around lines 124 to 127, the
calculation of amountPerPayment multiplies by nonce, which causes it to be zero
when nonce is zero and also risks overflow by casting to uint32 too early.
Change the multiplier to (nonce + 1) and keep the calculation in uint256 before
assigning or converting to uint32 to avoid overflow and off-by-one errors.
function registerOperator( | ||
address operator, | ||
address avs, | ||
uint32[] calldata operatorSetIds, | ||
bytes calldata data | ||
) external { | ||
// check that the task is valid, hasn't been responsed yet, | ||
// and is being responded in time | ||
require( | ||
keccak256(abi.encode(completedTask.task)) == | ||
allTaskHashes[referenceTaskIndex], | ||
"supplied task does not match the one recorded in the contract" | ||
); | ||
|
||
require( | ||
completedTask.task.expiresAt > uint32(block.timestamp), | ||
"Task has expired" | ||
); | ||
|
||
Claims.SignedClaim memory signedClaim = Claims.SignedClaim( | ||
Claims.CompleteClaimData( | ||
completedTask.task.request.claimHash, | ||
completedTask.task.request.owner, | ||
completedTask.task.createdAt, | ||
1 | ||
), | ||
completedTask.signatures | ||
); | ||
|
||
address[] memory operatorAddrs = new address[]( | ||
completedTask.task.operators.length | ||
); | ||
for (uint i = 0; i < operatorAddrs.length; i++) { | ||
operatorAddrs[i] = completedTask.task.operators[i].addr; | ||
} | ||
|
||
Claims.assertValidSignedClaim(signedClaim, operatorAddrs); | ||
} | ||
|
||
// remove so it cannot be claimed again | ||
delete allTaskHashes[referenceTaskIndex]; | ||
function deregisterOperator( | ||
address operator, | ||
address avs, uint32[] | ||
calldata operatorSetIds | ||
) external { | ||
|
||
// TODO: distribute fees | ||
} | ||
|
||
emit TaskCompleted(referenceTaskIndex, completedTask); | ||
function supportsAVS( | ||
address avs | ||
) external view returns (bool) { | ||
return true; | ||
} | ||
|
||
// HELPER | ||
function registerOperatorSets( | ||
uint256[] calldata operatorSetIds | ||
) external { | ||
// For example, forward to SlashingRegistryCoordinator: | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: required interface functions are no-ops
registerOperator
, deregisterOperator
, and registerOperatorSets
are empty. Calls from EigenLayer infrastructure will succeed silently yet do nothing, leaving the AVS unusable and making on-chain state inconsistent.
Either implement the logic or make them revert with “NOT_IMPLEMENTED”
until finished.
🤖 Prompt for AI Agents
In avs/contracts/src/ReclaimServiceManager.sol between lines 53 and 81, the
functions registerOperator, deregisterOperator, and registerOperatorSets are
currently empty no-ops, which causes silent failures and inconsistent on-chain
state. To fix this, either implement the required logic for these functions or
modify them to immediately revert with the error message "NOT_IMPLEMENTED" to
prevent silent success and signal that the functionality is pending.
function _getRegisteredOperators() internal view returns (OperatorMetadata[] memory) { | ||
OperatorMetadata[] memory operators = new OperatorMetadata[](0); | ||
revert("Not implemented"); | ||
return operators; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stub must be finished or declared abstract
_getRegisteredOperators()
is critical for operator selection yet just reverts. Either:
- Implement retrieval from the stake registry, or
- Mark the contract
abstract
so deployers must supply a concrete child.
🤖 Prompt for AI Agents
In avs/contracts/src/ReclaimTaskManager.sol around lines 340 to 344, the
function _getRegisteredOperators currently reverts and returns an empty array,
which is incomplete. To fix this, either implement the function to retrieve and
return the list of registered operators from the stake registry, or mark the
contract as abstract to require that any contract inheriting from it provides a
concrete implementation of this function.
function initialize( | ||
address initialOwner, | ||
address strategy | ||
) external initializer { | ||
taskCreationMetadata = TaskCreationMetadata( | ||
// 30m | ||
30 * 60, | ||
// 1m | ||
1, | ||
// 5m | ||
5 * 60, | ||
// spend a little bit | ||
2 | ||
); | ||
defaultStrategy = strategy; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Owner & pausable not initialised
initialize
:
- Never calls
__Ownable_init()
/_transferOwnership(initialOwner)
. - Never calls
__Pausable_init()
(required byPausable
). - Ignores
initialOwner
entirely.
Every onlyOwner
/ whenNotPaused
modifier will therefore revert.
🤖 Prompt for AI Agents
In avs/contracts/src/ReclaimTaskManager.sol lines 68 to 83, the initialize
function does not set the contract owner or initialize the pausable
functionality, causing all onlyOwner and whenNotPaused modifiers to fail. Fix
this by calling __Ownable_init() and transferring ownership to initialOwner
using _transferOwnership(initialOwner), and also call __Pausable_init() to
properly initialize the pausable state. Ensure these calls are added at the
start of the initialize function before setting other state variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (5)
avs/contracts/script/utils/ReclaimDeploymentLib.sol (2)
28-37
: Remove duplicate import & fix wrong interface path
CoreDeploymentLib
is imported twice andIStrategy
is still pulled fromIStrategyManager.sol
(which exposesIStrategyManager
, notIStrategy
). Both issues break compilation.-import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; -import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; ... -import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategyManager.sol"; -import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {IStrategy} from "@eigenlayer/contracts/interfaces/IStrategy.sol";
215-221
: Out-of-bounds access in operatorParams loopLoop increments
i
by 1 but consumes indicesi, i+1, i+2
, causing overflow wheni == numQuorums-1
.-for (uint256 i = 0; i < numQuorums; i++) { - quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ - maxOperatorCount: uint32(operator_params[i]), - kickBIPsOfOperatorStake: uint16(operator_params[i + 1]), - kickBIPsOfTotalStake: uint16(operator_params[i + 2]) - }); -} +for (uint256 i = 0; i < numQuorums; i++) { + uint256 base = 3 * i; + quorumsOperatorSetParams[i] = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: uint32(operator_params[base]), + kickBIPsOfOperatorStake: uint16(operator_params[base + 1]), + kickBIPsOfTotalStake: uint16(operator_params[base + 2]) + }); +}avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh (2)
25-25
: Harden script flagsReplace
set -e
with strict mode to catch unset vars & pipe errors.-set -e +set -euo pipefail
39-41
: Validate and quote$PRIVATE_KEY
: "${PRIVATE_KEY:?Environment variable PRIVATE_KEY must be set}" --private-key "$PRIVATE_KEY" \avs/contracts/script/utils/SetupPaymentsLib.sol (1)
56-62
: Operator rewards array still hard-coded to size 2 – breaks >2 operatorsPrevious review already pointed this out, but the code is unchanged.
operatorRewards
is created with a fixed length 2 and thefor
loop iterates only twice, ignoring thenumOperators
argument and silently dropping rewards for additional operators. This blocks any AVS with more than two operators.- IRewardsCoordinator.OperatorReward[] memory operatorRewards = new IRewardsCoordinator.OperatorReward[](2); - for (uint256 i = 0; i < 2; i++) { + IRewardsCoordinator.OperatorReward[] memory operatorRewards = + new IRewardsCoordinator.OperatorReward[](numOperators); + for (uint256 i = 0; i < numOperators; i++) {Please fix or explain why the restriction is intentional.
🧹 Nitpick comments (2)
avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh (1)
35-35
: Quote path variable to handle spaces-cd $EIGEN_CONTRACTS_DIR +cd "$EIGEN_CONTRACTS_DIR"avs/contracts/script/utils/SetupPaymentsLib.sol (1)
269-277
: Dead commented-out code – remove for clarityThe padded-length computation that follows is now implemented in
padLeaves
; these commented lines add noise and risk confusion.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
avs/contracts/script/ReclaimDeployer.s.sol
(1 hunks)avs/contracts/script/SetupPayments.s.sol
(1 hunks)avs/contracts/script/deployments/core/31337.json
(1 hunks)avs/contracts/script/deployments/reclaim/31337.json
(1 hunks)avs/contracts/script/utils/CoreDeploymentLib.sol
(1 hunks)avs/contracts/script/utils/ReclaimDeploymentLib.sol
(1 hunks)avs/contracts/script/utils/SetupPaymentsLib.sol
(1 hunks)avs/contracts/script/utils/Utils.sol
(1 hunks)avs/utils/anvil/start-anvil-chain-with-el-and-avs-deployed.sh
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- avs/contracts/script/deployments/reclaim/31337.json
- avs/contracts/script/utils/CoreDeploymentLib.sol
- avs/contracts/script/utils/Utils.sol
- avs/contracts/script/ReclaimDeployer.s.sol
- avs/contracts/script/SetupPayments.s.sol
🧰 Additional context used
🪛 Gitleaks (8.26.0)
avs/contracts/script/deployments/core/31337.json
3-3: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (2)
avs/contracts/script/utils/ReclaimDeploymentLib.sol (1)
147-151
: Deployed PauserRegistry not recorded inDeploymentData
The newly-created
PauserRegistry
address is never assigned toresult.pauserRegistry
, so the JSON output shows0x000…
. Add:PauserRegistry pausercontract = new PauserRegistry(pausers, admin); +result.pauserRegistry = address(pausercontract);
avs/contracts/script/deployments/core/31337.json (1)
29-37
: Confirm zero-address tokens are intentionalAll token addresses are
0x000…
. Double-check that downstream code can tolerate this or update the JSON once tokens are deployed.
UpgradeableProxyLib.upgrade(result.stakeRegistry, stakeRegistryImpl); | ||
UpgradeableProxyLib.upgrade(result.blsapkRegistry, blsApkRegistryImpl); | ||
UpgradeableProxyLib.upgrade(result.indexRegistry, indexRegistryimpl); | ||
UpgradeableProxyLib.upgradeAndCall( | ||
result.slashingRegistryCoordinator, slashingRegistryCoordinatorImpl, upgradeCall | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proxies upgraded but never initialised – state variables stay zero
StakeRegistry
, BLSApkRegistry
, IndexRegistry
, SocketRegistry
, and InstantSlasher
are upgraded with upgrade()
(no data).
All of these contracts set critical state in their constructor, which is NOT executed behind a proxy. Result: every storage slot is left at the default value, breaking the system.
Refactor to call their dedicated initialize
(or add one) and use upgradeAndCall
:
-UpgradeableProxyLib.upgrade(result.stakeRegistry, stakeRegistryImpl);
+bytes memory stakeInit = abi.encodeCall(
+ StakeRegistry.initialize,
+ (result.slashingRegistryCoordinator, core.delegationManager, core.avsDirectory, core.allocationManager)
+);
+UpgradeableProxyLib.upgradeAndCall(result.stakeRegistry, stakeRegistryImpl, stakeInit);
Repeat for the other four contracts.
Failing to do this will render the deployment unusable.
Also applies to: 203-208
🤖 Prompt for AI Agents
In avs/contracts/script/utils/ReclaimDeploymentLib.sol around lines 165 to 170,
the proxies for StakeRegistry, BLSApkRegistry, IndexRegistry, SocketRegistry,
and InstantSlasher are upgraded using upgrade() without initialization, leaving
their state variables unset. To fix this, refactor the code to call each
contract's initialize function by using upgradeAndCall() with the appropriate
initialization calldata for each proxy. Repeat this fix for the similar code
block at lines 203 to 208 to ensure all proxies are properly initialized after
upgrade.
uint256 operatorRewardAmount = amountPerPayment / numOperators; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Integer division may silently lose reward wei
operatorRewardAmount = amountPerPayment / numOperators;
truncates any remainder.
If amountPerPayment
is not perfectly divisible, the sum of per-operator rewards < amountPerPayment
, leaving dust locked in the contract.
Consider either:
require(amountPerPayment % numOperators == 0, "amountPerPayment not divisible by numOperators");
or distributing the remainder to the first operator to guarantee conservation of value.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In avs/contracts/script/utils/SetupPaymentsLib.sol around lines 56 to 57, the
division of amountPerPayment by numOperators uses integer division which
truncates remainders, causing some wei to be lost and locked in the contract. To
fix this, calculate the base operatorRewardAmount using integer division, then
compute the remainder and add it to the first operator's reward to ensure the
total distributed equals amountPerPayment, preserving all value without loss.
function generateMerkleProof(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory) { | ||
require(leaves.length > 0, "Leaves array cannot be empty"); | ||
require(index < leaves.length, "Index out of bounds"); | ||
|
||
leaves = padLeaves(leaves); | ||
|
||
uint256 n = leaves.length; | ||
uint256 depth = 0; | ||
while ((1 << depth) < n) { | ||
depth++; | ||
} | ||
|
||
bytes32[] memory proof = new bytes32[](depth); | ||
uint256 proofIndex = 0; | ||
|
||
for (uint256 i = 0; i < depth; i++) { | ||
uint256 levelSize = (n + 1) / 2; | ||
uint256 siblingIndex = (index % 2 == 0) ? index + 1 : index - 1; | ||
|
||
if (siblingIndex < n) { | ||
proof[proofIndex] = leaves[siblingIndex]; | ||
proofIndex++; | ||
} | ||
|
||
for (uint256 j = 0; j < levelSize; j++) { | ||
if (2 * j + 1 < n) { | ||
leaves[j] = keccak256(abi.encodePacked(leaves[2 * j], leaves[2 * j + 1])); | ||
} else { | ||
leaves[j] = leaves[2 * j]; | ||
} | ||
} | ||
|
||
n = levelSize; | ||
index /= 2; | ||
} | ||
|
||
return abi.encodePacked(proof); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merkle proof construction leaves zeroed siblings – proofs will fail
generateMerkleProof
pre-allocates the proof
array to depth
, but only fills it when siblingIndex < n
.
For uneven trees, uninitialised array slots remain 0x00…
, and the final abi.encodePacked(proof)
includes these zeros, producing an invalid proof.
Minimal fix:
- bytes32[] memory proof = new bytes32[](depth);
- uint256 proofIndex = 0;
+ bytes32[] memory proof = new bytes32[](depth); // upper bound
+ uint256 proofIndex;
...
- return abi.encodePacked(proof);
+ // shrink to actual size
+ bytes32[] memory compact = new bytes32[](proofIndex);
+ for (uint256 k = 0; k < proofIndex; k++) compact[k] = proof[k];
+ return abi.encodePacked(compact);
Alternatively, scrap this hand-rolled logic and rely on a battle-tested library (e.g. OpenZeppelin MerkleProof).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function generateMerkleProof(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory) { | |
require(leaves.length > 0, "Leaves array cannot be empty"); | |
require(index < leaves.length, "Index out of bounds"); | |
leaves = padLeaves(leaves); | |
uint256 n = leaves.length; | |
uint256 depth = 0; | |
while ((1 << depth) < n) { | |
depth++; | |
} | |
bytes32[] memory proof = new bytes32[](depth); | |
uint256 proofIndex = 0; | |
for (uint256 i = 0; i < depth; i++) { | |
uint256 levelSize = (n + 1) / 2; | |
uint256 siblingIndex = (index % 2 == 0) ? index + 1 : index - 1; | |
if (siblingIndex < n) { | |
proof[proofIndex] = leaves[siblingIndex]; | |
proofIndex++; | |
} | |
for (uint256 j = 0; j < levelSize; j++) { | |
if (2 * j + 1 < n) { | |
leaves[j] = keccak256(abi.encodePacked(leaves[2 * j], leaves[2 * j + 1])); | |
} else { | |
leaves[j] = leaves[2 * j]; | |
} | |
} | |
n = levelSize; | |
index /= 2; | |
} | |
return abi.encodePacked(proof); | |
} | |
function generateMerkleProof(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory) { | |
require(leaves.length > 0, "Leaves array cannot be empty"); | |
require(index < leaves.length, "Index out of bounds"); | |
leaves = padLeaves(leaves); | |
uint256 n = leaves.length; | |
uint256 depth = 0; | |
while ((1 << depth) < n) { | |
depth++; | |
} | |
- bytes32[] memory proof = new bytes32[](depth); | |
- uint256 proofIndex = 0; | |
+ bytes32[] memory proof = new bytes32[](depth); // upper bound | |
+ uint256 proofIndex; | |
for (uint256 i = 0; i < depth; i++) { | |
uint256 levelSize = (n + 1) / 2; | |
uint256 siblingIndex = (index % 2 == 0) ? index + 1 : index - 1; | |
if (siblingIndex < n) { | |
proof[proofIndex] = leaves[siblingIndex]; | |
proofIndex++; | |
} | |
for (uint256 j = 0; j < levelSize; j++) { | |
if (2 * j + 1 < n) { | |
leaves[j] = keccak256(abi.encodePacked(leaves[2 * j], leaves[2 * j + 1])); | |
} else { | |
leaves[j] = leaves[2 * j]; | |
} | |
} | |
n = levelSize; | |
index /= 2; | |
} | |
- return abi.encodePacked(proof); | |
+ // shrink to actual size | |
+ bytes32[] memory compact = new bytes32[](proofIndex); | |
+ for (uint256 k = 0; k < proofIndex; k++) { | |
+ compact[k] = proof[k]; | |
+ } | |
+ return abi.encodePacked(compact); | |
} |
🤖 Prompt for AI Agents
In avs/contracts/script/utils/SetupPaymentsLib.sol between lines 223 and 260,
the generateMerkleProof function pre-allocates the proof array to the tree depth
but does not fill all elements when siblingIndex is out of bounds, leaving
zeroed entries that cause invalid proofs. To fix this, track the actual number
of proof elements added and return only the filled portion of the proof array
encoded with abi.encodePacked. Alternatively, replace the custom implementation
with a well-tested library like OpenZeppelin's MerkleProof to ensure
correctness.
Description
Implements rewards for the decentralised AVS system
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Chores
Documentation
Revert