Skip to content
Merged
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
11 changes: 7 additions & 4 deletions crates/miden-agglayer/solidity-compat/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
[profile.default]
libs = ["lib"]
out = "out"
solc = "0.8.20"
src = "src"
libs = ["lib"]
optimizer = true
optimizer_runs = 200
out = "out"
solc = "0.8.20"
src = "src"
via_ir = true

remappings = [
"@agglayer/=lib/agglayer-contracts/contracts/",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"amount": 1000000000000000,
"deposit_count": 1,
"description": "L1 bridgeAsset transaction test vectors with valid Merkle proofs",
"destination_address": "0x00000000AA0000000000bb000000cc000000Dd00",
"destination_network": 20,
"global_exit_root": "0xbb7f9cbe48190e8859cbaecc54c8957863c786862bde7cff62df0a8b5adc19bb",
"global_index": "0x0000000000000000000000000000000000000000000000010000000000000000",
"leaf_type": 0,
"leaf_value": "0x8e5f0c4b8526561e30a89f900185ec681cc620fe3cadd8b281fb929063bd27ac",
"local_exit_root": "0x3e9a88b19d477b03175f9db12ba6b20eb33c0fe7a271667031685f034caf5af6",
"mainnet_exit_root": "0x3e9a88b19d477b03175f9db12ba6b20eb33c0fe7a271667031685f034caf5af6",
"metadata": "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045445535400000000000000000000000000000000000000000000000000000000",
"metadata_hash": "0x4d0d9fb7f9ab2f012da088dc1c228173723db7e09147fe4fea2657849d580161",
"origin_network": 0,
"origin_token_address": "0x2DC70fb75b88d2eB4715bc06E1595E6D97c34DFF",
"rollup_exit_root": "0xd18cc25ae65a4e3d95587ffea9411747238567d6e5d3744240554713edefc197",
"smt_proof_local_exit_root": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5",
"0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30",
"0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85",
"0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344",
"0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d",
"0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968",
"0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83",
"0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af",
"0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0",
"0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5",
"0xf8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892",
"0x3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c",
"0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb",
"0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc",
"0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2",
"0x2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f",
"0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a",
"0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0",
"0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0",
"0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2",
"0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9",
"0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377",
"0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652",
"0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef",
"0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d",
"0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0",
"0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e",
"0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e",
"0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322",
"0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735",
"0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9"
],
"smt_proof_rollup_exit_root": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "@agglayer/v2/lib/DepositContractV2.sol";
import "@agglayer/lib/GlobalExitRootLib.sol";

