fix: Simplify the BebopExecutor and fix Single tests

Make specific quotes that are expected to be used by the TychoRouter for the tests. Do not use the BebopHarness
Commented out Aggregate tests

Took 6 hours 40 minutes
This commit is contained in:
Diana Carvalho
2025-08-12 16:11:42 +01:00
parent ee3d0cc060
commit 76a09d0402
11 changed files with 804 additions and 1722 deletions

View File

@@ -3,14 +3,10 @@ pub mod encoding;
use std::str::FromStr;
use alloy::{
primitives::{B256, U256},
signers::local::PrivateKeySigner,
};
use alloy::{primitives::B256, signers::local::PrivateKeySigner};
use tycho_common::{models::Chain, Bytes};
use tycho_execution::encoding::{
evm::encoder_builders::TychoRouterEncoderBuilder,
models::{BebopOrderType, UserTransferType},
evm::encoder_builders::TychoRouterEncoderBuilder, models::UserTransferType,
tycho_encoder::TychoEncoder,
};
@@ -75,236 +71,9 @@ pub fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> Box<dyn
/// Builds the complete Bebop calldata in the format expected by the encoder
/// Returns: partialFillOffset (1 byte) | bebop_calldata (selector + ABI encoded params)
///
/// # Arguments
/// * `order_type` - The type of Bebop order (Single or Aggregate)
/// * `filled_taker_amount` - Amount to fill (0 means fill entire order)
/// * `quote_data` - The ABI-encoded order data (just the struct, not full calldata)
/// * `signatures` - Vector of (signature_bytes, signature_type) tuples
pub fn build_bebop_calldata(
order_type: BebopOrderType,
filled_taker_amount: U256,
quote_data: &[u8],
signatures: Vec<(Vec<u8>, u8)>,
) -> Bytes {
// 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) - after order and
* signature offset */
),
BebopOrderType::Aggregate => (
[0xa2, 0xf7, 0x48, 0x93], // swapAggregate selector
2u8, /* partialFillOffset (68 = 4 + 2*32) - aggregate still
* uses offsets */
),
};
// 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)
}
};
// 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
pub fn build_bebop_calldata(calldata: &[u8], partial_fill_offset: u8) -> Bytes {
let mut user_data = vec![partial_fill_offset];
user_data.extend_from_slice(&bebop_calldata);
user_data.extend_from_slice(calldata);
Bytes::from(user_data)
}
fn encode_single_params(
order_data: &[u8], // Already ABI-encoded Single struct
signature: &(Vec<u8>, u8),
filled_taker_amount: U256,
) -> Vec<u8> {
// abi.encode() with (struct, struct, uint256) where:
// - Single struct: all fixed-size fields, encoded inline
// - MakerSignature struct: has dynamic bytes field, needs offset
// - uint256: fixed-size, encoded inline
let mut encoded = Vec::new();
// 1. Order struct fields are encoded inline (11 fields * 32 bytes = 352 bytes)
encoded.extend_from_slice(order_data);
// 2. Offset to MakerSignature data (points after all inline data)
// Offset = 352 (order) + 32 (this offset) + 32 (filledTakerAmount) = 416
encoded.extend_from_slice(&U256::from(416).to_be_bytes::<32>());
// 3. filledTakerAmount inline
encoded.extend_from_slice(&filled_taker_amount.to_be_bytes::<32>());
// 4. MakerSignature struct data at the offset
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>, u8)],
filled_taker_amount: U256,
) -> Vec<u8> {
// abi.encode() with (struct, struct[], uint256) where:
// - Aggregate struct: has dynamic arrays, gets offset
// - MakerSignature[] array: dynamic, gets offset
// - uint256: fixed-size, encoded inline
//
// CRITICAL FIX: The issue is that Rust's ABI encoder produces aggregate orders
// that are 32 bytes larger than Solidity's (1536 vs 1504 bytes). We cannot just
// adjust the offset while keeping the wrong-sized data. Instead, we need to
// truncate the extra 32 bytes from the Rust-encoded order data to match Solidity.
let mut encoded = Vec::new();
// Fixed: Using alloy::primitives::Bytes for the commands field produces the correct
// 1504-byte encoding that matches Solidity. No truncation needed anymore.
let corrected_order_data = order_data;
// Calculate offsets
let order_offset = 96; // After 3 words (3 * 32 = 96)
let signatures_offset = order_offset + corrected_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 the corrected order data
encoded.extend_from_slice(corrected_order_data);
// Manually encode the signatures array to exactly match the working test
// Array length
encoded.extend_from_slice(&U256::from(signatures.len()).to_be_bytes::<32>());
// For 2 signatures, we need offsets for each struct
if signatures.len() == 2 {
// First signature starts after the two offset words (64 bytes)
let sig1_offset = 64;
// Calculate size of first signature struct:
// - offset to bytes field: 32
// - flags field: 32
// - length of bytes: 32
// - actual signature bytes + padding
let sig1 = &signatures[0];
let sig1_padding = (32 - (sig1.0.len() % 32)) % 32;
let sig1_size = 64 + 32 + sig1.0.len() + sig1_padding;
// Second signature starts after first
let sig2_offset = sig1_offset + sig1_size;
// Write offsets
encoded.extend_from_slice(&U256::from(sig1_offset).to_be_bytes::<32>());
encoded.extend_from_slice(&U256::from(sig2_offset).to_be_bytes::<32>());
// Encode each MakerSignature struct
for signature in signatures {
// Offset to signatureBytes within this struct (always 64)
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
encoded.extend_from_slice(&U256::from(signature.0.len()).to_be_bytes::<32>());
// SignatureBytes data
encoded.extend_from_slice(&signature.0);
// Padding to 32-byte boundary
let padding = (32 - (signature.0.len() % 32)) % 32;
encoded.extend(vec![0u8; padding]);
}
} else {
// General case for any number of signatures
let signatures_array = encode_maker_signatures_array(signatures);
encoded.extend_from_slice(&signatures_array);
}
encoded
}
fn encode_maker_signature(signature: &(Vec<u8>, u8)) -> Vec<u8> {
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>, u8)]) -> Vec<u8> {
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();
let struct_offsets_size = 32 * signatures.len();
let mut current_offset = struct_offsets_size;
for signature in signatures {
struct_offsets.push(current_offset);
// 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 (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.0.len()).to_be_bytes::<32>());
// SignatureBytes data (padded to 32 byte boundary)
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);
}
// Write struct offsets
for offset in struct_offsets {
encoded.extend_from_slice(&U256::from(offset).to_be_bytes::<32>());
}
// Write struct data
for data in struct_data {
encoded.extend_from_slice(&data);
}
encoded
}