Skip to content

feat(agglayer): store and use metadata hash for bridge-out leaves#2583

Open
mmagician wants to merge 18 commits intoagglayerfrom
mmagician-claude/agglayer-metadata-hash
Open

feat(agglayer): store and use metadata hash for bridge-out leaves#2583
mmagician wants to merge 18 commits intoagglayerfrom
mmagician-claude/agglayer-metadata-hash

Conversation

@mmagician
Copy link
Collaborator

@mmagician mmagician commented Mar 11, 2026

Summary

  • Store pre-computed keccak256(abi.encode(name, symbol, decimals)) metadata hash in faucet storage (2 value slots)
  • Read it via FPI during bridge-out leaf construction instead of pushing zeros
  • Add MetadataHash::from_token_info and encode_token_metadata Rust helpers with Solidity ABI encoding compatibility
  • Regenerate Solidity MMR test vectors with real metadata hash values

Context

When a user calls claimAsset() on PolygonZkEVMBridgeV2, they pass the raw metadata bytes as a parameter. The flow is:

  1. Leaf verification: The bridge reconstructs the merkle leaf using keccak256(metadata) as the metadataHash field, and verifies the merkle proof against the exit root. If the hash doesn't match what's in the leaf, the proof fails.
  2. Token lookup: The bridge computes tokenInfoHash = keccak256(abi.encodePacked(originNetwork, originTokenAddress)) and checks tokenInfoToWrappedToken[tokenInfoHash] to see if a wrapped token already exists.
  3. If no wrapped token exists:
    • Calls _deployWrappedToken(tokenInfoHash, metadata) which:
      • Deploys a transparent proxy via CREATE2 using tokenInfoHash as the salt (making the address deterministic across chains)
      • Decodes the metadata: (string name, string symbol, uint8 decimals) = abi.decode(metadata, (string, string, uint8))
      • Calls initialize(name, symbol, decimals) on the proxy to set up the ERC20
    • Mints tokens to the destination address
    • Stores bidirectional mappings: tokenInfoHash -> wrappedAddress and wrappedAddress -> {originNetwork, originTokenAddress}
  4. If wrapped token already exists: Just mints more tokens to the destination address. The metadata is still verified via the merkle proof but not used for deployment.

The key point: the metadata bytes serve to initialize the wrapped token. The claimer/relayer must provide the correct raw metadata as part of the claim process (they can get the individual elements from the Miden faucet storage), and it its hash must match what is added to the leaf in LET upon bridging out Miden -> EVM.

The metadata hash is computed off-chain at faucet creation time and stored in two storage slots. For future onchain verification during token registration see #2586.

Test plan

  • MetadataHash::from_token_info("Test Token", "TEST", 18) matches Solidity test vector hash 0x4d0d9fb7...
  • All 39 agglayer integration tests pass (bridge_in, bridge_out, config, MMR, etc.)
  • Bridge-out leaf values match regenerated Solidity MMR frontier vectors
  • make lint passes (clippy, format, typos, toml, machete)

Closes #2453

🤖 Generated with Claude Code

claude added 3 commits March 11, 2026 10:52
The metadata_hash field in bridge-out leaves was previously zeroed,
which would cause EVM-side claim verification to fail (no preimage
for bytes32(0) under keccak256). This stores a pre-computed
keccak256(abi.encode(name, symbol, decimals)) in faucet storage and
reads it via FPI during bridge-out leaf construction.

Changes:
- Add MetadataHash::from_token_info and encode_token_metadata helpers
  with Solidity ABI encoding compatibility (verified against test vectors)
- Add metadata_hash_lo/hi storage slots to AggLayerFaucet
- Add get_metadata_hash FPI-callable procedure to faucet MASM
- Update bridge_out.masm to fetch metadata hash via FPI instead of
  pushing zeros
- Export get_metadata_hash from faucet component
- Regenerate Solidity MMR test vectors with real metadata hash
- Add TODO in register_faucet for future on-chain verification

Closes #2453

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add bounds assertion to encode_token_metadata for string lengths
- Make encode_token_metadata pub(crate) since it's only used internally

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mmagician mmagician force-pushed the mmagician-claude/agglayer-metadata-hash branch from 2affd1c to d6d9127 Compare March 11, 2026 11:12
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mmagician mmagician added agglayer PRs or issues related to AggLayer bridging integration pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority labels Mar 12, 2026
claude added 3 commits March 12, 2026 16:26
…or ABI encoding