/**
* @title ClaimAssetTestVectorsLocalTx
* @notice Test contract that generates test vectors for an L1 bridgeAsset transaction.
* This simulates calling bridgeAsset() on the PolygonZkEVMBridgeV2 contract
* and captures all relevant data including VALID Merkle proofs.
*
* Run with: forge test -vv --match-contract ClaimAssetTestVectorsLocalTx
*
* The output can be used to verify Miden's ability to process L1 bridge transactions.
*/
contract ClaimAssetTestVectorsLocalTx is Test, DepositContractV2 {
/**
* @notice Generates bridge asset test vectors with VALID Merkle proofs.
* Simulates a user calling bridgeAsset() to bridge tokens from L1 to Miden.
*
* Output file: test-vectors/bridge_asset_vectors.json
*/
function test_generateClaimAssetVectorsLocalTx() public {
string memory obj = "root";

// ====== BRIDGE TRANSACTION PARAMETERS ======

uint8 leafType = 0;
uint32 originNetwork = 0;
address originTokenAddress = 0x2DC70fb75b88d2eB4715bc06E1595E6D97c34DFF;
uint32 destinationNetwork = 20;
address destinationAddress = 0x00000000AA0000000000bb000000cc000000Dd00;
uint256 amount = 1000000000000000;

bytes memory metadata = abi.encode("Test Token", "TEST", uint8(18));
bytes32 metadataHash = keccak256(metadata);

// ====== COMPUTE LEAF VALUE AND ADD TO TREE ======

bytes32 leafValue = getLeafValue(
leafType, originNetwork, originTokenAddress, destinationNetwork, destinationAddress, amount, metadataHash
);

// Add the leaf to the deposit tree to generate valid Merkle proof
_addLeaf(leafValue);

// Get the deposit count (leaf index) - depositCount is uint256 in DepositContractBase
uint256 depositCountValue = uint256(depositCount);

// Get the local exit root (root of the deposit tree)
bytes32 localExitRoot = getRoot();

// ====== GENERATE MERKLE PROOF ======

// Generate canonical zeros for the Merkle proof
bytes32[32] memory canonicalZeros = _computeCanonicalZeros();

// Build the Merkle proof from _branch array and canonical zeros
// The leaf index is depositCountValue - 1 (0-indexed)
uint256 leafIndex = depositCountValue - 1;
bytes32[32] memory smtProofLocal = _generateLocalProof(leafIndex, canonicalZeros);

// For mainnet deposits, the rollup proof is all zeros
bytes32[32] memory smtProofRollup;
for (uint256 i = 0; i < 32; i++) {
smtProofRollup[i] = bytes32(0);
}

// ====== COMPUTE EXIT ROOTS ======

// For a simulated L1 bridge transaction:
// - mainnetExitRoot is the local exit root from the deposit tree
// - rollupExitRoot is simulated (deterministic for reproducibility)
bytes32 mainnetExitRoot = localExitRoot;
bytes32 rollupExitRoot = keccak256(abi.encodePacked("rollup_exit_root_simulated"));

// Compute global exit root
bytes32 globalExitRoot = GlobalExitRootLib.calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot);

// ====== VERIFY MERKLE PROOF ======

// Verify that the generated proof is valid
require(
this.verifyMerkleProof(leafValue, smtProofLocal, uint32(leafIndex), mainnetExitRoot),
"Generated Merkle proof is invalid!"
);

// ====== COMPUTE GLOBAL INDEX ======

// Global index for mainnet deposits: (1 << 64) | leafIndex
// Note: leafIndex is 0-based (depositCount - 1), matching how the bridge contract
// extracts it via uint32(globalIndex) in _verifyLeaf()
uint256 globalIndex = (uint256(1) << 64) | uint256(leafIndex);

// ====== SERIALIZE SMT PROOFS ======
_serializeProofs(obj, smtProofLocal, smtProofRollup);

// Scoped block 2: Serialize transaction parameters
{
vm.serializeUint(obj, "leaf_type", leafType);
vm.serializeUint(obj, "origin_network", originNetwork);
vm.serializeAddress(obj, "origin_token_address", originTokenAddress);
vm.serializeUint(obj, "destination_network", destinationNetwork);
vm.serializeAddress(obj, "destination_address", destinationAddress);
vm.serializeUint(obj, "amount", amount);
vm.serializeBytes(obj, "metadata", metadata);
vm.serializeBytes32(obj, "metadata_hash", metadataHash);
vm.serializeBytes32(obj, "leaf_value", leafValue);
}

// Scoped block 3: Serialize state, exit roots, and finalize
{
vm.serializeUint(obj, "deposit_count", depositCountValue);
vm.serializeBytes32(obj, "global_index", bytes32(globalIndex));
vm.serializeBytes32(obj, "local_exit_root", localExitRoot);
vm.serializeBytes32(obj, "mainnet_exit_root", mainnetExitRoot);
vm.serializeBytes32(obj, "rollup_exit_root", rollupExitRoot);
vm.serializeBytes32(obj, "global_exit_root", globalExitRoot);

string memory json = vm.serializeString(
obj, "description", "L1 bridgeAsset transaction test vectors with valid Merkle proofs"
);

string memory outputPath = "test-vectors/claim_asset_vectors_local_tx.json";
vm.writeJson(json, outputPath);

console.log("Generated claim asset local tx test vectors with valid Merkle proofs");
console.log("Output file:", outputPath);
console.log("Leaf index:", leafIndex);
console.log("Deposit count:", depositCountValue);
}
}

/**
* @notice Computes the canonical zero hashes for the Sparse Merkle Tree.
* @dev Each level i has zero hash: keccak256(zero[i-1], zero[i-1])
* @return canonicalZeros Array of 32 zero hashes, one per tree level
*/
function _computeCanonicalZeros() internal pure returns (bytes32[32] memory canonicalZeros) {
bytes32 current = bytes32(0);
for (uint256 i = 0; i < 32; i++) {
canonicalZeros[i] = current;
current = keccak256(abi.encodePacked(current, current));
}
}

