## Summary
- sync `contracts/lib/eigenlayer-contracts` to tag
`v1.8.0-testnet-final` and refresh `EIGENLAYER.md` with the new commit
reference
- update local/test deployment flows to deploy the upstream
`EigenStrategy`, feed it into `AllocationManager`/`StrategyManager`, and
adopt the revised `EigenPod` constructor
- drop the obsolete `AllocationManagerMock` stub and replace its usage
with targeted `vm.mockCall` stubs that return `slashOperator` share data
- adjust slasher unit tests to match the new ABI so DataHaven stays
aligned with EigenLayer 1.8 semantics
## Testing
- forge build
- forge test
## Summary
Fixes the CI build failure in the `task-ts-build` workflow caused by
Foundry v1.4.2's Solar linter not being able to resolve Snowbridge's
context-specific import remappings.
## Problem
The Snowbridge submodule uses context-specific remappings (prefixed with
`:`) for its dependencies:
- `lib/snowbridge/contracts/:openzeppelin/` → OpenZeppelin contracts
- `lib/snowbridge/contracts/:prb/math/` → PRB Math library
Foundry v1.4.2's Solar linter doesn't understand these context-specific
remappings and fails with errors like:
```
error: file openzeppelin/utils/cryptography/MerkleProof.sol not found
error: file prb/math/src/UD60x18.sol not found
```
## Solution
Added global remappings that the linter can understand:
```toml
"openzeppelin/=lib/snowbridge/contracts/lib/openzeppelin-contracts/contracts/",
"prb/math/=lib/snowbridge/contracts/lib/prb-math/",
```
### Why This Works
- The linter can now resolve `openzeppelin/` and `prb/math/` imports
globally
- These global remappings take **lower precedence** than
context-specific ones during compilation
- The compiler still uses the context-specific remappings (with `:`)
when compiling Snowbridge contracts
- The linter uses the global remappings when checking all files
## Changes
### Commit 1: Add global remappings
- `contracts/foundry.toml`: Added 2 global remapping entries
### Commit 2: Apply forge fmt
- Applied automatic formatting via `forge fmt` to ensure code style
consistency
- Multi-line formatting for long import statements and function
signatures
- No functional changes - purely formatting updates
## Testing
✅ Local build succeeds with `forge build`
✅ No Snowbridge import resolution errors
✅ `forge fmt --check` passes with no formatting issues
✅ Only linting notes/warnings remain (not errors)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
### Summary
Removes the obsolete `operator/test` folder that was no longer being
used and contained outdated test scripts.
### Changes
- ✅ Deleted `operator/test/` directory and all contents
- ✅ Moved `test_message_encoding.sh` script to `operator/scripts/` for
proper organization
- ✅ Updated comment reference in `contracts/test/MessageEncoding.t.sol`
to point to new script location
### Impact
- **No breaking changes** - All legitimate test files remain properly
organized within their respective pallets
- **Cleaner codebase** - Removes unused/obsolete code
### PR Description
Add a comprehensive end-to-end test that validates rewards distribution
across the full system (chain → bridge → execution environment).
### Use cases covered
- Verify the rewards infrastructure is correctly deployed and reachable.
- Detect the end-of-era rewards emission and capture its essential data.
- Confirm the cross-chain delivery and execution of the rewards message.
- Ensure the rewards registry updates with the new root and can be
queried.
- Generate per-validator proofs for claiming rewards.
- Successfully claim rewards for a validator and validate the payout is
reflected.
- Prevent a second (double) claim for the same index with a proper
rejection.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
We remove `calculateMerkleRootUnsorted` and `buildMerkleProofUnsorted`
and instead introduce a new parameter `sorting`. This parameter
`sorting` will indicate if we need to sort the pair before hashing the
new node.
## Summary of changes
- We decided to remove the topics and nonce from the massage encoding
since we don't use them (original commit:
ee2a3f2fd4).
- Besides, we already have a nonce at the Snowbridge message level
f4ab5c2b2e/operator/primitives/snowbridge/inbound-queue/src/v2/message.rs (L105)
- I had to recreate the static test for _encoding_ (happens in
[DataHavenSnowbridgeMessages.sol](d12d40634f/contracts/src/libraries/DataHavenSnowbridgeMessages.sol)
) / _decoding_ (happens in
[operator/primitives/bridge/src/lib.rs)](f9f9cc65fe/operator/primitives/bridge/src/lib.rs).
Now it matches the current structure. The idea is that now we can test
that we don't break the decoding in followup refactoring.
- Fixes a problem with EigenLayer validator addresses. In all our
contracts we were using `bytes32` to refer to a Solochain validator
address. But on our Substrate change we actually expect AccountId20, so
only 20 bytes. This was causing the decoding to fail.
- I opted for the minimal change that would be to take the right-most 20
bytes to send that to our chain. But we might want aswell to limit our
EigenLayer contracts to be only 20 bytes long. @ahmadkaouk showcase this
[here](92a34c273c)
- Adds a bash script to run the static test. The test will compile the
contracts, run the encoding test, compile the operator, and run the
decoding test. This saves a huge amount of time since we don't need to
run the full e2e setup. The way of running it is the following:
```bash
cd operator/test/scripts
./test_message_encoding.sh
```
- As a consequence of this PR, the execution relayer now works properly.
EDIT:
> [!IMPORTANT]
**We decided to use 20-byte addresses in our contracts**. So what is
stated above is not valid anymore.
The change implies that the mapping from Ethereum addresses to bytes32
addresses now it's a mapping as follows:
dd3ba99ac0/contracts/src/DataHavenServiceManager.sol (L51-L52)
I've updated helper functions, tests, etc to be compliant with this
change. The execution relayer and beefy relayer look stable now.
---------
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
# Description
This PR implements a comprehensive overhaul of the `RewardsRegistry`
contract to maintain complete history of reward merkle roots while
providing index-based claim tracking for operators. The new architecture
enables operators to claim rewards from any historical merkle root
instead of only the latest one. To do so, it:
- Adds the `merkleRootHistory` storage array to the contract, in which
we keep all rewards roots that ever came from the DataHaven side.
- Adds the `operatorClaimedByIndex` storage map to the contract, in
which we keep track, for each validator and root index, if it has
claimed it or not.
- This works even for new validators, since theoretically with this
system you could argue they could claim older roots that they were not a
part of which would be catastrophic, but they could never draft a
correct proof for those to claim them.
- Keeps some of the interface from before the overhaul, to have quick
access to the latest rewards merkle root through `getLatestMerkleRoot()`
and to claim rewards for it with `claimRewards()`. This is because the
expected behaviour is for validators to claim their rewards every era.
- Adds a way to batch claim rewards with `claimRewardsBatch()`. This
function allows a validator to claim rewards for multiple root indices
in one call by providing multiple proofs, useful if the validator has
fallen behind claims and has to catch up, although special care will
have to be taken by it to avoid reaching the gas limit of a transaction.
## Storage Efficiency Analysis
One might think this solution is not as storage-efficient as other
solutions that we can think of (I even had two other alternatives in
mind as well), but a simple back-of-the-envelope calculation gives us
peace of mind that the impact of this solution on the overal state size
of the chain is negligible:
### Assumptions (Worst Case Scenario):
- 1,000 validators (actual estimate for DataHaven: ~50/100 validators)
- 6-hour eras (most-likely scenario, following what Polkadot does:
~24-hour eras)
- Which means 4 merkle root updates per day
### Annual Storage Requirements:
- Merkle Root History: **46,720 bytes/year**
- 4 roots/day × 32 bytes/root × 365 days/year = 46,720 bytes/year
- Operator Claim Tracking: **~1.46 MB/year**
- 1,000 operators × 1 boolean/(operator * root index) × 1 byte/boolean ×
4 root indices/day × 365 days/year = 1,460,000 bytes/year
- **Total: ~1.5 MB/year**
This represents negligible storage overhead compared to the significant
operational benefits gained.
## TODO
Since we want to allow the operators/validators to only have to interact
with the AVS contract (that's why the `claimRewards` functions have the
`onlyAVS` modifier), we still have to:
- [x] Add the required functions to the AVS to allow operators to claim
their rewards.
- [x] Adds comprehensive unit tests for them.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
## This PR includes:
- Running the execution relayer on the CLI
- Modifying the Payload generation in the `DataHavenServiceManager.sol`
- Modified the `EigenLayerMessageProcessor` to work with the
ValidatorSet update message, but for this change we are loosing the
generic message type (it was the only way to make it work so far).
- Adds a `--no-wait` argument to the cli launch and stop commands to
bootstrap faster.
### Testing the Snowbridge message encoding / decoding
- Added`MessageEncoding.t.sol` is documented and explains how to
generate bin data to use in the rust test.
- Added a Rust unit test to `EigenLayerMessageProcessor` to compare the
message encoding/decoding taking the bytes from a file (previously
generated with some mock data).
Specifically, we want that:
3cbca0db6d/contracts/src/libraries/DataHavenSnowbridgeMessages.sol (L78-L85)
Generates the right bytes encoding for
0e2c9cd518/operator/primitives/bridge/src/lib.rs (L51)
If the test passes, it's very likely that the CLI will also pass, if
not, then we might wanna check something else is missing.
### Breaking change ⚠️
For compatibility reasons with Snowbridge contracts (they call specific
extrinsics of specific pallets), I had to rename:
- `InboundQueueV2` -> `EthereumInboundQueueV2`
- `OutboundQueueV2` -> `EthereumOutboundQueueV2`
## For follow up PRs:
- Add an automated way of generating the Solidity bytes fo testing, so
we don't need to maintain the MessageEncoding.t.sol and generate the
binary data manually.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added support for the "execution" relayer type in relay configuration,
parsing, and CLI launch utilities.
- Introduced a Solidity test contract for encoding and logging validator
set messages.
- Added a comprehensive "start:all" script to streamline launching and
setup processes.
- **Enhancements**
- Improved message encoding for validator set updates, aligning with new
struct field names and message formats.
- Updated relay configuration schema and validation to support execution
relayers and OFAC settings.
- Increased beacon datastore capacity and adjusted relay scheduling
parameters in configuration files.
- **Refactor**
- Renamed runtime type aliases for inbound/outbound queues to more
descriptive names across mainnet, stagenet, and testnet.
- Centralized and streamlined validator set update logic in CLI
utilities.
- Centralized message decoding logic and improved visibility of message
fields in Rust components.
- **Bug Fixes**
- Improved error handling and decoding logic for message processing in
Rust components.
- **Tests**
- Added Rust and Solidity tests for message encoding and processing
validation.
- **Chores**
- Updated dependencies and feature flags in Rust project configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Facundo Farall <37149322+ffarall@users.noreply.github.com>
In this PR:
1. Implement application-specific functionalities in the
`DataHavenServiceManager` contract:
1. Registering of 3 operator sets: Validators, BSPs and MSPs.
2. Allowlisted sign up of operators.
3. Integration with Snowbridge to send message of new validator set.
2. Basic testing of the above functionalities.
3. Tests now use less mocked contracts (especially from EigenLayer).
4. Refactor of `SignUpOperator` script, which now supports the three
kinds of Operator sets.
In this PR:
1. Implement deployment script for all contracts involved. This includes
1. EigenLayer core contracts.
2. Snowbridge contracts.
3. Our custom contracts to interact with both protocols.
4. Update README to document functionalities of `contracts` directory.
Future work:
1. Deployment of EigenLayer contracts should be conditional, as we
wouldn't deploy them in testnet or mainnet, but we would do it in an
internal stagenet.
5. Cleaning up unused smart contracts.
This PR:
1. Adds some missing functionalities to connect the Snowbridge contracts
to the RewardsRegistry contract, through an Agent.
2. Adds a test suite for this integration, with a happy and unhappy
path. The former being with a valid and honest update of rewards, which
then a validator uses to claim them. The latter with a malicious message
that tries to update the rewards root, but it's not allowed.
This PR adds an initial implementation for a rewards registry, which
will be the contract in charge of allowing DataHaven validators to claim
the rewards they earned for being validators in the previous epoch. The
logic behind it is as follows:
- Whenever an epoch finishes, the corresponding BEEFY block gets relayed
to Ethereum through Snowbridge. This BEEFY block contains, in its
`extra` field, the merkle root of the tree that contains as leafs all
the message commitments of the messages of corresponding block, one of
which is the rewards distribution message.
- The rewards distribution message commitment is the root of the merkle
tree where each leaf is a tuple of the operator ID and the obtained era
points in the finished epoch. In this case, the operator ID is the
corresponding validator's Ethereum address.
- When the rewards distribution message is received, Snowbridge
validates it using the aforementioned BEEFY block and then dispatches
it. The dispatch invokes the `callContract` function of the
`RewardsAgent` agent, with the corresponding parameters so that this
agent calls the `updateRewardsMerkleRoot` function of the
`RewardsRegistry` contract with the new rewards distribution message
commitment.
- After this root is updated, any validator/operator can submit a proof
that it is in a leaf of the merkle tree that produced that root, which
means it has pending rewards to claim, through the
`ServiceManagerBase`'s `claimOperatorRewards` function.
- Each operator set of the AVS can have an assigned `RewardsRegistry`
contract. Operator sets that do not have an assigned `RewardsRegistry`
contract won't be able to received rewards.
This PR also adds two separate unit-test suites: one for the added
functionality to the `ServiceManagerBase` contract and one specific to
the new `RewardsRegistry` contract.
> [!CAUTION]
The `RewardsAgent` agent is the only one allowed to update the rewards'
merkle root, which means if a malicious user could get access to it it
could set the pending rewards to be claimed to an arbitrary tree that
benefits it. Extreme caution must be taken in the Substrate side so only
validated messages are sent to the Ethereum side, as to not allow any
users to impersonate being this agent.
### TODO:
Ideally, we would use the `RewardsCoordinator` contract from the
EigenLayer core to distribute the rewards, but currently that adds a
huge overhead for Operators since they'd have to wait for EigenLayer's
SideCar to snapshot state and update the distribution root (which
happens once a day), generate a proof that they belong to the tree of
that distribution root, store it while waiting for the `activationDelay`
(currently a week) to pass, and just then they would be able to claim
their earned rewards.
---------
Co-authored-by: Facundo Farall <37149322+ffarall@users.noreply.github.com>
This PR:
- Sets up the slasher infrastructure with the base functionality
required (in `ISlasher.sol`, `SlasherBase.sol` and
`SlasherBaseStorage.sol`) and adds the tests for it (in
`SlasherBase.t.sol`).
- Adds an implementation of a more complex slasher (in
`IVetoableSlasher.sol` and `VetoableSlasher.sol`) and tests for it (in
`VetoableSlasher.t.sol`).
- Modifies the `ServiceManagerBase` contract to use the new
`VetoableSlasher` contract to manage slashing.
- Updates mocks and tests to reflect the newly added functionality.
---------
Co-authored-by: Facundo Farall <37149322+ffarall@users.noreply.github.com>
* forge install: forge-std
v1.9.6
* chore: 🏗️ Add `forge-std` submodule
* chore: 🏗️ Setup foundry project for AVS contracts
* ci: 👷 Restrict push execution of CI to `main` branch
* ci: 💚 Put restriction to `main` branch in `push` not in `pull_request`