diff --git a/tests/common/mod.rs b/tests/common/mod.rs index b581288..d3e17c6 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -69,90 +69,191 @@ pub fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> Box, u8)>, // (signature, signature_type) + signatures: Vec<(Vec, u8)>, ) -> Bytes { - // ABI encode MakerSignature[] array - // Format: offset_to_array | array_length | [offset_to_struct_i]... | [struct_i_data]... - let mut encoded_maker_sigs = Vec::new(); + // Step 1: Determine selector and partialFillOffset based on order type + let (selector, partial_fill_offset) = match order_type { + BebopOrderType::Single => ( + [0x4d, 0xce, 0xbc, 0xba], // swapSingle selector + 12u8, // partialFillOffset (388 = 4 + 12*32) + ), + BebopOrderType::Aggregate => ( + [0xa2, 0xf7, 0x48, 0x93], // swapAggregate selector + 2u8, // partialFillOffset (68 = 4 + 2*32) + ), + }; - // Calculate total size needed - let array_offset = 32; // offset to array start - let array_length_size = 32; - let struct_offsets_size = 32 * signatures.len(); - let _header_size = array_length_size + struct_offsets_size; + // Step 2: Build the ABI-encoded parameters based on order type + let encoded_params = match order_type { + BebopOrderType::Single => { + // swapSingle(Single order, MakerSignature signature, uint256 filledTakerAmount) + encode_single_params(quote_data, &signatures[0], filled_taker_amount) + } + BebopOrderType::Aggregate => { + // swapAggregate(Aggregate order, MakerSignature[] signatures, uint256 + // filledTakerAmount) + encode_aggregate_params(quote_data, &signatures, filled_taker_amount) + } + }; - // Build each struct's data and calculate offsets + // Step 3: Combine selector and encoded parameters into complete calldata + let mut bebop_calldata = Vec::new(); + bebop_calldata.extend_from_slice(&selector); + bebop_calldata.extend_from_slice(&encoded_params); + + // Step 4: Prepend partialFillOffset to create final user_data + let mut user_data = vec![partial_fill_offset]; + user_data.extend_from_slice(&bebop_calldata); + + Bytes::from(user_data) +} + +fn encode_single_params( + order_data: &[u8], // Already ABI-encoded Single struct + signature: &(Vec, u8), + filled_taker_amount: U256, +) -> Vec { + // For swapSingle, we need to encode three parameters: + // 1. Single struct (dynamic) - offset at position 0 + // 2. MakerSignature struct (dynamic) - offset at position 32 + // 3. uint256 filledTakerAmount (static) - at position 64 + + let mut encoded = Vec::new(); + + // The order struct is already ABI encoded, we just need to wrap it properly + // Calculate offsets (relative to start of params, not selector) + let order_offset = 96; // After 3 words (2 offsets + filledTakerAmount) + let signature_offset = order_offset + order_data.len(); + + // Write the three parameter slots + encoded.extend_from_slice(&U256::from(order_offset).to_be_bytes::<32>()); + encoded.extend_from_slice(&U256::from(signature_offset).to_be_bytes::<32>()); + encoded.extend_from_slice(&filled_taker_amount.to_be_bytes::<32>()); + + // Append order data (already encoded) + encoded.extend_from_slice(order_data); + + // Encode MakerSignature struct + let signature_struct = encode_maker_signature(signature); + encoded.extend_from_slice(&signature_struct); + + encoded +} + +fn encode_aggregate_params( + order_data: &[u8], // Already ABI-encoded Aggregate struct + signatures: &[(Vec, u8)], + filled_taker_amount: U256, +) -> Vec { + // For swapAggregate, we need to encode three parameters: + // 1. Aggregate struct (dynamic) - offset at position 0 + // 2. MakerSignature[] array (dynamic) - offset at position 32 + // 3. uint256 filledTakerAmount (static) - at position 64 + + let mut encoded = Vec::new(); + + // Encode signatures array + let signatures_array = encode_maker_signatures_array(signatures); + + // Calculate offsets + let order_offset = 96; // After 3 words + let signatures_offset = order_offset + order_data.len(); + + // Write the three parameter slots + encoded.extend_from_slice(&U256::from(order_offset).to_be_bytes::<32>()); + encoded.extend_from_slice(&U256::from(signatures_offset).to_be_bytes::<32>()); + encoded.extend_from_slice(&filled_taker_amount.to_be_bytes::<32>()); + + // Append order data + encoded.extend_from_slice(order_data); + + // Append signatures array + encoded.extend_from_slice(&signatures_array); + + encoded +} + +fn encode_maker_signature(signature: &(Vec, u8)) -> Vec { + let mut encoded = Vec::new(); + + // MakerSignature struct has two fields: + // - bytes signatureBytes (dynamic) - offset at position 0 + // - uint256 flags - at position 32 + + // Offset to signatureBytes (always 64 for this struct layout) + encoded.extend_from_slice(&U256::from(64).to_be_bytes::<32>()); + + // Flags (signature type) + encoded.extend_from_slice(&U256::from(signature.1).to_be_bytes::<32>()); + + // SignatureBytes (length + data) + encoded.extend_from_slice(&U256::from(signature.0.len()).to_be_bytes::<32>()); + encoded.extend_from_slice(&signature.0); + + // Pad to 32-byte boundary + let padding = (32 - (signature.0.len() % 32)) % 32; + encoded.extend(vec![0u8; padding]); + + encoded +} + +fn encode_maker_signatures_array(signatures: &[(Vec, u8)]) -> Vec { + let mut encoded = Vec::new(); + + // Array length + encoded.extend_from_slice(&U256::from(signatures.len()).to_be_bytes::<32>()); + + // Calculate offsets for each struct (relative to start of array data) let mut struct_data = Vec::new(); let mut struct_offsets = Vec::new(); - // Offsets are relative to the start of array data, not the absolute position - // Array data starts after array length, so first offset is after all offset values - let mut current_offset = struct_offsets_size; // Just the space for offsets, not including array length + let struct_offsets_size = 32 * signatures.len(); + let mut current_offset = struct_offsets_size; - for (signature, signature_type) in &signatures { + for signature in signatures { struct_offsets.push(current_offset); - // Each struct contains: - // - offset to signatureBytes (32 bytes) - always 0x40 (64) - // - flags (32 bytes) - // - signatureBytes length (32 bytes) - // - signatureBytes data (padded to 32 bytes) + // Build struct data let mut struct_bytes = Vec::new(); // Offset to signatureBytes within this struct struct_bytes.extend_from_slice(&U256::from(64).to_be_bytes::<32>()); - // Flags (contains signature type) - AFTER the offset, not before! - let flags = U256::from(*signature_type); - struct_bytes.extend_from_slice(&flags.to_be_bytes::<32>()); + // Flags (signature type) + struct_bytes.extend_from_slice(&U256::from(signature.1).to_be_bytes::<32>()); // SignatureBytes length - struct_bytes.extend_from_slice(&U256::from(signature.len()).to_be_bytes::<32>()); + struct_bytes.extend_from_slice(&U256::from(signature.0.len()).to_be_bytes::<32>()); // SignatureBytes data (padded to 32 byte boundary) - struct_bytes.extend_from_slice(signature); - let padding = (32 - (signature.len() % 32)) % 32; - struct_bytes.extend_from_slice(&vec![0u8; padding]); + struct_bytes.extend_from_slice(&signature.0); + let padding = (32 - (signature.0.len() % 32)) % 32; + struct_bytes.extend(vec![0u8; padding]); current_offset += struct_bytes.len(); struct_data.push(struct_bytes); } - // Build the complete ABI encoded array - // Offset to array (always 0x20 for a single parameter) - encoded_maker_sigs.extend_from_slice(&U256::from(array_offset).to_be_bytes::<32>()); - - // Array length - encoded_maker_sigs.extend_from_slice(&U256::from(signatures.len()).to_be_bytes::<32>()); - - // Struct offsets (relative to start of array data) + // Write struct offsets for offset in struct_offsets { - encoded_maker_sigs.extend_from_slice(&U256::from(offset).to_be_bytes::<32>()); + encoded.extend_from_slice(&U256::from(offset).to_be_bytes::<32>()); } - // Struct data + // Write struct data for data in struct_data { - encoded_maker_sigs.extend_from_slice(&data); + encoded.extend_from_slice(&data); } - // Build complete user_data - let mut user_data = Vec::new(); - user_data.push(order_type as u8); - user_data.extend_from_slice(&filled_taker_amount.to_be_bytes::<32>()); - user_data.extend_from_slice(&(quote_data.len() as u32).to_be_bytes()); - user_data.extend_from_slice(quote_data); - user_data.extend_from_slice(&encoded_maker_sigs); - Bytes::from(user_data) + encoded } diff --git a/tests/optimized_transfers_integration_tests.rs b/tests/optimized_transfers_integration_tests.rs index e2a43c9..17e2d45 100644 --- a/tests/optimized_transfers_integration_tests.rs +++ b/tests/optimized_transfers_integration_tests.rs @@ -13,7 +13,7 @@ use tycho_execution::encoding::{ }; use crate::common::{ - build_bebop_user_data, encoding::encode_tycho_router_call, eth, eth_chain, get_signer, + build_bebop_calldata, encoding::encode_tycho_router_call, eth, eth_chain, get_signer, get_tycho_router_encoder, ondo, usdc, weth, }; @@ -656,7 +656,7 @@ fn test_uniswap_v3_bebop() { let signature = hex::decode("eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c").unwrap(); // Build user_data with the quote and signature - let user_data = build_bebop_user_data( + let user_data = build_bebop_calldata( BebopOrderType::Single, U256::from(0), // 0 means fill entire order "e_data, diff --git a/tests/protocol_integration_tests.rs b/tests/protocol_integration_tests.rs index 078469a..41ec518 100644 --- a/tests/protocol_integration_tests.rs +++ b/tests/protocol_integration_tests.rs @@ -15,7 +15,7 @@ use tycho_execution::encoding::{ }; use crate::common::{ - build_bebop_user_data, encoding::encode_tycho_router_call, eth, eth_chain, get_signer, + build_bebop_calldata, encoding::encode_tycho_router_call, eth, eth_chain, get_signer, get_tycho_router_encoder, ondo, pepe, usdc, weth, }; @@ -623,8 +623,8 @@ fn test_single_encoding_strategy_bebop() { // Real signature from mainnet let signature = hex::decode("eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c").unwrap(); - // Build user_data with the quote and signature - let user_data = build_bebop_user_data( + // Build user_data with the quote and signature using new calldata format + let user_data = build_bebop_calldata( BebopOrderType::Single, U256::ZERO, // 0 means fill entire order "e_data, @@ -751,7 +751,7 @@ fn test_single_encoding_strategy_bebop_aggregate() { (sig2, 0u8), // ETH_SIGN for maker 2 ]; - let user_data = build_bebop_user_data( + let user_data = build_bebop_calldata( BebopOrderType::Aggregate, U256::from(0), // 0 means fill entire aggregate order "e_data,