/**
* @notice Generates the SMT proof for the local exit root.
* @dev For each level i:
* - If bit i of leafIndex is 1: use _branch[i] (sibling on left)
* - If bit i of leafIndex is 0: use canonicalZeros[i] (sibling on right)
* @param leafIndex The 0-indexed position of the leaf in the tree
* @param canonicalZeros The precomputed canonical zero hashes
* @return smtProofLocal The 32-element Merkle proof array
*/
function _generateLocalProof(uint256 leafIndex, bytes32[32] memory canonicalZeros)
internal
view
returns (bytes32[32] memory smtProofLocal)
{
for (uint256 i = 0; i < 32; i++) {
// Check if bit i of leafIndex is set
if ((leafIndex >> i) & 1 == 1) {
// Bit is 1: sibling is on the left, use _branch[i]
smtProofLocal[i] = _branch[i];
} else {
// Bit is 0: sibling is on the right (or doesn't exist), use zero hash
smtProofLocal[i] = canonicalZeros[i];
}
}
}

/**
* @notice Helper function to serialize SMT proofs (avoids stack too deep)
* @param obj The JSON object key
* @param smtProofLocal The local exit root proof
* @param smtProofRollup The rollup exit root proof
*/
function _serializeProofs(string memory obj, bytes32[32] memory smtProofLocal, bytes32[32] memory smtProofRollup)
internal
{
bytes32[] memory smtProofLocalDyn = new bytes32[](32);
bytes32[] memory smtProofRollupDyn = new bytes32[](32);
for (uint256 i = 0; i < 32; i++) {
smtProofLocalDyn[i] = smtProofLocal[i];
smtProofRollupDyn[i] = smtProofRollup[i];
}

vm.serializeBytes32(obj, "smt_proof_local_exit_root", smtProofLocalDyn);
vm.serializeBytes32(obj, "smt_proof_rollup_exit_root", smtProofRollupDyn);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import "@agglayer/v2/lib/DepositContractV2.sol";
import "@agglayer/lib/GlobalExitRootLib.sol";

/**
* @title ClaimAssetTestVectors
* @title ClaimAssetTestVectorsRealTx
* @notice Test contract that generates comprehensive test vectors for verifying
* compatibility between Solidity's claimAsset and Miden's implementation.
*
* Generates vectors for both LeafData and ProofData from a real transaction.
*
* Run with: forge test -vv --match-contract ClaimAssetTestVectors
* Run with: forge test -vv --match-contract ClaimAssetTestVectorsRealTx
*
* The output can be compared against the Rust ClaimNoteStorage implementation.
*/
contract ClaimAssetTestVectors is Test, DepositContractV2 {
contract ClaimAssetTestVectorsRealTx is Test, DepositContractV2 {
/**
* @notice Generates claim asset test vectors from real Katana transaction and saves to JSON.
* Uses real transaction data from Katana explorer:
Expand Down Expand Up @@ -68,7 +68,7 @@ contract ClaimAssetTestVectors is Test, DepositContractV2 {

// forge-std JSON serialization supports `bytes32[]` but not `bytes32[32]`.
bytes32[] memory smtProofLocalExitRootDyn = new bytes32[](32);
for (uint i = 0; i < 32; i++) {
for (uint256 i = 0; i < 32; i++) {
smtProofLocalExitRootDyn[i] = smtProofLocalExitRoot[i];
}

Expand Down Expand Up @@ -105,7 +105,8 @@ contract ClaimAssetTestVectors is Test, DepositContractV2 {

// Original metadata from the transaction (ABI encoded: name, symbol, decimals)
// name = "Vault Bridge ETH", symbol = "vbETH", decimals = 18
bytes memory metadata = hex"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000105661756c7420427269646765204554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057662455448000000000000000000000000000000000000000000000000000000";
bytes memory metadata =
hex"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000105661756c7420427269646765204554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057662455448000000000000000000000000000000000000000000000000000000";
bytes32 metadataHash = keccak256(metadata);

// Compute the leaf value using the official DepositContractV2 implementation
Expand All @@ -129,7 +130,7 @@ contract ClaimAssetTestVectors is Test, DepositContractV2 {
string memory json = vm.serializeBytes32(obj, "leaf_value", leafValue);

// Save to file
string memory outputPath = "test-vectors/claim_asset_vectors.json";
string memory outputPath = "test-vectors/claim_asset_vectors_real_tx.json";
vm.writeJson(json, outputPath);
}
}
Expand Down
Loading