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
6 changes: 4 additions & 2 deletions contracts/L2/src/core/ZeroXBridgeL2.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ pub mod ZeroXBridgeL2 {

#[derive(Drop, Debug, starknet::Event)]
pub struct BurnEvent {
pub burn_id: u256,
pub user: ContractAddress,
pub amount: u256,
pub nonce: felt252,
Expand Down Expand Up @@ -241,7 +242,7 @@ pub mod ZeroXBridgeL2 {
}


fn burn_xzb_for_unlock(ref self: ContractState, amount: u256) {
fn burn_xzb_for_unlock(ref self: ContractState, burn_id: u256, amount: u256) {
let caller = get_caller_address();
let token_addr = self.xzb_token.read();
let protocol = get_contract_address();
Expand Down Expand Up @@ -277,6 +278,7 @@ pub mod ZeroXBridgeL2 {
self
.emit(
BurnEvent {
burn_id,
user: caller,
amount: burn_amount_usd,
nonce: current_nonce,
Expand Down Expand Up @@ -479,7 +481,7 @@ pub mod ZeroXBridgeL2 {
// Clear all existing peaks from storage
let current_len = self.last_peaks.len();
for _i in 0..current_len {
self.last_peaks.pop();
let _ = self.last_peaks.pop();
}
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/L2/src/core/xZBERC20.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub mod xZBERC20 {

#[generate_trait]
#[abi(per_item)]
impl TestMint of IxZbTest<ContractState> {
impl TestMint of IxZbTest {
#[external(v0)]
fn test_mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {
self.erc20.mint(recipient, amount);
Expand Down
2 changes: 1 addition & 1 deletion contracts/L2/src/interfaces/IZeroXBridgeL2.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub trait IZeroXBridgeL2<TContractState> {
y_parity: bool,
);

fn burn_xzb_for_unlock(ref self: TContractState, amount: core::integer::u256);
fn burn_xzb_for_unlock(ref self: TContractState, burn_id: u256, amount: core::integer::u256);
}

#[starknet::interface]
Expand Down
48 changes: 35 additions & 13 deletions contracts/L2/tests/test_ZeroXBridgeL2.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ fn test_burn_xzb_for_unlock_happy_path() {

// Burn tokens through bridge with alice as caller.
let mut spy = spy_events();
let burn_id = 123124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);

let burn_amount_usd = (burn_amount * PRECISION) / rate;

Expand All @@ -150,7 +152,11 @@ fn test_burn_xzb_for_unlock_happy_path() {
bridge_addr,
Event::BurnEvent(
BurnEvent {
user: alice_addr, amount: burn_amount_usd, nonce: 0, commitment_hash: expected_hash,
burn_id,
user: alice_addr,
amount: burn_amount_usd,
nonce: 0,
commitment_hash: expected_hash,
},
),
);
Expand Down Expand Up @@ -198,9 +204,11 @@ fn test_burn_xzb_updates_balance() {
cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1));
erc20.approve(bridge_addr, burn_amount);

let burn_id = 123124_u256;
// Burn tokens through bridge with alice as caller
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);

// Check balance after burn.
let final_balance = erc20.balance_of(alice_addr);
Expand Down Expand Up @@ -233,9 +241,11 @@ fn test_burn_xzb_insufficient_balance() {
cheat_caller_address(token_addr, alice_addr, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: token_addr }.approve(bridge_addr, burn_amount);

let burn_id = 123124_u256;
// Attempt to burn 500 tokens when balance is only 300.
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);
}


Expand Down Expand Up @@ -492,9 +502,10 @@ fn test_mmr_index_fix_single_withdrawal() {
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();
let burn_id = 123124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);

// Verify the leaf count and MMR state
let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr };
Expand All @@ -519,7 +530,6 @@ 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);
Expand All @@ -535,32 +545,42 @@ fn test_mmr_index_fix_multiple_withdrawals() {
let merkle_manager = IMerkleManagerDispatcher { contract_address: bridge_addr };

// First burn (leaf 0 -> MMR index 0)
let burn_id = 123124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);
let leaves_count_1 = merkle_manager.get_leaves_count();
assert(leaves_count_1 == 1, 'Expected 1 leaf');

// Second burn (leaf 1 -> MMR index 1)
let burn_id = 1223124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);
let leaves_count_2 = merkle_manager.get_leaves_count();
assert(leaves_count_2 == 2, 'Expected 2 leaves');

// Third burn (leaf 2 -> MMR index 2) - this is where the original bug would manifest
let burn_id = 323124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);
let leaves_count_3 = merkle_manager.get_leaves_count();
assert(leaves_count_3 == 3, 'Expected 3 leaves');

// Fourth burn (leaf 3 -> MMR index 4) - critical test case
let burn_id = 423124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);
let leaves_count_4 = merkle_manager.get_leaves_count();
assert(leaves_count_4 == 4, 'Expected 4 leaves');

// Fifth burn (leaf 4 -> MMR index 5)
let burn_id = 523124_u256;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);
let leaves_count_5 = merkle_manager.get_leaves_count();
assert(leaves_count_5 == 5, 'Expected 5 leaves');

Expand Down Expand Up @@ -604,8 +624,10 @@ fn test_mmr_leaf_index_calculation_edge_cases() {
if i > 8 {
break;
}
let burn_id = 1;
cheat_caller_address(bridge_addr, alice_addr, CheatSpan::TargetCalls(1));
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }.burn_xzb_for_unlock(burn_amount);
IZeroXBridgeL2Dispatcher { contract_address: bridge_addr }
.burn_xzb_for_unlock(burn_id, burn_amount);

let current_leaves = merkle_manager.get_leaves_count();
let expected_leaves: felt252 = i.into();
Expand Down
Loading