From 0764fc741510cc26eb695da82d9e7d65ae04ad26 Mon Sep 17 00:00:00 2001 From: yogesh0509 Date: Mon, 7 Jul 2025 00:01:42 +0530 Subject: [PATCH 1/3] draft fix for mmr index --- contracts/L2/src/core/ZeroXBridgeL2.cairo | 64 ++++--- contracts/L2/tests/test_ZeroXBridgeL2.cairo | 174 ++++++++++++++++++++ 2 files changed, 219 insertions(+), 19 deletions(-) diff --git a/contracts/L2/src/core/ZeroXBridgeL2.cairo b/contracts/L2/src/core/ZeroXBridgeL2.cairo index 5bd9664..a051a5f 100644 --- a/contracts/L2/src/core/ZeroXBridgeL2.cairo +++ b/contracts/L2/src/core/ZeroXBridgeL2.cairo @@ -64,8 +64,8 @@ pub mod ZeroXBridgeL2 { verified_roots: Map, burn_nonce: Map, rates: Rates, - // Merkle Manager Storage - mmr: MMR, + mmr_root: felt252, + mmr_last_pos: felt252, node_index_to_root: Map, commitment_hash_to_index: Map, last_peaks: Vec, @@ -171,8 +171,9 @@ pub mod ZeroXBridgeL2 { self.oracle_address.write(oracle_address); let rates = Rates { min_rate, max_rate }; self.rates.write(rates); - let mmr: MMR = Default::default(); - self.mmr.write(mmr); + self.mmr_root.write(0); + self.mmr_last_pos.write(0); + self.leaves_count.write(0); } #[abi(embed_v0)] @@ -382,13 +383,11 @@ pub mod ZeroXBridgeL2 { #[abi(embed_v0)] impl MerkleImpl of IMerkleManager { fn get_root_hash(self: @ContractState) -> felt252 { - let mmr = self.mmr.read(); - mmr.root + self.mmr_root.read() } fn get_element_count(self: @ContractState) -> felt252 { - let mmr = self.mmr.read(); - mmr.last_pos.into() + self.mmr_last_pos.read() } fn get_commitment_index(self: @ContractState, commitment_hash: felt252) -> felt252 { @@ -416,26 +415,38 @@ pub mod ZeroXBridgeL2 { peaks: Array, proof: Array, ) -> Result { - let mmr = self.mmr.read(); - mmr.verify_proof(index, commitment_hash, peaks.span(), proof.span()) + let mut mmr: MMR = Default::default(); + mmr.root = self.mmr_root.read(); + mmr.last_pos = self.mmr_last_pos.read().try_into().unwrap(); + + MMRTrait::verify_proof(@mmr, index, commitment_hash, peaks.span(), proof.span()) } } #[generate_trait] impl InternalFunctions of InternalFunctionsTrait { fn append_withdrawal_hash(ref self: ContractState, commitment_hash: felt252) { - let mut mmr = self.mmr.read(); - let last_peaks = self.get_last_peaks().span(); + let mut mmr: MMR = Default::default(); + mmr.root = self.mmr_root.read(); + mmr.last_pos = self.mmr_last_pos.read().try_into().unwrap(); + + let last_peaks = self.get_last_peaks(); let mut leaves_count = self.leaves_count.read(); - match mmr.append(commitment_hash, last_peaks) { + match MMRTrait::append(ref mmr, commitment_hash, last_peaks.span()) { Result::Ok(( root_hash, peaks, )) => { + // Store the new root and last position + self.mmr_root.write(root_hash); + self.mmr_last_pos.write(mmr.last_pos.into()); self.node_index_to_root.write(mmr.last_pos, root_hash); + + // Calculate the correct MMR leaf index using the helper function + let correct_leaf_index = Self::leaf_count_to_mmr_index(leaves_count); + self.commitment_hash_to_index.write(commitment_hash, correct_leaf_index); + leaves_count += 1; - - self.commitment_hash_to_index.write(commitment_hash, mmr.last_pos.into()); self.leaves_count.write(leaves_count); let last_peaks_len = last_peaks.len(); @@ -466,17 +477,32 @@ pub mod ZeroXBridgeL2 { .emit( Event::WithdrawalHashAppended( WithdrawalHashAppended { - index: mmr.last_pos.into(), + index: correct_leaf_index, commitment_hash: commitment_hash, - root_hash: mmr.root, + root_hash: root_hash, }, ), ); - - self.mmr.write(mmr); }, Result::Err(err) => { panic(array![err]) }, } } + + fn leaf_count_to_mmr_index(leaf_count: felt252) -> felt252 { + if leaf_count == 0 { + return 0; + } + + // Convert to u64 for arithmetic operations + let mut internal_nodes: u64 = 0; + let mut temp: u64 = leaf_count.try_into().unwrap(); + + while temp > 0 { + temp = temp / 2; // Right shift equivalent for division by 2 + internal_nodes += temp; + }; + + leaf_count + internal_nodes.into() + } } } diff --git a/contracts/L2/tests/test_ZeroXBridgeL2.cairo b/contracts/L2/tests/test_ZeroXBridgeL2.cairo index 9cd0aa0..f9a95fa 100644 --- a/contracts/L2/tests/test_ZeroXBridgeL2.cairo +++ b/contracts/L2/tests/test_ZeroXBridgeL2.cairo @@ -10,6 +10,7 @@ use l2::interfaces::IZeroXBridgeL2::{ }; use l2::interfaces::IxZBErc20::{IXZBERC20Dispatcher, IXZBERC20DispatcherTrait}; use l2::mocks::MockRegistry::{IMockRegistryDispatcher, IMockRegistryDispatcherTrait}; +use l2::interfaces::IMerkleManager::{IMerkleManagerDispatcher, IMerkleManagerDispatcherTrait}; use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin_utils::serde::SerializedAppend; use snforge_std::{ @@ -466,4 +467,177 @@ fn test_insufficient_proof_data() { IZeroXBridgeL2Dispatcher { contract_address: bridge_addr } .mint_and_claim_xzb(proof, commitment_hash, eth_address, r, s, y_parity); } +#[test] +fn test_mmr_index_fix_single_withdrawal() { + // Test that the first withdrawal has correct index (should be 0) + let token_addr = deploy_xzb(); + let proof_registry_addr = deploy_registry(); + let oracle_addr = deploy_oracle(); + let bridge_addr = deploy_bridge(token_addr, proof_registry_addr, oracle_addr); + + let alice_addr = alice(); + let owner_addr = owner(); + let burn_amount = 1000_u256 * PRECISION; + + // Setup: mint tokens to Alice + cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); + IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount); + + // Setup: set TVL + cheat_caller_address(oracle_addr, owner_addr, CheatSpan::TargetCalls(1)); + IL2OracleDispatcher { contract_address: oracle_addr }.set_total_tvl(100_000_u256 * PRECISION); + + // Approve and burn + cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1)); + IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount); + + let mut spy = spy_events(); + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + // Verify the event contains index 0 for the first withdrawal + let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; + let leaves_count = merkle_manager.get_leaves_count(); + assert(leaves_count == 1, 'Expected 1 leaf'); +} + +#[test] +fn test_mmr_index_fix_multiple_withdrawals() { + // Test that multiple withdrawals have sequential correct indices + let token_addr = deploy_xzb(); + let proof_registry_addr = deploy_registry(); + let oracle_addr = deploy_oracle(); + let bridge_addr = deploy_bridge(token_addr, proof_registry_addr, oracle_addr); + + let alice_addr = alice(); + let owner_addr = owner(); + let burn_amount = 1000_u256 * PRECISION; + + // Setup: mint tokens to Alice + cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); + IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 5); + + // Setup: set TVL + cheat_caller_address(oracle_addr, owner_addr, CheatSpan::TargetCalls(1)); + IL2OracleDispatcher { contract_address: oracle_addr }.set_total_tvl(100_000_u256 * PRECISION); + + // Approve all burns at once + cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1)); + IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount * 5); + + let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; + + // First burn + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + let leaves_count_1 = merkle_manager.get_leaves_count(); + assert(leaves_count_1 == 1, 'Expected 1 leaf'); + + // Second burn + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + let leaves_count_2 = merkle_manager.get_leaves_count(); + assert(leaves_count_2 == 2, 'Expected 2 leaves'); + + // Third burn (this is where the original bug would manifest) + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + let leaves_count_3 = merkle_manager.get_leaves_count(); + assert(leaves_count_3 == 3, 'Expected 3 leaves'); + + // Fourth burn + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + let leaves_count_4 = merkle_manager.get_leaves_count(); + assert(leaves_count_4 == 4, 'Expected 4 leaves'); +} + +#[test] +fn test_mmr_index_fix_event_emission() { + // Test that the WithdrawalHashAppended event emits the correct leaf index + let token_addr = deploy_xzb(); + let proof_registry_addr = deploy_registry(); + let oracle_addr = deploy_oracle(); + let bridge_addr = deploy_bridge(token_addr, proof_registry_addr, oracle_addr); + + let alice_addr = alice(); + let owner_addr = owner(); + let burn_amount = 1000_u256 * PRECISION; + + // Setup + cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); + IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 3); + + cheat_caller_address(oracle_addr, owner_addr, CheatSpan::TargetCalls(1)); + IL2OracleDispatcher { contract_address: oracle_addr }.set_total_tvl(100_000_u256 * PRECISION); + + cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1)); + IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount * 3); + + // Test the third burn specifically (where the bug would occur) + // First two burns to setup state + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + // Third burn with event monitoring + let mut spy = spy_events(); + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + // The event should contain the correct leaf index (4 for the 3rd leaf in MMR structure) + // Expected MMR positions: leaf 0 -> index 0, leaf 1 -> index 1, leaf 2 -> index 4 + // This validates that our helper function calculates the correct MMR index + + // We can't easily test the exact index without accessing internal state, + // but we can verify the burn completed successfully and incremented leaf count + let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; + let final_leaves_count = merkle_manager.get_leaves_count(); + assert(final_leaves_count == 3, 'Expected 3 leaves'); +} + +#[test] +fn test_mmr_commitment_hash_retrieval() { + // Test that commitment hashes can be retrieved with correct indices + let token_addr = deploy_xzb(); + let proof_registry_addr = deploy_registry(); + let oracle_addr = deploy_oracle(); + let bridge_addr = deploy_bridge(token_addr, proof_registry_addr, oracle_addr); + + let alice_addr = alice(); + let owner_addr = owner(); + let burn_amount = 1000_u256 * PRECISION; + + // Setup + cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); + IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 3); + + cheat_caller_address(oracle_addr, owner_addr, CheatSpan::TargetCalls(1)); + IL2OracleDispatcher { contract_address: oracle_addr }.set_total_tvl(100_000_u256 * PRECISION); + + cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1)); + IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount * 3); + + let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; + + // Perform burns and store the expected commitment hashes + + // First burn + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + // Second burn + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + // Third burn (the critical test case) + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + // Verify all burns completed and leaf count is correct + let final_leaves_count = merkle_manager.get_leaves_count(); + assert(final_leaves_count == 3, 'Expected 3 leaves total'); +} From 62a57f369865c5da9ab3db6fc39e1473c688145c Mon Sep 17 00:00:00 2001 From: yogesh0509 Date: Sat, 23 Aug 2025 12:43:33 +0530 Subject: [PATCH 2/3] fixed peak count issue --- contracts/L2/package-lock.json | 11 - contracts/L2/package.json | 1 - contracts/L2/src/core/ZeroXBridgeL2.cairo | 98 +++-- contracts/L2/tests/test_ZeroXBridgeL2.cairo | 125 +++---- contracts/L2/yarn.lock | 392 ++++++++++++++++---- 5 files changed, 425 insertions(+), 202 deletions(-) diff --git a/contracts/L2/package-lock.json b/contracts/L2/package-lock.json index ee447d8..5d72964 100644 --- a/contracts/L2/package-lock.json +++ b/contracts/L2/package-lock.json @@ -16,7 +16,6 @@ "starknet": "^6.24.1" }, "devDependencies": { - "@types/commander": "^2.12.0", "@types/node": "^24.0.4", "ts-node": "^10.9.2", "typescript": "^5.3.3" @@ -182,16 +181,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/commander": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@types/commander/-/commander-2.12.0.tgz", - "integrity": "sha512-DDmRkovH7jPjnx7HcbSnqKg2JeNANyxNZeUvB0iE+qKBLN+vzN5iSIwt+J2PFSmBuYEut4mgQvI/fTX9YQH/vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "*" - } - }, "node_modules/@types/node": { "version": "24.0.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.4.tgz", diff --git a/contracts/L2/package.json b/contracts/L2/package.json index 68a4403..fc1e3f8 100644 --- a/contracts/L2/package.json +++ b/contracts/L2/package.json @@ -28,5 +28,4 @@ "ts-node": "^10.9.2", "typescript": "^5.3.3" } - "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/contracts/L2/src/core/ZeroXBridgeL2.cairo b/contracts/L2/src/core/ZeroXBridgeL2.cairo index a051a5f..9c287f1 100644 --- a/contracts/L2/src/core/ZeroXBridgeL2.cairo +++ b/contracts/L2/src/core/ZeroXBridgeL2.cairo @@ -64,8 +64,8 @@ pub mod ZeroXBridgeL2 { verified_roots: Map, burn_nonce: Map, rates: Rates, - mmr_root: felt252, - mmr_last_pos: felt252, + // Merkle Manager Storage + mmr: MMR, node_index_to_root: Map, commitment_hash_to_index: Map, last_peaks: Vec, @@ -171,8 +171,8 @@ pub mod ZeroXBridgeL2 { self.oracle_address.write(oracle_address); let rates = Rates { min_rate, max_rate }; self.rates.write(rates); - self.mmr_root.write(0); - self.mmr_last_pos.write(0); + let mmr: MMR = Default::default(); + self.mmr.write(mmr); self.leaves_count.write(0); } @@ -383,11 +383,13 @@ pub mod ZeroXBridgeL2 { #[abi(embed_v0)] impl MerkleImpl of IMerkleManager { fn get_root_hash(self: @ContractState) -> felt252 { - self.mmr_root.read() + let mmr = self.mmr.read(); + mmr.root } fn get_element_count(self: @ContractState) -> felt252 { - self.mmr_last_pos.read() + let mmr = self.mmr.read(); + mmr.last_pos.into() } fn get_commitment_index(self: @ContractState, commitment_hash: felt252) -> felt252 { @@ -398,8 +400,12 @@ pub mod ZeroXBridgeL2 { fn get_last_peaks(self: @ContractState) -> Array { let mut peaks = array![]; - for i in 0..self.last_peaks.len() { - peaks.append(self.last_peaks.at(i).read()); + let len = self.last_peaks.len(); + for i in 0..len { + let peak = self.last_peaks.at(i).read(); + if peak != 0 { // Only include non-zero peaks + peaks.append(peak); + } } peaks } @@ -415,10 +421,7 @@ pub mod ZeroXBridgeL2 { peaks: Array, proof: Array, ) -> Result { - let mut mmr: MMR = Default::default(); - mmr.root = self.mmr_root.read(); - mmr.last_pos = self.mmr_last_pos.read().try_into().unwrap(); - + let mmr = self.mmr.read(); MMRTrait::verify_proof(@mmr, index, commitment_hash, peaks.span(), proof.span()) } } @@ -426,51 +429,26 @@ pub mod ZeroXBridgeL2 { #[generate_trait] impl InternalFunctions of InternalFunctionsTrait { fn append_withdrawal_hash(ref self: ContractState, commitment_hash: felt252) { - let mut mmr: MMR = Default::default(); - mmr.root = self.mmr_root.read(); - mmr.last_pos = self.mmr_last_pos.read().try_into().unwrap(); - + let mut mmr = self.mmr.read(); let last_peaks = self.get_last_peaks(); let mut leaves_count = self.leaves_count.read(); match MMRTrait::append(ref mmr, commitment_hash, last_peaks.span()) { - Result::Ok(( - root_hash, peaks, - )) => { + Result::Ok((root_hash, peaks)) => { // Store the new root and last position - self.mmr_root.write(root_hash); - self.mmr_last_pos.write(mmr.last_pos.into()); self.node_index_to_root.write(mmr.last_pos, root_hash); - // Calculate the correct MMR leaf index using the helper function - let correct_leaf_index = Self::leaf_count_to_mmr_index(leaves_count); + // Calculate the correct MMR leaf index + let correct_leaf_index = Self::leaf_count_to_mmr_index(leaves_count + 1); self.commitment_hash_to_index.write(commitment_hash, correct_leaf_index); leaves_count += 1; self.leaves_count.write(leaves_count); - let last_peaks_len = last_peaks.len(); - let peaks_len = peaks.len(); - - if last_peaks_len > peaks_len { - // Overwrite up to peaks_len, then set the rest to 0 - for i in 0..peaks_len { - let mut storage_ptr = self.last_peaks.at(i.into()); - storage_ptr.write(*peaks.at(i)); - } - for i in peaks_len..last_peaks_len { - let mut storage_ptr = self.last_peaks.at(i.into()); - storage_ptr.write(0); - }; - } else { - // Overwrite up to last_peaks_len, then append the rest - for i in 0..last_peaks_len { - let mut storage_ptr = self.last_peaks.at(i.into()); - storage_ptr.write(*peaks.at(i)); - } - for i in last_peaks_len..peaks_len { - self.last_peaks.push(*peaks.at(i)); - }; + // Clear and update peaks storage properly + self.clear_peaks_storage(); + for i in 0..peaks.len() { + self.last_peaks.push(*peaks.at(i)); } self @@ -483,26 +461,42 @@ pub mod ZeroXBridgeL2 { }, ), ); + + // Write the updated MMR back to storage + self.mmr.write(mmr); }, Result::Err(err) => { panic(array![err]) }, } } + fn clear_peaks_storage(ref self: ContractState) { + // Clear all existing peaks from storage + let current_len = self.last_peaks.len(); + for _i in 0..current_len { + self.last_peaks.pop(); + } + } + fn leaf_count_to_mmr_index(leaf_count: felt252) -> felt252 { if leaf_count == 0 { return 0; } - // Convert to u64 for arithmetic operations + // For MMR, the first leaf is at index 1, second at index 2, etc. + // But we need to account for internal nodes let mut internal_nodes: u64 = 0; let mut temp: u64 = leaf_count.try_into().unwrap(); - while temp > 0 { - temp = temp / 2; // Right shift equivalent for division by 2 - internal_nodes += temp; - }; + // Count internal nodes created by building the MMR + let mut level = 1_u64; + while level < temp { + internal_nodes += temp / (level * 2); + level *= 2; + } - leaf_count + internal_nodes.into() + // The MMR index is leaf position + internal nodes before it + let mmr_index = leaf_count + internal_nodes.into(); + mmr_index } } -} +} \ No newline at end of file diff --git a/contracts/L2/tests/test_ZeroXBridgeL2.cairo b/contracts/L2/tests/test_ZeroXBridgeL2.cairo index f9a95fa..a1dcd92 100644 --- a/contracts/L2/tests/test_ZeroXBridgeL2.cairo +++ b/contracts/L2/tests/test_ZeroXBridgeL2.cairo @@ -467,6 +467,7 @@ fn test_insufficient_proof_data() { IZeroXBridgeL2Dispatcher { contract_address: bridge_addr } .mint_and_claim_xzb(proof, commitment_hash, eth_address, r, s, y_parity); } + #[test] fn test_mmr_index_fix_single_withdrawal() { // Test that the first withdrawal has correct index (should be 0) @@ -495,15 +496,20 @@ fn test_mmr_index_fix_single_withdrawal() { cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - // Verify the event contains index 0 for the first withdrawal + // Verify the leaf count and MMR state let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; let leaves_count = merkle_manager.get_leaves_count(); assert(leaves_count == 1, 'Expected 1 leaf'); + + // Verify MMR root was set (not zero) + let root_hash = merkle_manager.get_root_hash(); + assert(root_hash != 0, 'Root hash should not be zero'); } #[test] fn test_mmr_index_fix_multiple_withdrawals() { - // Test that multiple withdrawals have sequential correct indices + // Test that multiple withdrawals have correct MMR indices + // Expected MMR indices: leaf 0 -> 0, leaf 1 -> 1, leaf 2 -> 2, leaf 3 -> 4, leaf 4 -> 5, etc. let token_addr = deploy_xzb(); let proof_registry_addr = deploy_registry(); let oracle_addr = deploy_oracle(); @@ -527,80 +533,46 @@ fn test_mmr_index_fix_multiple_withdrawals() { let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; - // First burn + // First burn (leaf 0 -> MMR index 0) cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); let leaves_count_1 = merkle_manager.get_leaves_count(); assert(leaves_count_1 == 1, 'Expected 1 leaf'); - // Second burn + // Second burn (leaf 1 -> MMR index 1) cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); let leaves_count_2 = merkle_manager.get_leaves_count(); assert(leaves_count_2 == 2, 'Expected 2 leaves'); - // Third burn (this is where the original bug would manifest) + // Third burn (leaf 2 -> MMR index 2) - this is where the original bug would manifest cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); let leaves_count_3 = merkle_manager.get_leaves_count(); assert(leaves_count_3 == 3, 'Expected 3 leaves'); - // Fourth burn + // Fourth burn (leaf 3 -> MMR index 4) - critical test case cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); let leaves_count_4 = merkle_manager.get_leaves_count(); assert(leaves_count_4 == 4, 'Expected 4 leaves'); -} - -#[test] -fn test_mmr_index_fix_event_emission() { - // Test that the WithdrawalHashAppended event emits the correct leaf index - let token_addr = deploy_xzb(); - let proof_registry_addr = deploy_registry(); - let oracle_addr = deploy_oracle(); - let bridge_addr = deploy_bridge(token_addr, proof_registry_addr, oracle_addr); - - let alice_addr = alice(); - let owner_addr = owner(); - let burn_amount = 1000_u256 * PRECISION; - - // Setup - cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); - IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 3); - - cheat_caller_address(oracle_addr, owner_addr, CheatSpan::TargetCalls(1)); - IL2OracleDispatcher { contract_address: oracle_addr }.set_total_tvl(100_000_u256 * PRECISION); - - cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1)); - IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount * 3); - // Test the third burn specifically (where the bug would occur) - // First two burns to setup state + // Fifth burn (leaf 4 -> MMR index 5) cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - - cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); - IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - - // Third burn with event monitoring - let mut spy = spy_events(); - cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); - IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - - // The event should contain the correct leaf index (4 for the 3rd leaf in MMR structure) - // Expected MMR positions: leaf 0 -> index 0, leaf 1 -> index 1, leaf 2 -> index 4 - // This validates that our helper function calculates the correct MMR index - - // We can't easily test the exact index without accessing internal state, - // but we can verify the burn completed successfully and incremented leaf count - let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; - let final_leaves_count = merkle_manager.get_leaves_count(); - assert(final_leaves_count == 3, 'Expected 3 leaves'); + let leaves_count_5 = merkle_manager.get_leaves_count(); + assert(leaves_count_5 == 5, 'Expected 5 leaves'); + + // Verify MMR element count reflects the total number of nodes (leaves + internal nodes) + let element_count = merkle_manager.get_element_count(); + let leaves_count_u256: u256 = leaves_count_5.into(); + let element_count_u256: u256 = element_count.into(); + assert(element_count_u256 > leaves_count_u256, 'Element count > leaf count'); } -#[test] -fn test_mmr_commitment_hash_retrieval() { - // Test that commitment hashes can be retrieved with correct indices +#[test] +fn test_mmr_leaf_index_calculation_edge_cases() { + // Test edge cases for the leaf index calculation helper function let token_addr = deploy_xzb(); let proof_registry_addr = deploy_registry(); let oracle_addr = deploy_oracle(); @@ -612,32 +584,45 @@ fn test_mmr_commitment_hash_retrieval() { // Setup cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); - IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 3); + IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 8); cheat_caller_address(oracle_addr, owner_addr, CheatSpan::TargetCalls(1)); IL2OracleDispatcher { contract_address: oracle_addr }.set_total_tvl(100_000_u256 * PRECISION); cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1)); - IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount * 3); + IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount * 8); let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr }; - // Perform burns and store the expected commitment hashes - - // First burn - cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); - IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - - // Second burn - cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); - IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - - // Third burn (the critical test case) - cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); - IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + // Test power-of-2 boundaries where MMR structure changes significantly + // These are critical test points for the leaf index calculation + + // Burns 1-8 to test various MMR tree shapes + let mut i: u32 = 1; + loop { + if i > 8 { + break; + } + cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); + IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); + + let current_leaves = merkle_manager.get_leaves_count(); + let expected_leaves: felt252 = i.into(); + assert(current_leaves == expected_leaves, 'Incorrect leaf count'); + + // Verify MMR is still valid after each burn + let root_hash = merkle_manager.get_root_hash(); + assert(root_hash != 0, 'Root hash should not be zero'); + + i += 1; + }; - // Verify all burns completed and leaf count is correct + // Final verification let final_leaves_count = merkle_manager.get_leaves_count(); - assert(final_leaves_count == 3, 'Expected 3 leaves total'); -} + assert(final_leaves_count == 8, 'Expected 8 leaves total'); + let final_element_count = merkle_manager.get_element_count(); + let final_leaves_count_u256: u256 = final_leaves_count.into(); + let final_element_count_u256: u256 = final_element_count.into(); + assert(final_element_count_u256 > final_leaves_count_u256, 'Element count > leaf count'); +} \ No newline at end of file diff --git a/contracts/L2/yarn.lock b/contracts/L2/yarn.lock index 5ebfbf6..e647da7 100644 --- a/contracts/L2/yarn.lock +++ b/contracts/L2/yarn.lock @@ -4,61 +4,120 @@ "@adraffy/ens-normalize@1.10.1": version "1.10.1" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz" integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.0" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@noble/curves@~1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz" + integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== + dependencies: + "@noble/hashes" "1.6.0" + "@noble/curves@1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz" integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== dependencies: "@noble/hashes" "1.3.2" -"@noble/curves@1.7.0", "@noble/curves@~1.7.0": +"@noble/curves@1.7.0": version "1.7.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz" integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== dependencies: "@noble/hashes" "1.6.0" +"@noble/hashes@~1.6.0": + version "1.6.1" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz" + integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w== + "@noble/hashes@1.3.2": version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== "@noble/hashes@1.6.0": version "1.6.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz" integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== -"@noble/hashes@~1.6.0": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.1.tgz#df6e5943edcea504bac61395926d6fd67869a0d5" - integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w== - "@scure/base@1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.1.tgz#dd0b2a533063ca612c17aa9ad26424a2ff5aa865" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz" integrity sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ== "@scure/starknet@1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/starknet/-/starknet-1.1.0.tgz#d1902e053d98196e161b9b2c3996b20999094e7a" + resolved "https://registry.npmjs.org/@scure/starknet/-/starknet-1.1.0.tgz" integrity sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ== dependencies: "@noble/curves" "~1.7.0" "@noble/hashes" "~1.6.0" +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/node@*", "@types/node@^24.0.4": + version "24.0.4" + resolved "https://registry.npmjs.org/@types/node/-/node-24.0.4.tgz" + integrity sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA== + dependencies: + undici-types "~7.8.0" + "@types/node@22.7.5": version "22.7.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== dependencies: undici-types "~6.19.2" abi-wan-kanabi@^2.2.3: version "2.2.4" - resolved "https://registry.yarnpkg.com/abi-wan-kanabi/-/abi-wan-kanabi-2.2.4.tgz#47ebbafbb7f8df81773efbdcca60cdda8008c821" + resolved "https://registry.npmjs.org/abi-wan-kanabi/-/abi-wan-kanabi-2.2.4.tgz" integrity sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg== dependencies: ansicolors "^0.3.2" @@ -66,39 +125,78 @@ abi-wan-kanabi@^2.2.3: fs-extra "^10.0.0" yargs "^17.7.2" +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.15.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + aes-js@4.0.0-beta.5: version "4.0.0-beta.5" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + resolved "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz" integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^4.0.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansicolors@^0.3.2, ansicolors@~0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + resolved "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz" integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + cardinal@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" + resolved "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz" integrity sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw== dependencies: ansicolors "~0.3.2" redeyed "~2.1.0" +chalk@^5.3.0: + version "5.4.1" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== + +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== + dependencies: + restore-cursor "^5.0.0" + +cli-spinners@^2.9.2: + version "2.9.2" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + cliui@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -107,35 +205,60 @@ cliui@^8.0.1: color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +commander@^14.0.0: + version "14.0.0" + resolved "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz" + integrity sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dotenv@^16.5.0: + version "16.5.0" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz" + integrity sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg== + +emoji-regex@^10.3.0: + version "10.4.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz" + integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== + emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== escalade@^3.1.1: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== esprima@~4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== ethers@^6.14.3: - version "6.14.4" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.14.4.tgz#0f6fbc562a8425c7c888da307fa71ef796be0c04" - integrity sha512-Jm/dzRs2Z9iBrT6e9TvGxyb5YVKAPLlpna7hjxH7KH/++DSh2T/JVmQUv7iHI5E55hDbp/gEVvstWYXVxXFzsA== + version "6.14.3" + resolved "https://registry.npmjs.org/ethers/-/ethers-6.14.3.tgz" + integrity sha512-qq7ft/oCJohoTcsNPFaXSQUm457MA5iWqkf1Mb11ujONdg7jBI6sAOrHaTi3j0CBqIGFSCeR/RMc+qwRRub7IA== dependencies: "@adraffy/ens-normalize" "1.10.1" "@noble/curves" "1.2.0" @@ -147,7 +270,7 @@ ethers@^6.14.3: fetch-cookie@~3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-3.0.1.tgz#6a77f7495e1a639ae019db916a234db8c85d5963" + resolved "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.0.1.tgz" integrity sha512-ZGXe8Y5Z/1FWqQ9q/CrJhkUD73DyBU9VF0hBQmEO/wPHe4A9PKTjplFDLeFX8aOsYypZUcX5Ji/eByn3VCVO3Q== dependencies: set-cookie-parser "^2.4.8" @@ -155,7 +278,7 @@ fetch-cookie@~3.0.0: fs-extra@^10.0.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" @@ -164,22 +287,42 @@ fs-extra@^10.0.0: get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz" + integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== + isomorphic-fetch@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz" integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== dependencies: node-fetch "^2.6.1" @@ -187,77 +330,130 @@ isomorphic-fetch@~3.0.0: jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" +log-symbols@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz" + integrity sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw== + dependencies: + chalk "^5.3.0" + is-unicode-supported "^1.3.0" + lossless-json@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lossless-json/-/lossless-json-4.1.1.tgz#b7cbac00c222a68072a9037563dfc4c71cee52f0" - integrity sha512-HusN80C0ohtT9kOHQH7EuUaqzRQsnekpa+2ot8OzvW0iC08dq/YtM/7uKwwajldQsCrHyC8q9fz3t3L+TmDltA== + version "4.1.0" + resolved "https://registry.npmjs.org/lossless-json/-/lossless-json-4.1.0.tgz" + integrity sha512-DgoRs42jH/yNubp8iinRqvG0xn5awHKXVY+7lGYjBaByoHGZt/Dz5Jkaf5znP2XHbTnAA+bbkhK3lMIaf3+92A== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== node-fetch@^2.6.1: version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + +ora@^8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz" + integrity sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw== + dependencies: + chalk "^5.3.0" + cli-cursor "^5.0.0" + cli-spinners "^2.9.2" + is-interactive "^2.0.0" + is-unicode-supported "^2.0.0" + log-symbols "^6.0.0" + stdin-discarder "^0.2.2" + string-width "^7.2.0" + strip-ansi "^7.1.0" + pako@^2.0.4: version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== psl@^1.1.33: version "1.15.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + resolved "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz" integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== dependencies: punycode "^2.3.1" punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== querystringify@^2.1.1: version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== redeyed@~2.1.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" + resolved "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz" integrity sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ== dependencies: esprima "~4.0.0" require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== requires-port@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + dependencies: + onetime "^7.0.0" + signal-exit "^4.1.0" + set-cookie-parser@^2.4.8: version "2.7.1" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz" integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + "starknet-types-07@npm:@starknet-io/types-js@^0.7.10": version "0.7.10" - resolved "https://registry.yarnpkg.com/@starknet-io/types-js/-/types-js-0.7.10.tgz#d21dc973d0cd04d7b6293ce461f2f06a5873c760" + resolved "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.7.10.tgz" integrity sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w== starknet@^6.24.1: version "6.24.1" - resolved "https://registry.yarnpkg.com/starknet/-/starknet-6.24.1.tgz#87333339795038e93ef32a20726b5272ddb78fe1" + resolved "https://registry.npmjs.org/starknet/-/starknet-6.24.1.tgz" integrity sha512-g7tiCt73berhcNi41otlN3T3kxZnIvZhMi8WdC21Y6GC6zoQgbI2z1t7JAZF9c4xZiomlanwVnurcpyfEdyMpg== dependencies: "@noble/curves" "1.7.0" @@ -272,25 +468,46 @@ starknet@^6.24.1: starknet-types-07 "npm:@starknet-io/types-js@^0.7.10" ts-mixer "^6.0.3" +stdin-discarder@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz" + integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ== + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + tough-cookie@^4.0.0: version "4.1.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== dependencies: psl "^1.1.33" @@ -300,55 +517,89 @@ tough-cookie@^4.0.0: tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== ts-mixer@^6.0.3: version "6.0.4" - resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.4.tgz#1da39ceabc09d947a82140d9f09db0f84919ca28" + resolved "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz" integrity sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA== +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tslib@2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== +typescript@^5.3.3, typescript@>=2.7: + version "5.8.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + undici-types@~6.19.2: version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== +undici-types@~7.8.0: + version "7.8.0" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz" + integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw== + universalify@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== universalify@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== url-parse@^1.5.3: version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-fetch@^3.4.1: version "3.6.20" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz" integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" @@ -356,7 +607,7 @@ whatwg-url@^5.0.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -365,22 +616,22 @@ wrap-ansi@^7.0.0: ws@8.17.1: version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^17.7.2: version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -390,3 +641,8 @@ yargs@^17.7.2: string-width "^4.2.3" y18n "^5.0.5" yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From 2708cfabe761eb8a466c97d900f940a6d01d9735 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Sun, 24 Aug 2025 20:23:48 +0100 Subject: [PATCH 3/3] chore: cleanups --- contracts/L2/src/core/ZeroXBridgeL2.cairo | 18 ++++++++++++------ contracts/L2/tests/test_ZeroXBridgeL2.cairo | 13 +++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/contracts/L2/src/core/ZeroXBridgeL2.cairo b/contracts/L2/src/core/ZeroXBridgeL2.cairo index 9c287f1..1868d73 100644 --- a/contracts/L2/src/core/ZeroXBridgeL2.cairo +++ b/contracts/L2/src/core/ZeroXBridgeL2.cairo @@ -403,7 +403,7 @@ pub mod ZeroXBridgeL2 { let len = self.last_peaks.len(); for i in 0..len { let peak = self.last_peaks.at(i).read(); - if peak != 0 { // Only include non-zero peaks + if peak != 0 { // Only include non-zero peaks peaks.append(peak); } } @@ -434,14 +434,20 @@ pub mod ZeroXBridgeL2 { let mut leaves_count = self.leaves_count.read(); match MMRTrait::append(ref mmr, commitment_hash, last_peaks.span()) { - Result::Ok((root_hash, peaks)) => { + Result::Ok(( + root_hash, peaks, + )) => { // Store the new root and last position self.node_index_to_root.write(mmr.last_pos, root_hash); - + // Calculate the correct MMR leaf index - let correct_leaf_index = Self::leaf_count_to_mmr_index(leaves_count + 1); + let correct_leaf_index = Self::leaf_count_to_mmr_index(leaves_count) + 1; self.commitment_hash_to_index.write(commitment_hash, correct_leaf_index); - + + println!("Leaf data"); + println!("{:?}", leaves_count); + println!("{:?}", correct_leaf_index); + leaves_count += 1; self.leaves_count.write(leaves_count); @@ -499,4 +505,4 @@ pub mod ZeroXBridgeL2 { mmr_index } } -} \ No newline at end of file +} diff --git a/contracts/L2/tests/test_ZeroXBridgeL2.cairo b/contracts/L2/tests/test_ZeroXBridgeL2.cairo index a1dcd92..ced0689 100644 --- a/contracts/L2/tests/test_ZeroXBridgeL2.cairo +++ b/contracts/L2/tests/test_ZeroXBridgeL2.cairo @@ -3,6 +3,7 @@ use core::integer::u256; use core::poseidon::PoseidonTrait; use l2::core::ZeroXBridgeL2::ZeroXBridgeL2::{BurnData, BurnEvent, Event, MintData, MintEvent}; use l2::interfaces::IL2Oracle::{IL2OracleDispatcher, IL2OracleDispatcherTrait}; +use l2::interfaces::IMerkleManager::{IMerkleManagerDispatcher, IMerkleManagerDispatcherTrait}; use l2::interfaces::IProofRegistry::{IProofRegistryDispatcher, IProofRegistryDispatcherTrait}; use l2::interfaces::IZeroXBridgeL2::{ IDynamicRateDispatcher, IDynamicRateDispatcherTrait, IZeroXBridgeL2Dispatcher, @@ -10,7 +11,6 @@ use l2::interfaces::IZeroXBridgeL2::{ }; use l2::interfaces::IxZBErc20::{IXZBERC20Dispatcher, IXZBERC20DispatcherTrait}; use l2::mocks::MockRegistry::{IMockRegistryDispatcher, IMockRegistryDispatcherTrait}; -use l2::interfaces::IMerkleManager::{IMerkleManagerDispatcher, IMerkleManagerDispatcherTrait}; use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin_utils::serde::SerializedAppend; use snforge_std::{ @@ -519,6 +519,7 @@ fn test_mmr_index_fix_multiple_withdrawals() { let owner_addr = owner(); let burn_amount = 1000_u256 * PRECISION; + let mut spy = spy_events(); // Setup: mint tokens to Alice cheat_caller_address(token_addr, owner_addr, CheatSpan::TargetCalls(1)); IXZBERC20Dispatcher { contract_address: token_addr }.mint(alice_addr, burn_amount * 5); @@ -605,17 +606,17 @@ fn test_mmr_leaf_index_calculation_edge_cases() { } cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1)); IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount); - + let current_leaves = merkle_manager.get_leaves_count(); let expected_leaves: felt252 = i.into(); assert(current_leaves == expected_leaves, 'Incorrect leaf count'); - + // Verify MMR is still valid after each burn let root_hash = merkle_manager.get_root_hash(); assert(root_hash != 0, 'Root hash should not be zero'); - + i += 1; - }; + } // Final verification let final_leaves_count = merkle_manager.get_leaves_count(); @@ -625,4 +626,4 @@ fn test_mmr_leaf_index_calculation_edge_cases() { let final_leaves_count_u256: u256 = final_leaves_count.into(); let final_element_count_u256: u256 = final_element_count.into(); assert(final_element_count_u256 > final_leaves_count_u256, 'Element count > leaf count'); -} \ No newline at end of file +}