Swap `tiny-keccak` for `miden_crypto::hash::keccak::Keccak256` to avoid
an extra cargo dependency, and replace the hand-rolled ABI encoding with
`alloy-sol-types` `sol!` macro for type-safe `abi.encode(string, string,
uint8)`. Tests now read expected values from the test vector JSON instead
of hardcoding them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hash

Use `swapdw dropw dropw` instead of `exec.sys::truncate_stack` to
explicitly drop the 8 excess padding elements in `get_metadata_hash`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ity test

Replace verbose TODO in bridge_config.masm with a concise version
referencing issue #2586. In the Solidity test, compute METADATA_HASH via
`keccak256(abi.encode(...))` instead of hardcoding the constant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mmagician mmagician force-pushed the mmagician-claude/agglayer-metadata-hash branch from d7e1b9c to c57ca64 Compare March 12, 2026 16:32

This comment was marked as resolved.

claude and others added 6 commits March 14, 2026 08:35
Use workspace dependency for serde in dev-dependencies and rename
sol!-generated TokenMetadata to SolTokenMetadata to avoid ambiguity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add padw padw padw padw before the get_metadata_hash FPI call,
matching the pattern established in bridge_in.masm (cfb6211).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add padw padw padw padw before the get_metadata_hash FPI call,
  matching the pattern from bridge_in.masm (cfb6211).
- Reorder: load PROC_MAST_ROOT first via procref, then faucet ID
  on top via mem_load, eliminating movup.5 movup.5.
- Use temp global memory (not loc_loadw_be) to load faucet ID after
  procref, since loc_loadw_be after procref conflicts with FPI state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

This comment was marked as resolved.

claude and others added 2 commits March 15, 2026 09:21
Merge agglayer into metadata-hash branch, resolving conflict in lib.rs.
The agglayer branch moved AggLayerBridge and AggLayerFaucet to separate
modules (bridge.rs, faucet.rs). Added metadata hash support to the new
faucet.rs module and updated the faucet_helpers test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mmagician mmagician requested a review from Copilot March 15, 2026 11:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end support for Solidity-compatible token metadata hashing in the AggLayer flow by storing a precomputed metadata hash on the faucet and retrieving it via FPI during bridge-out leaf construction, with corresponding updates to test vectors and dependencies.

Changes:

  • Add MetadataHash (keccak256 over Solidity abi.encode(name, symbol, decimals)) and store it in two faucet storage slots.
  • Update bridge-out MASM to fetch the faucet’s metadata hash via FPI and include it in leaf data / MMR computation.
  • Refresh Solidity tests + JSON vectors and update Rust tests/call sites for the new faucet constructor parameter.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
deny.toml Allow CC0-1.0 license for newly introduced dependencies.
crates/miden-testing/tests/agglayer/faucet_helpers.rs Update faucet creation in tests to provide MetadataHash.
crates/miden-testing/tests/agglayer/bridge_out.rs Update bridge-out tests to pass MetadataHash into faucet creation.
crates/miden-testing/tests/agglayer/bridge_in.rs Update faucet creation in tests to pass metadata hash from leaf data.
crates/miden-agglayer/src/lib.rs Thread metadata_hash through faucet account creation helpers.
crates/miden-agglayer/src/faucet.rs Add metadata-hash storage slots and persist hash into faucet component storage.
crates/miden-agglayer/src/eth_types/metadata_hash.rs Implement Solidity ABI encoding + keccak hashing, plus compatibility tests.
crates/miden-agglayer/solidity-compat/test/MMRTestVectors.t.sol Generate vectors using a real metadata hash instead of zero.
crates/miden-agglayer/solidity-compat/test-vectors/mmr_frontier_vectors.json Update expected leaves/roots to match new metadata hash behavior.
crates/miden-agglayer/Cargo.toml Add alloy-sol-types, miden-crypto, and dev deps for new tests.
crates/miden-agglayer/asm/components/faucet.masm Re-export get_metadata_hash from the faucet component.
crates/miden-agglayer/asm/agglayer/faucet/mod.masm Add storage constants and implement get_metadata_hash procedure.
crates/miden-agglayer/asm/agglayer/bridge/bridge_out.masm Fetch metadata hash via FPI and write it into leaf data before hashing/MMR update.
crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Add TODO note about verification on faucet registration.
CHANGELOG.md Document the new metadata-hash behavior in AggLayer faucet/bridge-out.
Cargo.toml Add workspace dependency definition for alloy-sol-types.
Cargo.lock Lock new transitive dependencies pulled in by alloy-sol-types and related crates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mmagician mmagician marked this pull request as ready for review March 15, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agglayer PRs or issues related to AggLayer bridging integration pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants