Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
[submodule "crates/miden-agglayer/solidity-compat/lib/agglayer-contracts"]
path = crates/miden-agglayer/solidity-compat/lib/agglayer-contracts
url = https://github.com/agglayer/agglayer-contracts
[submodule "crates/miden-agglayer/solidity-compat/lib/openzeppelin-contracts-upgradeable"]
path = crates/miden-agglayer/solidity-compat/lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git
branch = release-v4.9
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- Added AggLayer faucet registry to bridge account with conversion metadata, `CONFIG_AGG_BRIDGE` note for faucet registration, and FPI-based asset conversion in `bridge_out` ([#2426](https://github.com/0xMiden/miden-base/pull/2426)).
- Enable `CodeBuilder` to add advice map entries to compiled scripts ([#2275](https://github.com/0xMiden/miden-base/pull/2275)).
- Added `BlockNumber::MAX` constant to represent the maximum block number ([#2324](https://github.com/0xMiden/miden-base/pull/2324)).
- Added single-word `Array` standard ([#2203](https://github.com/0xMiden/miden-base/pull/2203)).
Expand All @@ -12,6 +13,8 @@
- Implemented verification of AggLayer deposits (claims) against GER ([#2295](https://github.com/0xMiden/miden-base/pull/2295), [#2288](https://github.com/0xMiden/miden-base/pull/2288)).
- Added `SignedBlock` struct ([#2355](https://github.com/0xMiden/miden-base/pull/2235)).
- Added `PackageKind` and `ProcedureExport` ([#2358](https://github.com/0xMiden/miden-base/pull/2358)).
- Changed GER storage to a map ([#2388](https://github.com/0xMiden/miden-base/pull/2388)).
- Implemented `assert_valid_ger` procedure for verifying GER against storage ([#2388](https://github.com/0xMiden/miden-base/pull/2388)).
- [BREAKING] Added `get_asset` and `get_initial_asset` kernel procedures and removed `get_balance`, `get_initial_balance` and `has_non_fungible_asset` kernel procedures ([#2369](https://github.com/0xMiden/miden-base/pull/2369)).
- Introduced `TokenMetadata` type to encapsulate fungible faucet metadata ([#2344](https://github.com/0xMiden/miden-base/issues/2344)).
- Added `StandardNote::from_script_root()` and `StandardNote::name()` methods, and exposed `NoteType` `PUBLIC`/`PRIVATE` masks as public constants ([#2411](https://github.com/0xMiden/miden-base/pull/2411)).
Expand Down Expand Up @@ -39,6 +42,11 @@
- [BREAKING] Updated note tag length to support up to 32 bits ([#2329](https://github.com/0xMiden/miden-base/pull/2329)).
- [BREAKING] Moved standard note code into individual note modules ([#2363](https://github.com/0xMiden/miden-base/pull/2363)).
- [BREAKING] Added `miden::standards::note_tag` module for account target note tags ([#2366](https://github.com/0xMiden/miden-base/pull/2366)).
- Unified the underlying representation of `ExitRoot` and `SmtNode` and use type aliases ([#2387](https://github.com/0xMiden/miden-base/pull/2387)).
- [BREAKING] Moved padding to the end of `CLAIM` `NoteStorage` layout ([#2405](https://github.com/0xMiden/miden-base/pull/2405)).

### Fixes
- Fixed byte-to-felt conversion for `ExitRoot` and `SmtNode`'s `to_elements()` method ([#2387](https://github.com/0xMiden/miden-base/pull/2387)).

## 0.13.3 (2026-01-27)

Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ generate-solidity-test-vectors: ## Regenerate Solidity MMR test vectors using Fo
cd crates/miden-agglayer/solidity-compat && forge test -vv --match-test test_generateVectors
cd crates/miden-agglayer/solidity-compat && forge test -vv --match-test test_generateCanonicalZeros
cd crates/miden-agglayer/solidity-compat && forge test -vv --match-test test_generateVerificationProofData
cd crates/miden-agglayer/solidity-compat && forge test -vv --match-test test_generateLeafValueVectors
cd crates/miden-agglayer/solidity-compat && forge test -vv --match-test test_generateClaimAssetVectors

# --- benchmarking --------------------------------------------------------------------------------

Expand Down
5 changes: 5 additions & 0 deletions crates/miden-agglayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ testing = ["miden-protocol/testing"]
# Miden dependencies
miden-assembly = { workspace = true }
miden-core = { workspace = true }
miden-core-lib = { workspace = true }
miden-protocol = { workspace = true }
miden-standards = { workspace = true }
miden-utils-sync = { workspace = true }

# Third-party dependencies
primitive-types = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
miden-agglayer = { features = ["testing"], path = "." }

Expand Down
210 changes: 185 additions & 25 deletions crates/miden-agglayer/asm/bridge/agglayer_faucet.masm
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use miden::agglayer::bridge_in
use miden::core::sys
use miden::agglayer::utils
use miden::agglayer::asset_conversion
use miden::agglayer::eth_address
use miden::protocol::active_account
Expand All @@ -16,6 +18,12 @@ use miden::core::word
# The slot in this component's storage layout where the bridge account ID is stored.
const BRIDGE_ID_SLOT = word("miden::agglayer::faucet")

# Storage slots for conversion metadata.
# Slot 1: [addr_felt0, addr_felt1, addr_felt2, addr_felt3] — first 4 felts of origin token address
const CONVERSION_INFO_1_SLOT = word("miden::agglayer::faucet::conversion_info_1")
# Slot 2: [addr_felt4, origin_network, scale, 0] — remaining address felt + origin network + scale
const CONVERSION_INFO_2_SLOT = word("miden::agglayer::faucet::conversion_info_2")

const PROOF_DATA_WORD_LEN = 134
const LEAF_DATA_WORD_LEN = 8
const OUTPUT_NOTE_DATA_WORD_LEN = 2
Expand All @@ -30,17 +38,33 @@ const LEAF_DATA_KEY_MEM_ADDR = 704
const OUTPUT_NOTE_DATA_MEM_ADDR = 708
const CLAIM_NOTE_DATA_MEM_ADDR = 712

const OUTPUT_NOTE_INPUTS_MEM_ADDR = 0
const OUTPUT_NOTE_STORAGE_MEM_ADDR = 0
const OUTPUT_NOTE_TAG_MEM_ADDR = 574
const OUTPUT_NOTE_SERIAL_NUM_MEM_ADDR = 568
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 = 552
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 = 556

const DESTINATION_ADDRESS_0 = 547
const DESTINATION_ADDRESS_1 = 548
const DESTINATION_ADDRESS_2 = 549
const DESTINATION_ADDRESS_3 = 550
const DESTINATION_ADDRESS_4 = 551
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 = 549
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 = 550
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2 = 551
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_3 = 552
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_4 = 553
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 = 554
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 = 555
const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 = 556

const DESTINATION_ADDRESS_0 = 544
const DESTINATION_ADDRESS_1 = 545
const DESTINATION_ADDRESS_2 = 546
const DESTINATION_ADDRESS_3 = 547
const DESTINATION_ADDRESS_4 = 548

# Memory locals in claim
const CLAIM_PREFIX_MEM_LOC = 5
const CLAIM_SUFFIX_MEM_LOC = 6
const CLAIM_AMOUNT_MEM_LOC_0 = 0
const CLAIM_AMOUNT_MEM_LOC_1 = 4

# Memory locals in build_p2id_output_note
const BUILD_P2ID_AMOUNT_MEM_LOC_0 = 0
const BUILD_P2ID_AMOUNT_MEM_LOC_1 = 4

# P2ID output note constants
const P2ID_NOTE_NUM_STORAGE_ITEMS = 2
Expand All @@ -54,6 +78,120 @@ const P2ID_OUTPUT_NOTE_AMOUNT_MEM_PTR = 611

const ERR_INVALID_CLAIM_PROOF = "invalid claim proof"

# CONVERSION METADATA HELPERS
# =================================================================================================

#! Returns the origin token address (5 felts) from faucet conversion storage.
#!
#! Reads conversion_info_1 (first 4 felts of address) and conversion_info_2 (5th felt)
#! from storage.
#!
#! Inputs: []
#! Outputs: [addr0, addr1, addr2, addr3, addr4]
#!
#! Invocation: exec
pub proc get_origin_token_address
push.CONVERSION_INFO_1_SLOT[0..2]
exec.active_account::get_item
# => [addr3, addr2, addr1, addr0]
exec.word::reverse
# => [addr0, addr1, addr2, addr3]

# Read slot 2: [0, scale, origin_network, addr4]
push.CONVERSION_INFO_2_SLOT[0..2]
exec.active_account::get_item
# => [0, scale, origin_network, addr4, addr0, addr1, addr2, addr3]

# Keep only addr4 from slot 2 and append it after slot 1 limbs
drop drop drop
movdn.4
# => [addr0, addr1, addr2, addr3, addr4]
end

#! Returns the origin network identifier from faucet conversion storage.
#!
#! Inputs: []
#! Outputs: [origin_network]
#!
#! Invocation: exec
pub proc get_origin_network
push.CONVERSION_INFO_2_SLOT[0..2]
exec.active_account::get_item
# => [0, scale, origin_network, addr4]

drop drop swap drop
# => [origin_network]
end

#! Returns the scale factor from faucet conversion storage.
#!
#! Inputs: []
#! Outputs: [scale]
#!
#! Invocation: exec
pub proc get_scale
push.CONVERSION_INFO_2_SLOT[0..2]
exec.active_account::get_item
# => [0, scale, origin_network, addr4]

drop movdn.2 drop drop
# => [scale]
end

#! Converts a native Miden asset amount to origin asset data using the stored
#! conversion metadata (origin_token_address, origin_network, and scale).
#!
#! This procedure is intended to be called via FPI from the bridge account.
#! It reads the faucet's conversion metadata from storage, scales the native amount
#! to U256 format, and returns the result along with origin token address and network.
#!
#! Inputs: [amount, pad(15)]
#! Outputs: [AMOUNT_U256[0], AMOUNT_U256[1], addr0, addr1, addr2, addr3, addr4, origin_network, pad(2)]
#!
#! Where:
#! - amount: The native Miden asset amount
#! - AMOUNT_U256: The scaled amount as 8 u32 limbs (little-endian U256)
#! - addr0..addr4: Origin token address (5 felts, u32 limbs)
#! - origin_network: Origin network identifier
#!
#! Invocation: call
pub proc asset_to_origin_asset
# => [amount, pad(15)]

# Step 1: Get scale from storage
exec.get_scale
# => [scale, amount, pad(15)]
swap
# => [amount, scale, pad(15)]

# Step 2: Scale amount to U256
exec.asset_conversion::scale_native_amount_to_u256
exec.asset_conversion::reverse_limbs_and_change_byte_endianness
# => [U256_LO(4), U256_HI(4), pad(15)]

# Step 3: Get origin token address
exec.get_origin_token_address
# => [addr0, addr1, addr2, addr3, addr4, U256_LO(4), U256_HI(4), pad(15)]

# Move address below the U256 amount
repeat.5 movdn.12 end
# => [U256_LO(4), U256_HI(4), addr0, addr1, addr2, addr3, addr4, pad(15)]

# Step 4: Get origin network
exec.get_origin_network
exec.utils::swap_u32_bytes
# => [origin_network, U256_LO(4), U256_HI(4), addr0..addr4, pad(15)]

# Move origin_network after the address fields
movdn.13
# => [U256_LO(4), U256_HI(4), addr0, addr1, addr2, addr3, addr4, origin_network, pad(15)]

exec.sys::truncate_stack
end

# CLAIM PROCEDURES
# =================================================================================================

#! Inputs: [LEAF_DATA_KEY, PROOF_DATA_KEY]
#! Outputs: []
#!
Expand Down Expand Up @@ -86,8 +224,14 @@ end
# Inputs: []
# Outputs: [U256[0], U256[1]]
proc get_raw_claim_amount
padw mem_loadw_be.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0
padw mem_loadw_be.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_3
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_4
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6
mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7
end

# Inputs: [U256[0], U256[1]]
Expand Down Expand Up @@ -151,11 +295,19 @@ end
#! It reads the destination account ID, amount, and other note parameters from memory to construct
#! the output note.
#!
#! Inputs: []
#! Inputs: [prefix, suffix, AMOUNT[0], AMOUNT[1]]
#! Outputs: []
#!
#! Note: This procedure will be refactored in a follow-up to use leaf data to build the output note.
@locals(8)
proc build_p2id_output_note
# write destination account id into memory for use in note::build_recipient
push.OUTPUT_NOTE_STORAGE_MEM_ADDR add.1 mem_store mem_store.OUTPUT_NOTE_STORAGE_MEM_ADDR

# store amount in memory locals for use in faucets::distribute
loc_storew_be.BUILD_P2ID_AMOUNT_MEM_LOC_0 dropw loc_storew_be.BUILD_P2ID_AMOUNT_MEM_LOC_1 dropw
# => [pad(16)]

# Build P2ID output note
procref.::miden::standards::notes::p2id::main
# => [SCRIPT_ROOT]
Expand All @@ -166,15 +318,8 @@ proc build_p2id_output_note
push.P2ID_NOTE_NUM_STORAGE_ITEMS
# => [note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT]

push.OUTPUT_NOTE_INPUTS_MEM_ADDR
# => [storage_ptr = 0, note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT]

exec.get_destination_account_id_data
# => [prefix, suffix]

# Write destination account id into memory
mem_store.1 mem_store.0
# => []
push.OUTPUT_NOTE_STORAGE_MEM_ADDR
# => [storage_ptr, note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT]

exec.note::build_recipient
# => [RECIPIENT]
Expand All @@ -185,11 +330,10 @@ proc build_p2id_output_note
mem_load.OUTPUT_NOTE_TAG_MEM_ADDR
# => [tag, RECIPIENT]

exec.get_raw_claim_amount
# => [AMOUNT[1], AMOUNT[0], tag, note_type, RECIPIENT]

# TODO: implement scale down logic; stubbed out for now
exec.asset_conversion::scale_u256_to_native_amount
padw loc_loadw_be.BUILD_P2ID_AMOUNT_MEM_LOC_1 padw loc_loadw_be.BUILD_P2ID_AMOUNT_MEM_LOC_0
# => [AMOUNT[0], AMOUNT[1], tag, note_type, RECIPIENT]
exec.asset_conversion::verify_u256_to_native_amount_conversion_stubbed
# => [amount, tag, note_type, RECIPIENT]

exec.faucets::distribute
Expand Down Expand Up @@ -220,6 +364,7 @@ end
#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts)
#! ],
#! LEAF_DATA_KEY => [
#! leafType[1], // Leaf type (1 felt, uint8)
#! originNetwork[1], // Origin network identifier (1 felt, uint32)
#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts)
#! destinationNetwork[1], // Destination network identifier (1 felt, uint32)
Expand All @@ -239,11 +384,22 @@ end
#! - any of the validations in faucets::distribute fail.
#!
#! Invocation: call
@locals(10) # 2 for prefix and suffix, 8 for amount
pub proc claim
# Check AdviceMap values hash to keys & write CLAIM inputs & DATA_KEYs to global memory
exec.batch_pipe_double_words
# => [pad(16)]

# validate_claim will overwrite memory in-place, so we need to load the account and amount
# before calling validate_claim and store it in memory locals
exec.get_destination_account_id_data
loc_store.CLAIM_PREFIX_MEM_LOC loc_store.CLAIM_SUFFIX_MEM_LOC
# => [pad(16)]

exec.get_raw_claim_amount
loc_storew_be.CLAIM_AMOUNT_MEM_LOC_0 dropw loc_storew_be.CLAIM_AMOUNT_MEM_LOC_1 dropw
# => [pad(16)]

# VALIDATE CLAIM
mem_loadw_be.PROOF_DATA_KEY_MEM_ADDR
# => [PROOF_DATA_KEY, pad(12)]
Expand All @@ -256,6 +412,10 @@ pub proc claim
# => [pad(16)]

# Create P2ID output note
loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_1 swapw loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_0
# => [AMOUNT[0], AMOUNT[1], pad(8)]
loc_load.CLAIM_SUFFIX_MEM_LOC loc_load.CLAIM_PREFIX_MEM_LOC
# => [prefix, suffix, AMOUNT[0], AMOUNT[1], pad(8)]
exec.build_p2id_output_note
# => [pad(16)]
end
Expand Down
Loading