fix(Bebop): Fix encoding and tests
Misc: Fix revert condition in UniswapXFiller Took 1 hour 20 minutes
This commit is contained in:
@@ -24,9 +24,6 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
|
|
||||||
/// @notice Bebop-specific errors
|
/// @notice Bebop-specific errors
|
||||||
error BebopExecutor__InvalidDataLength();
|
error BebopExecutor__InvalidDataLength();
|
||||||
error BebopExecutor__InvalidInput();
|
|
||||||
error BebopExecutor__InvalidSignatureLength();
|
|
||||||
error BebopExecutor__InvalidSignatureType();
|
|
||||||
error BebopExecutor__ZeroAddress();
|
error BebopExecutor__ZeroAddress();
|
||||||
|
|
||||||
/// @notice The Bebop settlement contract address
|
/// @notice The Bebop settlement contract address
|
||||||
@@ -78,17 +75,14 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
|
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the receiver's balance before the swap
|
|
||||||
uint256 balanceBefore = _balanceOf(tokenOut, receiver);
|
uint256 balanceBefore = _balanceOf(tokenOut, receiver);
|
||||||
|
|
||||||
// Execute the swap with the forwarded calldata
|
|
||||||
uint256 ethValue = tokenIn == address(0) ? givenAmount : 0;
|
uint256 ethValue = tokenIn == address(0) ? givenAmount : 0;
|
||||||
|
|
||||||
// Use OpenZeppelin's Address library for safe call with value
|
// Use OpenZeppelin's Address library for safe call with value
|
||||||
// This will revert if the call fails
|
// This will revert if the call fails
|
||||||
|
// slither-disable-next-line unused-return
|
||||||
bebopSettlement.functionCallWithValue(finalCalldata, ethValue);
|
bebopSettlement.functionCallWithValue(finalCalldata, ethValue);
|
||||||
|
|
||||||
// Calculate actual amount received by the receiver
|
|
||||||
uint256 balanceAfter = _balanceOf(tokenOut, receiver);
|
uint256 balanceAfter = _balanceOf(tokenOut, receiver);
|
||||||
calculatedAmount = balanceAfter - balanceBefore;
|
calculatedAmount = balanceAfter - balanceBefore;
|
||||||
}
|
}
|
||||||
@@ -149,6 +143,7 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use assembly to modify the filledTakerAmount at the correct position
|
// Use assembly to modify the filledTakerAmount at the correct position
|
||||||
|
// slither-disable-next-line assembly
|
||||||
assembly {
|
assembly {
|
||||||
// Get pointer to the data portion of the bytes array
|
// Get pointer to the data portion of the bytes array
|
||||||
let dataPtr := add(bebopCalldata, 0x20)
|
let dataPtr := add(bebopCalldata, 0x20)
|
||||||
|
|||||||
@@ -60,10 +60,9 @@ contract UniswapXFiller is AccessControl, IReactorCallback {
|
|||||||
ResolvedOrder[] calldata resolvedOrders,
|
ResolvedOrder[] calldata resolvedOrders,
|
||||||
bytes calldata callbackData
|
bytes calldata callbackData
|
||||||
) external onlyRole(REACTOR_ROLE) {
|
) external onlyRole(REACTOR_ROLE) {
|
||||||
require(
|
if (resolvedOrders.length != 1) {
|
||||||
resolvedOrders.length == 1,
|
revert UniswapXFiller__BatchExecutionNotSupported();
|
||||||
UniswapXFiller__BatchExecutionNotSupported()
|
}
|
||||||
);
|
|
||||||
|
|
||||||
ResolvedOrder memory order = resolvedOrders[0];
|
ResolvedOrder memory order = resolvedOrders[0];
|
||||||
|
|
||||||
|
|||||||
@@ -27,26 +27,4 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
|||||||
assertTrue(success, "Call Failed");
|
assertTrue(success, "Call Failed");
|
||||||
assertEq(balanceAfter - balanceBefore, 732214216964381330);
|
assertEq(balanceAfter - balanceBefore, 732214216964381330);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSingleUSV4IntegrationInputETH() public {
|
|
||||||
// Test created with calldata from our router encoder.
|
|
||||||
|
|
||||||
// Performs a single swap from ETH to PEPE without wrapping or unwrapping
|
|
||||||
//
|
|
||||||
// ETH ───(USV4)──> PEPE
|
|
||||||
//
|
|
||||||
deal(ALICE, 1 ether);
|
|
||||||
uint256 balanceBefore = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
|
||||||
|
|
||||||
bytes memory callData =
|
|
||||||
loadCallDataFromFile("test_single_encoding_strategy_usv4_eth_in");
|
|
||||||
(bool success,) = tychoRouterAddr.call{value: 1 ether}(callData);
|
|
||||||
|
|
||||||
vm.stopPrank();
|
|
||||||
|
|
||||||
uint256 balanceAfter = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
|
||||||
|
|
||||||
assertTrue(success, "Call Failed");
|
|
||||||
assertEq(balanceAfter - balanceBefore, 235610487387677804636755778);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -513,48 +513,4 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
|
|||||||
assertTrue(success, "Call Failed");
|
assertTrue(success, "Call Failed");
|
||||||
assertEq(balanceAfter - balanceBefore, 1404194006633772805);
|
assertEq(balanceAfter - balanceBefore, 1404194006633772805);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function testUSV3BebopIntegration() public {
|
|
||||||
// // Performs a sequential swap from WETH to ONDO through USDC using USV3 and Bebop RFQ
|
|
||||||
// //
|
|
||||||
// // WETH ──(USV3)──> USDC ───(Bebop RFQ)──> ONDO
|
|
||||||
//
|
|
||||||
// // The Bebop order expects:
|
|
||||||
// // - 200 USDC input -> 237.21 ONDO output
|
|
||||||
// // - Receiver: 0xc5564C13A157E6240659fb81882A28091add8670
|
|
||||||
// // - Maker: 0xCe79b081c0c924cb67848723ed3057234d10FC6b
|
|
||||||
//
|
|
||||||
// // Now using 0.099 WETH to get approximately 200 USDC from UniswapV3
|
|
||||||
// uint256 wethAmount = 99000000000000000; // 0.099 WETH
|
|
||||||
// address orderTaker = 0xc5564C13A157E6240659fb81882A28091add8670; // Must match Bebop order taker
|
|
||||||
// deal(WETH_ADDR, orderTaker, wethAmount);
|
|
||||||
// uint256 balanceBefore = IERC20(ONDO_ADDR).balanceOf(orderTaker);
|
|
||||||
//
|
|
||||||
// // Fund the Bebop maker with ONDO and approve settlement
|
|
||||||
// uint256 ondoAmount = 237212396774431060000; // From the real order
|
|
||||||
// deal(ONDO_ADDR, 0xCe79b081c0c924cb67848723ed3057234d10FC6b, ondoAmount);
|
|
||||||
// vm.prank(0xCe79b081c0c924cb67848723ed3057234d10FC6b);
|
|
||||||
// IERC20(ONDO_ADDR).approve(BEBOP_SETTLEMENT, ondoAmount);
|
|
||||||
//
|
|
||||||
// // Approve router from the order taker
|
|
||||||
// vm.startPrank(orderTaker);
|
|
||||||
// IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
|
||||||
// bytes memory callData = loadCallDataFromFile("test_uniswap_v3_bebop");
|
|
||||||
// (bool success,) = tychoRouterAddr.call(callData);
|
|
||||||
//
|
|
||||||
// vm.stopPrank();
|
|
||||||
//
|
|
||||||
// uint256 balanceAfter = IERC20(ONDO_ADDR).balanceOf(orderTaker);
|
|
||||||
//
|
|
||||||
// assertTrue(success, "Call Failed");
|
|
||||||
// assertEq(balanceAfter - balanceBefore, ondoAmount);
|
|
||||||
// assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
|
||||||
//
|
|
||||||
// // With 0.099 WETH input, UniswapV3 produces ~200.15 USDC
|
|
||||||
// // Bebop order consumes exactly 200 USDC, leaving only dust amount
|
|
||||||
// uint256 expectedLeftoverUsdc = 153845; // ~0.153845 USDC dust
|
|
||||||
// assertEq(
|
|
||||||
// IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), expectedLeftoverUsdc
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,28 +192,6 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeBebopSwap(
|
|
||||||
address tokenIn,
|
|
||||||
address tokenOut,
|
|
||||||
RestrictTransferFrom.TransferType transferType,
|
|
||||||
uint8 partialFillOffset,
|
|
||||||
uint256 originalAmountIn,
|
|
||||||
bool approvalNeeded,
|
|
||||||
address receiver,
|
|
||||||
bytes memory bebopCalldata
|
|
||||||
) internal pure returns (bytes memory) {
|
|
||||||
return abi.encodePacked(
|
|
||||||
tokenIn,
|
|
||||||
tokenOut,
|
|
||||||
uint8(transferType),
|
|
||||||
partialFillOffset,
|
|
||||||
originalAmountIn,
|
|
||||||
approvalNeeded ? uint8(1) : uint8(0),
|
|
||||||
receiver,
|
|
||||||
bebopCalldata
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeUniswapV2Swap(
|
function encodeUniswapV2Swap(
|
||||||
address tokenIn,
|
address tokenIn,
|
||||||
address target,
|
address target,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -50,34 +50,27 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeData() public {
|
function testDecodeData() public {
|
||||||
// Fork to ensure consistent setup
|
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), 22667985);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), 22667985);
|
||||||
|
|
||||||
// Deploy Bebop executor harness
|
|
||||||
bebopExecutor =
|
bebopExecutor =
|
||||||
new BebopExecutorExposed(BEBOP_SETTLEMENT, PERMIT2_ADDRESS);
|
new BebopExecutorExposed(BEBOP_SETTLEMENT, PERMIT2_ADDRESS);
|
||||||
|
|
||||||
// Create a simple bebop calldata
|
|
||||||
bytes memory bebopCalldata = abi.encodePacked(
|
bytes memory bebopCalldata = abi.encodePacked(
|
||||||
bytes4(0x4dcebcba), // swapSingle selector
|
bytes4(0x4dcebcba), // swapSingle selector
|
||||||
hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000068470140"
|
hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000068470140"
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 originalAmountIn = 200000000; // 200 USDC
|
uint256 originalAmountIn = 200000000; // 200 USDC
|
||||||
|
|
||||||
// Create the executor params
|
|
||||||
bytes memory params = abi.encodePacked(
|
bytes memory params = abi.encodePacked(
|
||||||
USDC_ADDR,
|
USDC_ADDR,
|
||||||
ONDO_ADDR,
|
ONDO_ADDR,
|
||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint8(2), // partialFillOffset for swapSingle (68 = 4 + 2*32)
|
uint8(2),
|
||||||
originalAmountIn,
|
originalAmountIn,
|
||||||
true,
|
true,
|
||||||
address(123),
|
address(123),
|
||||||
bebopCalldata
|
bebopCalldata
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test decoding
|
|
||||||
(
|
(
|
||||||
address tokenIn,
|
address tokenIn,
|
||||||
address tokenOut,
|
address tokenOut,
|
||||||
@@ -325,10 +318,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testInvalidDataLength() public {
|
function testInvalidDataLength() public {
|
||||||
// Fork to ensure consistent setup
|
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), 22667985);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), 22667985);
|
||||||
|
|
||||||
// Deploy Bebop executor with real settlement contract
|
|
||||||
bebopExecutor =
|
bebopExecutor =
|
||||||
new BebopExecutorExposed(BEBOP_SETTLEMENT, PERMIT2_ADDRESS);
|
new BebopExecutorExposed(BEBOP_SETTLEMENT, PERMIT2_ADDRESS);
|
||||||
|
|
||||||
@@ -342,9 +332,9 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
WETH_ADDR,
|
WETH_ADDR,
|
||||||
USDC_ADDR,
|
USDC_ADDR,
|
||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint8(2), // partialFillOffset for swapSingle (68 = 4 + 2*32)
|
uint8(2),
|
||||||
originalAmountIn,
|
originalAmountIn,
|
||||||
uint8(1), // approvalNeeded: true
|
true,
|
||||||
address(bebopExecutor),
|
address(bebopExecutor),
|
||||||
bebopCalldata
|
bebopCalldata
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ pub static IN_TRANSFER_REQUIRED_PROTOCOLS: LazyLock<HashSet<&'static str>> = Laz
|
|||||||
set.insert("ekubo_v2");
|
set.insert("ekubo_v2");
|
||||||
set.insert("vm:maverick_v2");
|
set.insert("vm:maverick_v2");
|
||||||
set.insert("vm:balancer_v3");
|
set.insert("vm:balancer_v3");
|
||||||
set.insert("rfq:bebop");
|
|
||||||
set
|
set
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,6 +48,5 @@ pub static CALLBACK_CONSTRAINED_PROTOCOLS: LazyLock<HashSet<&'static str>> = Laz
|
|||||||
set.insert("uniswap_v4");
|
set.insert("uniswap_v4");
|
||||||
set.insert("ekubo_v2");
|
set.insert("ekubo_v2");
|
||||||
set.insert("vm:balancer_v3");
|
set.insert("vm:balancer_v3");
|
||||||
set.insert("rfq:bebop");
|
|
||||||
set
|
set
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -658,144 +658,6 @@ pub struct BebopSwapEncoder {
|
|||||||
settlement_address: String,
|
settlement_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the total taker amount from a Bebop aggregate order calldata
|
|
||||||
/// This is required because BebopExecutor needs a non-zero filledTakerAmount
|
|
||||||
fn extract_aggregate_taker_amount(bebop_calldata: &[u8]) -> Option<U256> {
|
|
||||||
// Minimum size check: 4 (selector) + 32 (order offset) + 32 (signatures offset) + 32
|
|
||||||
// (filledTakerAmount) = 100 bytes
|
|
||||||
if bebop_calldata.len() < 100 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPECIAL CASE: For the specific test case with 2116 bytes starting with swapAggregate selector
|
|
||||||
// Return the known expected total since the ABI structure analysis shows the generated
|
|
||||||
// calldata doesn't match the mainnet structure we analyzed
|
|
||||||
if bebop_calldata.len() == 2116 && bebop_calldata.starts_with(&[0xa2, 0xf7, 0x48, 0x93]) {
|
|
||||||
let expected_total = U256::from_str_radix("9850000000000000", 10).unwrap(); // 0.00985 ETH in wei
|
|
||||||
return Some(expected_total);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For other cases, implement proper ABI structure parsing
|
|
||||||
|
|
||||||
// Read the offset to the order struct (first parameter)
|
|
||||||
// The order offset is at bytes 4-36 (after selector)
|
|
||||||
let order_offset_value = U256::from_be_slice(&bebop_calldata[4..36]);
|
|
||||||
// Add 4 to account for the selector when calculating absolute position
|
|
||||||
let order_offset = order_offset_value.to::<usize>() + 4;
|
|
||||||
|
|
||||||
// The Aggregate struct has 11 fields:
|
|
||||||
// 0: expiry (U256) - at order_offset + 0
|
|
||||||
// 1: taker_address (address) - at order_offset + 32
|
|
||||||
// 2: maker_addresses (address[]) - offset at order_offset + 64
|
|
||||||
// 3: maker_nonces (uint256[]) - offset at order_offset + 96
|
|
||||||
// 4: taker_tokens (address[][]) - offset at order_offset + 128
|
|
||||||
// 5: maker_tokens (address[][]) - offset at order_offset + 160
|
|
||||||
// 6: taker_amounts (uint256[][]) - offset at order_offset + 192 <- we need this
|
|
||||||
// 7: maker_amounts (uint256[][]) - offset at order_offset + 224
|
|
||||||
// 8: receiver (address) - at order_offset + 256
|
|
||||||
// 9: commands (bytes) - offset at order_offset + 288
|
|
||||||
// 10: flags (uint256) - at order_offset + 320
|
|
||||||
|
|
||||||
// Make sure we can read the taker_amounts offset
|
|
||||||
if bebop_calldata.len() <= order_offset + 224 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the offset to taker_amounts (in ABI encoding, this is relative to start of parameter
|
|
||||||
// area)
|
|
||||||
let taker_amounts_offset_u256 =
|
|
||||||
U256::from_be_slice(&bebop_calldata[order_offset + 192..order_offset + 224]);
|
|
||||||
|
|
||||||
// Check for reasonable offset value to avoid overflow
|
|
||||||
if taker_amounts_offset_u256 > U256::from(bebop_calldata.len()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TEMPORARY FIX: Hardcode the correct position until we understand the offset calculation
|
|
||||||
// The correct taker_amounts array is at position 1157 in our test data
|
|
||||||
let taker_amounts_data_offset = 1157; // TODO: Fix offset calculation
|
|
||||||
|
|
||||||
// Make sure we can read the array length
|
|
||||||
if bebop_calldata.len() <= taker_amounts_data_offset + 32 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the number of makers (outer array length)
|
|
||||||
let raw_bytes = &bebop_calldata[taker_amounts_data_offset..taker_amounts_data_offset + 32];
|
|
||||||
let num_makers = U256::from_be_slice(raw_bytes);
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if num_makers == U256::ZERO || num_makers > U256::from(100) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_makers = num_makers.to::<usize>();
|
|
||||||
|
|
||||||
// Now we need to read the 2D array structure
|
|
||||||
// After the array length, we have num_makers offsets (each 32 bytes)
|
|
||||||
// Each offset points to that maker's taker_amounts array
|
|
||||||
|
|
||||||
let mut total = U256::ZERO;
|
|
||||||
|
|
||||||
for maker_idx in 0..num_makers {
|
|
||||||
// Read the offset to this maker's taker_amounts array
|
|
||||||
let offset_position = taker_amounts_data_offset + 32 + (maker_idx * 32);
|
|
||||||
|
|
||||||
if bebop_calldata.len() <= offset_position + 32 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This offset is relative to the start of the taker_amounts array data
|
|
||||||
let maker_array_offset_u256 =
|
|
||||||
U256::from_be_slice(&bebop_calldata[offset_position..offset_position + 32]);
|
|
||||||
|
|
||||||
// Check for reasonable offset to avoid overflow
|
|
||||||
if maker_array_offset_u256 > U256::from(bebop_calldata.len()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TEMPORARY FIX: Hardcode correct sub-array positions
|
|
||||||
// Based on search, amounts are at 1285 and 1349, preceded by length=1
|
|
||||||
// So sub-arrays start at 1285-32=1253 and 1349-32=1317
|
|
||||||
let maker_array_position = if maker_idx == 0 { 1253 } else { 1317 };
|
|
||||||
|
|
||||||
// Read the length of this maker's taker_amounts array
|
|
||||||
if bebop_calldata.len() <= maker_array_position + 32 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_amounts_u256 =
|
|
||||||
U256::from_be_slice(&bebop_calldata[maker_array_position..maker_array_position + 32]);
|
|
||||||
|
|
||||||
// Sanity check - must be reasonable value to avoid overflow
|
|
||||||
if num_amounts_u256 > U256::from(100) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_amounts = num_amounts_u256.to::<usize>();
|
|
||||||
|
|
||||||
// Sum all amounts for this maker
|
|
||||||
for amount_idx in 0..num_amounts {
|
|
||||||
let amount_position = maker_array_position + 32 + (amount_idx * 32);
|
|
||||||
|
|
||||||
if bebop_calldata.len() <= amount_position + 32 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let amount =
|
|
||||||
U256::from_be_slice(&bebop_calldata[amount_position..amount_position + 32]);
|
|
||||||
|
|
||||||
total = total.saturating_add(amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if total > U256::ZERO {
|
|
||||||
Some(total)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SwapEncoder for BebopSwapEncoder {
|
impl SwapEncoder for BebopSwapEncoder {
|
||||||
fn new(
|
fn new(
|
||||||
executor_address: String,
|
executor_address: String,
|
||||||
@@ -845,119 +707,24 @@ impl SwapEncoder for BebopSwapEncoder {
|
|||||||
approval_needed = true;
|
approval_needed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract bebop calldata from user_data (required for Bebop)
|
// The user data required for Bebop is
|
||||||
|
// partial_fill_offset (u8) | original_taker_amount (U256) | calldata (bytes (selector ABI
|
||||||
|
// encoded params))
|
||||||
let user_data = swap.user_data.clone().ok_or_else(|| {
|
let user_data = swap.user_data.clone().ok_or_else(|| {
|
||||||
EncodingError::InvalidInput("Bebop swaps require user_data with calldata".to_string())
|
EncodingError::InvalidInput("Bebop swaps require user_data with calldata".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// User data format: partialFillOffset (1 byte) + bebop_calldata
|
if user_data.len() < 37 {
|
||||||
if user_data.len() < 5 {
|
|
||||||
return Err(EncodingError::InvalidInput(
|
return Err(EncodingError::InvalidInput(
|
||||||
"User data too short to contain offset and Bebop calldata".to_string(),
|
"User data too short to contain offset and Bebop calldata".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the partialFillOffset from the first byte
|
|
||||||
let partial_fill_offset = user_data[0];
|
let partial_fill_offset = user_data[0];
|
||||||
|
let original_filled_taker_amount = U256::from_be_slice(&user_data[1..33]);
|
||||||
|
|
||||||
// The calldata should be for either swapSingle or swapAggregate
|
// The calldata should be for either swapSingle or swapAggregate
|
||||||
let bebop_calldata = user_data[1..].to_vec();
|
let bebop_calldata = user_data[33..].to_vec();
|
||||||
|
|
||||||
// Extract the original filledTakerAmount from the calldata using partialFillOffset
|
|
||||||
// The actual byte position is 4 (selector) + partialFillOffset * 32
|
|
||||||
let filled_taker_amount_pos = 4 + (partial_fill_offset as usize) * 32;
|
|
||||||
|
|
||||||
// Ensure the calldata is long enough to contain filledTakerAmount at the calculated
|
|
||||||
// position
|
|
||||||
if bebop_calldata.len() < filled_taker_amount_pos + 32 {
|
|
||||||
return Err(EncodingError::InvalidInput(format!(
|
|
||||||
"Bebop calldata too short to contain filledTakerAmount at offset {partial_fill_offset}",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the original filledTakerAmount from the order and use the context receiver
|
|
||||||
let original_filled_taker_amount = {
|
|
||||||
let filled_taker_amount = U256::from_be_slice(
|
|
||||||
&bebop_calldata[filled_taker_amount_pos..filled_taker_amount_pos + 32],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extract taker_amount from the order based on the function selector
|
|
||||||
let selector = &bebop_calldata[0..4];
|
|
||||||
|
|
||||||
// swapSingle selector: 0x4dcebcba
|
|
||||||
// swapAggregate selector: 0xa2f74893
|
|
||||||
const SWAP_SINGLE_SELECTOR: [u8; 4] = [0x4d, 0xce, 0xbc, 0xba];
|
|
||||||
const SWAP_AGGREGATE_SELECTOR: [u8; 4] = [0xa2, 0xf7, 0x48, 0x93];
|
|
||||||
|
|
||||||
if selector == SWAP_SINGLE_SELECTOR {
|
|
||||||
// For swapSingle, only care about taker_amount; receiver comes from context
|
|
||||||
// The bebop_calldata can come in different formats:
|
|
||||||
// 1. Selector + inline params (from integration test via build_bebop_calldata):
|
|
||||||
// - Bytes 0-4: selector (0x4dcebcba)
|
|
||||||
// - Bytes 4-356: order struct inline (352 bytes)
|
|
||||||
// - Bytes 356-388: signature offset (32 bytes)
|
|
||||||
// - Bytes 388-420: filledTakerAmount (32 bytes)
|
|
||||||
// - Bytes 420+: signature data
|
|
||||||
// - taker_amount is at bytes 196-228 (4 + 192)
|
|
||||||
//
|
|
||||||
// 2. Selector + offsets + data (from unit test):
|
|
||||||
// - Bytes 0-4: selector
|
|
||||||
// - Bytes 4-36: order offset (value = 96)
|
|
||||||
// - Bytes 36-68: signature offset
|
|
||||||
// - Bytes 68-100: filledTakerAmount
|
|
||||||
// - Bytes 100+: order data
|
|
||||||
// - taker_amount is at bytes 292-324 (100 + 192)
|
|
||||||
|
|
||||||
if filled_taker_amount != U256::ZERO {
|
|
||||||
filled_taker_amount
|
|
||||||
} else {
|
|
||||||
// Check if we have a selector (starts with 0x4dcebcba)
|
|
||||||
if bebop_calldata.len() >= 4 && bebop_calldata[0..4] == [0x4d, 0xce, 0xbc, 0xba]
|
|
||||||
{
|
|
||||||
// We have a selector, need to determine which format
|
|
||||||
// Check if bytes 4-36 look like an offset (should be 0x60 = 96 for offset
|
|
||||||
// format)
|
|
||||||
if bebop_calldata.len() >= 36 {
|
|
||||||
let potential_offset = U256::from_be_slice(&bebop_calldata[4..36]);
|
|
||||||
if potential_offset == U256::from(96) {
|
|
||||||
// Format with offsets - taker_amount is at 292-324
|
|
||||||
if bebop_calldata.len() >= 324 {
|
|
||||||
U256::from_be_slice(&bebop_calldata[292..324])
|
|
||||||
} else {
|
|
||||||
U256::ZERO
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Inline format with selector - taker_amount is at 196-228
|
|
||||||
if bebop_calldata.len() >= 228 {
|
|
||||||
U256::from_be_slice(&bebop_calldata[196..228])
|
|
||||||
} else {
|
|
||||||
U256::ZERO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
U256::ZERO
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No selector, pure inline format - taker_amount is at 192-224
|
|
||||||
if bebop_calldata.len() >= 224 {
|
|
||||||
U256::from_be_slice(&bebop_calldata[192..224])
|
|
||||||
} else {
|
|
||||||
U256::ZERO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if selector == SWAP_AGGREGATE_SELECTOR {
|
|
||||||
// For swapAggregate, compute taker_amount from calldata if needed; receiver from
|
|
||||||
// context
|
|
||||||
if filled_taker_amount != U256::ZERO {
|
|
||||||
filled_taker_amount
|
|
||||||
} else {
|
|
||||||
extract_aggregate_taker_amount(&bebop_calldata).unwrap_or(U256::ZERO)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
U256::ZERO
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let receiver = bytes_to_address(&encoding_context.receiver)?;
|
let receiver = bytes_to_address(&encoding_context.receiver)?;
|
||||||
|
|
||||||
@@ -2048,91 +1815,15 @@ mod tests {
|
|||||||
|
|
||||||
mod bebop {
|
mod bebop {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::encoding::{evm::utils::write_calldata_to_file, models::TransferType};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encode_bebop_single() {
|
fn test_encode_bebop_single() {
|
||||||
use alloy::{hex, primitives::Address, sol_types::SolValue};
|
// 200 USDC -> ONDO
|
||||||
|
let bebop_calldata= Bytes::from_str("0x4dcebcba00000000000000000000000000000000000000000000000000000000689b548f0000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000067336cec42645f55059eff241cb02ea5cc52ff86000000000000000000000000000000000000000000000000279ead5d9685f25b000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be3000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000a8aea46aa4ec5c0f5000000000000000000000000d2068e04cf586f76eece7ba5beb779d7bb1474a100000000000000000000000000000000000000000000000000000000000000005230bcb979c81cebf94a3b5c08bcfa300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414ce40058ff07f11d9224c2c8d1e58369e4a90173856202d8d2a17da48058ad683dedb742eda0d4c0cf04cf1c09138898dd7fd06f97268ea7f74ef9b42d29bf4c1b00000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
// Transaction: https://etherscan.io/tx/0x6279bc970273b6e526e86d9b69133c2ca1277e697ba25375f5e6fc4df50c0c94
|
let original_taker_amount = U256::from_str("200000000").unwrap();
|
||||||
let expiry = 1749483840u64;
|
// partialFillOffset 12 for swapSingle
|
||||||
let taker_address = Address::from_slice(
|
let mut user_data = vec![12u8];
|
||||||
&hex::decode("c5564C13A157E6240659fb81882A28091add8670").unwrap(),
|
user_data.extend_from_slice(&original_taker_amount.to_be_bytes::<32>());
|
||||||
);
|
|
||||||
let maker_address = Address::from_slice(
|
|
||||||
&hex::decode("Ce79b081c0c924cb67848723ed3057234d10FC6b").unwrap(),
|
|
||||||
);
|
|
||||||
let maker_nonce = 1749483765992417u64;
|
|
||||||
let taker_token = Address::from_slice(
|
|
||||||
&hex::decode("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap(),
|
|
||||||
); // USDC
|
|
||||||
let maker_token = Address::from_slice(
|
|
||||||
&hex::decode("fAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3").unwrap(),
|
|
||||||
); // ONDO
|
|
||||||
let taker_amount = U256::from(200000000u64); // 200 USDC
|
|
||||||
let maker_amount = U256::from_str_radix("cd97e88ccc64d54000", 16).unwrap(); // 237.21 ONDO
|
|
||||||
let receiver = taker_address;
|
|
||||||
let packed_commands = U256::ZERO;
|
|
||||||
let flags = U256::from_str_radix(
|
|
||||||
"727220e0ad42bc02077c9bb3a3d60c41bfd3df1a80f5e97aa87e3ea6e93a0000",
|
|
||||||
16,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Encode the order struct using ABI encoding
|
|
||||||
let quote_data = (
|
|
||||||
expiry,
|
|
||||||
taker_address,
|
|
||||||
maker_address,
|
|
||||||
maker_nonce,
|
|
||||||
taker_token,
|
|
||||||
maker_token,
|
|
||||||
taker_amount,
|
|
||||||
maker_amount,
|
|
||||||
receiver,
|
|
||||||
packed_commands,
|
|
||||||
flags,
|
|
||||||
)
|
|
||||||
.abi_encode();
|
|
||||||
|
|
||||||
// Real signature from mainnet
|
|
||||||
let signature = hex::decode("eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c").unwrap();
|
|
||||||
|
|
||||||
// Build the complete swapSingle calldata
|
|
||||||
let mut bebop_calldata = Vec::new();
|
|
||||||
|
|
||||||
// swapSingle selector
|
|
||||||
bebop_calldata.extend_from_slice(&[0x4d, 0xce, 0xbc, 0xba]);
|
|
||||||
|
|
||||||
// Encode parameters: (Single order, MakerSignature signature, uint256
|
|
||||||
// filledTakerAmount) Calculate offsets (relative to start of params, not
|
|
||||||
// selector)
|
|
||||||
let order_offset = U256::from(96); // After 3 words (2 offsets + filledTakerAmount)
|
|
||||||
let signature_offset = U256::from(96 + quote_data.len());
|
|
||||||
|
|
||||||
// Write the three parameter slots
|
|
||||||
bebop_calldata.extend_from_slice(&order_offset.to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&signature_offset.to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&U256::ZERO.to_be_bytes::<32>()); // filledTakerAmount = 0 for no pre-fill
|
|
||||||
|
|
||||||
// Append order data (already encoded)
|
|
||||||
bebop_calldata.extend_from_slice("e_data);
|
|
||||||
|
|
||||||
// Encode MakerSignature struct
|
|
||||||
// Offset to signatureBytes (always 64 for this struct layout)
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(64).to_be_bytes::<32>());
|
|
||||||
// Flags (0 for ETH_SIGN)
|
|
||||||
bebop_calldata.extend_from_slice(&U256::ZERO.to_be_bytes::<32>());
|
|
||||||
// SignatureBytes (length + data)
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(signature.len()).to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&signature);
|
|
||||||
// Pad to 32-byte boundary
|
|
||||||
let padding = (32 - (signature.len() % 32)) % 32;
|
|
||||||
bebop_calldata.extend(vec![0u8; padding]);
|
|
||||||
|
|
||||||
// Prepend the partialFillOffset (2 for swapSingle - filledTakerAmount is at position
|
|
||||||
// 68)
|
|
||||||
let mut user_data = vec![2u8];
|
|
||||||
user_data.extend_from_slice(&bebop_calldata);
|
user_data.extend_from_slice(&bebop_calldata);
|
||||||
|
|
||||||
let bebop_component = ProtocolComponent {
|
let bebop_component = ProtocolComponent {
|
||||||
@@ -2178,164 +1869,36 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let hex_swap = encode(&encoded_swap);
|
let hex_swap = encode(&encoded_swap);
|
||||||
|
|
||||||
// Verify the encoding contains the expected tokens
|
let expected_swap = String::from(concat!(
|
||||||
assert!(hex_swap.contains("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")); // USDC
|
// token in
|
||||||
assert!(hex_swap.contains("faba6f8e4a5e8ab82f62fe7c39859fa577269be3")); // ONDO
|
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||||
|
// token out
|
||||||
// Verify it includes the bebop calldata
|
"faba6f8e4a5e8ab82f62fe7c39859fa577269be3",
|
||||||
let calldata_hex = hex::encode(bebop_calldata);
|
// transfer type
|
||||||
assert!(hex_swap.contains(&calldata_hex));
|
"01",
|
||||||
|
// partiall filled offset
|
||||||
// Verify the original amount matches the filledTakerAmount from calldata
|
"0c",
|
||||||
assert!(
|
// original taker amount
|
||||||
hex_swap.contains("0000000000000000000000000000000000000000000000000000000bebc200")
|
"000000000000000000000000000000000000000000000000000000000bebc200",
|
||||||
); // 200000000 in hex
|
// approval needed
|
||||||
|
"01",
|
||||||
// The packed data format is:
|
//receiver,
|
||||||
// token_in (20) | token_out (20) | transfer_type (1) | partial_fill_offset (1) |
|
"c5564c13a157e6240659fb81882a28091add8670",
|
||||||
// original_filled_taker_amount (32) | approval_needed (1) | receiver (20) |
|
));
|
||||||
// bebop_calldata Verify partialFillOffset and original_filled_taker_amount
|
assert_eq!(hex_swap, expected_swap + &bebop_calldata.to_string()[2..]);
|
||||||
// are correct
|
|
||||||
let offset_pos = 41 * 2; // 41 bytes * 2 hex chars per byte
|
|
||||||
let partial_fill_offset_hex = &hex_swap[offset_pos..offset_pos + 2];
|
|
||||||
let amount_hex = &hex_swap[offset_pos + 2..offset_pos + 2 + 64];
|
|
||||||
|
|
||||||
assert_eq!(partial_fill_offset_hex, "02", "partialFillOffset should be 02");
|
|
||||||
assert_eq!(
|
|
||||||
amount_hex, "000000000000000000000000000000000000000000000000000000000bebc200",
|
|
||||||
"original_filled_taker_amount should be 200000000 (0xbebc200)"
|
|
||||||
);
|
|
||||||
|
|
||||||
write_calldata_to_file("test_encode_bebop_single", hex_swap.as_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encode_bebop_aggregate() {
|
fn test_encode_bebop_aggregate() {
|
||||||
use alloy::{hex, primitives::Address, sol_types::SolValue};
|
// 20k USDC -> ONDO
|
||||||
|
let bebop_calldata= Bytes::from_str("0xa2f7489300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000689b78880000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000004c0000000000000000000000000d2068e04cf586f76eece7ba5beb779d7bb1474a100000000000000000000000000000000000000000000000000000000000005a060a5c2aaaaa2fe2cda34423cac76a84c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000002901f2d62bb356ca0000000000000000000000000000000000000000000000002901f2d62bb356cb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000044f83c726000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000589400da00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000003aa5f96046644f6e37a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000004b51a26526ddbeec60000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000417ab4332f2b091d87d56d04eee35dd49452782c782de71608c0425c5ae41f1d7e147173851c870d76720ce07d45cd8622352716b1c7965819ee2bf8c573c499ae1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410c8da2637aa929e11caff9afdfc4c489320c6dba77cc934d88ba8956e365fd1d48983087c6e474bbb828181cdfdd17317c4c9c3ee4bc98e3769d0c05cc7a285e1c00000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
|
let original_taker_amount = U256::from_str("20000000000").unwrap();
|
||||||
|
|
||||||
// Transaction: https://etherscan.io/tx/0xec88410136c287280da87d0a37c1cb745f320406ca3ae55c678dec11996c1b1c
|
// partialFillOffset is 2 for swapAggregate
|
||||||
let expiry = 1746367285u64;
|
|
||||||
let taker_address = Address::from_slice(
|
|
||||||
&hex::decode("7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6").unwrap(),
|
|
||||||
);
|
|
||||||
let receiver = taker_address;
|
|
||||||
|
|
||||||
// Set up makers
|
|
||||||
let maker_addresses = vec![
|
|
||||||
Address::from_slice(
|
|
||||||
&hex::decode("67336Cec42645F55059EfF241Cb02eA5cC52fF86").unwrap(),
|
|
||||||
),
|
|
||||||
Address::from_slice(
|
|
||||||
&hex::decode("BF19CbF0256f19f39A016a86Ff3551ecC6f2aAFE").unwrap(),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
let maker_nonces = vec![U256::from(1746367197308u64), U256::from(15460096u64)];
|
|
||||||
|
|
||||||
// 2D arrays for tokens
|
|
||||||
let weth_address = Address::from_slice(
|
|
||||||
&hex::decode("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").unwrap(),
|
|
||||||
);
|
|
||||||
let usdc_address = Address::from_slice(
|
|
||||||
&hex::decode("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap(),
|
|
||||||
);
|
|
||||||
let taker_tokens = vec![vec![weth_address], vec![weth_address]];
|
|
||||||
let maker_tokens = vec![vec![usdc_address], vec![usdc_address]];
|
|
||||||
|
|
||||||
// 2D arrays for amounts
|
|
||||||
let taker_amounts =
|
|
||||||
vec![vec![U256::from(5812106401997138u64)], vec![U256::from(4037893598002862u64)]];
|
|
||||||
let maker_amounts = vec![vec![U256::from(10607211u64)], vec![U256::from(7362350u64)]];
|
|
||||||
|
|
||||||
// Commands and flags from the real transaction
|
|
||||||
let commands = alloy::primitives::Bytes::from(hex::decode("00040004").unwrap());
|
|
||||||
let flags = U256::from_str_radix(
|
|
||||||
"d3fa5d891de82c082d5c51f03b47e826f86c96b88802b96a09bbae087e880000",
|
|
||||||
16,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Encode Aggregate order using ABI encoding
|
|
||||||
let quote_data = (
|
|
||||||
U256::from(expiry),
|
|
||||||
taker_address,
|
|
||||||
maker_addresses,
|
|
||||||
maker_nonces,
|
|
||||||
taker_tokens,
|
|
||||||
maker_tokens,
|
|
||||||
taker_amounts,
|
|
||||||
maker_amounts,
|
|
||||||
receiver,
|
|
||||||
commands,
|
|
||||||
flags,
|
|
||||||
)
|
|
||||||
.abi_encode();
|
|
||||||
|
|
||||||
// Real signatures from mainnet
|
|
||||||
let sig1 = hex::decode("d5abb425f9bac1f44d48705f41a8ab9cae207517be8553d2c03b06a88995f2f351ab8ce7627a87048178d539dd64fd2380245531a0c8e43fdc614652b1f32fc71c").unwrap();
|
|
||||||
let sig2 = hex::decode("f38c698e48a3eac48f184bc324fef0b135ee13705ab38cc0bbf5a792f21002f051e445b9e7d57cf24c35e17629ea35b3263591c4abf8ca87ffa44b41301b89c41b").unwrap();
|
|
||||||
|
|
||||||
// Build the complete swapAggregate calldata
|
|
||||||
let mut bebop_calldata = Vec::new();
|
|
||||||
|
|
||||||
// swapAggregate selector
|
|
||||||
bebop_calldata.extend_from_slice(&[0xa2, 0xf7, 0x48, 0x93]);
|
|
||||||
|
|
||||||
// Calculate filled taker amount (sum of both taker amounts)
|
|
||||||
let filled_taker_amount = U256::from(9850000000000000u64); // Total: 0.00985 WETH
|
|
||||||
|
|
||||||
// Encode parameters: (Aggregate order, MakerSignature[] signatures, uint256
|
|
||||||
// filledTakerAmount) Calculate offsets (relative to start of params, not
|
|
||||||
// selector)
|
|
||||||
let order_offset = U256::from(96); // After 3 words
|
|
||||||
|
|
||||||
// Fixed: Using Bytes type for commands field produces correct 1504-byte encoding
|
|
||||||
let signatures_offset = U256::from(96 + quote_data.len());
|
|
||||||
|
|
||||||
// Write the three parameter slots
|
|
||||||
bebop_calldata.extend_from_slice(&order_offset.to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&signatures_offset.to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&filled_taker_amount.to_be_bytes::<32>());
|
|
||||||
|
|
||||||
// Append the order data
|
|
||||||
bebop_calldata.extend_from_slice("e_data);
|
|
||||||
|
|
||||||
// Encode MakerSignature[] array
|
|
||||||
// Array length
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(2).to_be_bytes::<32>());
|
|
||||||
|
|
||||||
// Calculate offsets for each signature struct (relative to start of array data)
|
|
||||||
let sig1_data_size = 32 + 32 + 32 + sig1.len() + ((32 - (sig1.len() % 32)) % 32);
|
|
||||||
let sig1_offset = 64; // After 2 offset words
|
|
||||||
let sig2_offset = sig1_offset + sig1_data_size;
|
|
||||||
|
|
||||||
// Write offsets for each signature
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(sig1_offset).to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(sig2_offset).to_be_bytes::<32>());
|
|
||||||
|
|
||||||
// Encode first MakerSignature struct
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(64).to_be_bytes::<32>()); // offset to bytes
|
|
||||||
bebop_calldata.extend_from_slice(&U256::ZERO.to_be_bytes::<32>()); // flags = 0 (ETH_SIGN)
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(sig1.len()).to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&sig1);
|
|
||||||
let padding1 = (32 - (sig1.len() % 32)) % 32;
|
|
||||||
bebop_calldata.extend(vec![0u8; padding1]);
|
|
||||||
|
|
||||||
// Encode second MakerSignature struct
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(64).to_be_bytes::<32>()); // offset to bytes
|
|
||||||
bebop_calldata.extend_from_slice(&U256::ZERO.to_be_bytes::<32>()); // flags = 0 (ETH_SIGN)
|
|
||||||
bebop_calldata.extend_from_slice(&U256::from(sig2.len()).to_be_bytes::<32>());
|
|
||||||
bebop_calldata.extend_from_slice(&sig2);
|
|
||||||
let padding2 = (32 - (sig2.len() % 32)) % 32;
|
|
||||||
bebop_calldata.extend(vec![0u8; padding2]);
|
|
||||||
|
|
||||||
// Prepend the partialFillOffset (2 for swapAggregate)
|
|
||||||
let mut user_data = vec![2u8];
|
let mut user_data = vec![2u8];
|
||||||
|
user_data.extend_from_slice(&original_taker_amount.to_be_bytes::<32>());
|
||||||
user_data.extend_from_slice(&bebop_calldata);
|
user_data.extend_from_slice(&bebop_calldata);
|
||||||
|
|
||||||
// Extract bebop_calldata for verification (skip the first byte which is
|
|
||||||
// partialFillOffset)
|
|
||||||
let bebop_calldata = user_data[1..].to_vec();
|
|
||||||
|
|
||||||
let bebop_component = ProtocolComponent {
|
let bebop_component = ProtocolComponent {
|
||||||
id: String::from("bebop-rfq"),
|
id: String::from("bebop-rfq"),
|
||||||
protocol_system: String::from("rfq:bebop"),
|
protocol_system: String::from("rfq:bebop"),
|
||||||
@@ -2343,8 +1906,8 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let token_in = Bytes::from("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); // WETH
|
let token_in = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC
|
||||||
let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC
|
let token_out = Bytes::from("0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3"); // ONDO
|
||||||
|
|
||||||
let swap = Swap {
|
let swap = Swap {
|
||||||
component: bebop_component,
|
component: bebop_component,
|
||||||
@@ -2356,7 +1919,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let encoding_context = EncodingContext {
|
let encoding_context = EncodingContext {
|
||||||
receiver: Bytes::from("0x7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6"), /* Use actual receiver from order */
|
receiver: Bytes::from("0xc5564C13A157E6240659fb81882A28091add8670"),
|
||||||
exact_out: false,
|
exact_out: false,
|
||||||
router_address: Some(Bytes::zero(20)),
|
router_address: Some(Bytes::zero(20)),
|
||||||
group_token_in: token_in.clone(),
|
group_token_in: token_in.clone(),
|
||||||
@@ -2379,29 +1942,24 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let hex_swap = encode(&encoded_swap);
|
let hex_swap = encode(&encoded_swap);
|
||||||
|
|
||||||
// Verify the encoding contains the expected tokens
|
let expected_swap = String::from(concat!(
|
||||||
assert!(hex_swap.contains("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")); // WETH
|
// token in
|
||||||
assert!(hex_swap.contains("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")); // USDC
|
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||||
|
// token out
|
||||||
|
"faba6f8e4a5e8ab82f62fe7c39859fa577269be3",
|
||||||
|
// transfer type
|
||||||
|
"01",
|
||||||
|
// partiall filled offset
|
||||||
|
"02",
|
||||||
|
// original taker amount
|
||||||
|
"00000000000000000000000000000000000000000000000000000004a817c800",
|
||||||
|
// approval needed
|
||||||
|
"01",
|
||||||
|
//receiver,
|
||||||
|
"c5564c13a157e6240659fb81882a28091add8670",
|
||||||
|
));
|
||||||
|
|
||||||
// Verify it includes the bebop calldata
|
assert_eq!(hex_swap, expected_swap + &bebop_calldata.to_string()[2..]);
|
||||||
let calldata_hex = hex::encode(&bebop_calldata);
|
|
||||||
assert!(hex_swap.contains(&calldata_hex));
|
|
||||||
|
|
||||||
// Verify the original amount
|
|
||||||
let filled_amount_hex = format!("{filled_taker_amount:064x}",);
|
|
||||||
assert!(
|
|
||||||
hex_swap.contains(&filled_amount_hex),
|
|
||||||
"Should contain filled_taker_amount in hex"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify the partialFillOffset byte (02 = 2) appears in the right place
|
|
||||||
let expected_pattern = format!("02{filled_amount_hex}");
|
|
||||||
assert!(
|
|
||||||
hex_swap.contains(&expected_pattern),
|
|
||||||
"partialFillOffset byte (02) should be followed by original filledTakerAmount"
|
|
||||||
);
|
|
||||||
|
|
||||||
write_calldata_to_file("test_encode_bebop_aggregate", hex_swap.as_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use tycho_common::{
|
|||||||
models::protocol::ProtocolComponent, simulation::protocol_sim::ProtocolSim, Bytes,
|
models::protocol::ProtocolComponent, simulation::protocol_sim::ProtocolSim, Bytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::encoding::{errors::EncodingError, serde_primitives::biguint_string};
|
use crate::encoding::serde_primitives::biguint_string;
|
||||||
|
|
||||||
/// Specifies the method for transferring user funds into Tycho execution.
|
/// Specifies the method for transferring user funds into Tycho execution.
|
||||||
///
|
///
|
||||||
@@ -223,25 +223,6 @@ pub enum TransferType {
|
|||||||
None = 2,
|
None = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bebop order types
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum BebopOrderType {
|
|
||||||
Single = 0,
|
|
||||||
Aggregate = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u8> for BebopOrderType {
|
|
||||||
type Error = EncodingError;
|
|
||||||
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
0 => Ok(BebopOrderType::Single),
|
|
||||||
1 => Ok(BebopOrderType::Aggregate),
|
|
||||||
_ => Err(EncodingError::InvalidInput(format!("Invalid Bebop order type: {value}"))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ pub mod encoding;
|
|||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use alloy::{primitives::B256, signers::local::PrivateKeySigner};
|
use alloy::{
|
||||||
|
primitives::{B256, U256},
|
||||||
|
signers::local::PrivateKeySigner,
|
||||||
|
};
|
||||||
use tycho_common::{models::Chain, Bytes};
|
use tycho_common::{models::Chain, Bytes};
|
||||||
use tycho_execution::encoding::{
|
use tycho_execution::encoding::{
|
||||||
evm::encoder_builders::TychoRouterEncoderBuilder, models::UserTransferType,
|
evm::encoder_builders::TychoRouterEncoderBuilder, models::UserTransferType,
|
||||||
@@ -70,9 +73,16 @@ pub fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> Box<dyn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the complete Bebop calldata in the format expected by the encoder
|
/// Builds the complete Bebop calldata in the format expected by the encoder
|
||||||
/// Returns: partialFillOffset (1 byte) | bebop_calldata (selector + ABI encoded params)
|
/// Returns: [ partial_fill_offset (u8) | original_taker_amount (U256) | calldata (bytes (selector +
|
||||||
pub fn build_bebop_calldata(calldata: &[u8], partial_fill_offset: u8) -> Bytes {
|
/// ABI encoded params)) ]
|
||||||
let mut user_data = vec![partial_fill_offset];
|
pub fn build_bebop_calldata(
|
||||||
|
calldata: &[u8],
|
||||||
|
partial_fill_offset: u8,
|
||||||
|
original_taker_amount: U256,
|
||||||
|
) -> Bytes {
|
||||||
|
let mut user_data = Vec::with_capacity(1 + 32 + calldata.len());
|
||||||
|
user_data.push(partial_fill_offset);
|
||||||
|
user_data.extend_from_slice(&original_taker_amount.to_be_bytes::<32>());
|
||||||
user_data.extend_from_slice(calldata);
|
user_data.extend_from_slice(calldata);
|
||||||
|
|
||||||
Bytes::from(user_data)
|
Bytes::from(user_data)
|
||||||
|
|||||||
@@ -593,143 +593,3 @@ fn test_uniswap_v3_balancer_v3() {
|
|||||||
let hex_calldata = encode(&calldata);
|
let hex_calldata = encode(&calldata);
|
||||||
write_calldata_to_file("test_uniswap_v3_balancer_v3", hex_calldata.as_str());
|
write_calldata_to_file("test_uniswap_v3_balancer_v3", hex_calldata.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_uniswap_v3_bebop() {
|
|
||||||
// // Note: This test does not assert anything. It is only used to obtain
|
|
||||||
// // integration test data for our router solidity test.
|
|
||||||
// //
|
|
||||||
// // Performs a sequential swap from WETH to ONDO through USDC using USV3 and
|
|
||||||
// // Bebop RFQ
|
|
||||||
// //
|
|
||||||
// // WETH ───(USV3)──> USDC ───(Bebop RFQ)──> ONDO
|
|
||||||
//
|
|
||||||
// let weth = weth();
|
|
||||||
// let usdc = usdc();
|
|
||||||
// let ondo = ondo();
|
|
||||||
//
|
|
||||||
// // First swap: WETH -> USDC via UniswapV3
|
|
||||||
// let swap_weth_usdc = Swap {
|
|
||||||
// component: ProtocolComponent {
|
|
||||||
// id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* WETH-USDC USV3 Pool
|
|
||||||
// * 0.05% */
|
|
||||||
// protocol_system: "uniswap_v3".to_string(),
|
|
||||||
// static_attributes: {
|
|
||||||
// let mut attrs = HashMap::new();
|
|
||||||
// attrs
|
|
||||||
// .insert("fee".to_string(),
|
|
||||||
// Bytes::from(BigInt::from(500).to_signed_bytes_be())); attrs
|
|
||||||
// },
|
|
||||||
// ..Default::default()
|
|
||||||
// },
|
|
||||||
// token_in: weth.clone(),
|
|
||||||
// token_out: usdc.clone(),
|
|
||||||
// split: 0f64,
|
|
||||||
// user_data: None,
|
|
||||||
// protocol_state: None,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// // Second swap: USDC -> ONDO via Bebop RFQ using real order data
|
|
||||||
// // Using the same real order from the mainnet transaction at block 22667985
|
|
||||||
// let expiry = 1749483840u64; // Real expiry from the order
|
|
||||||
// let taker_address = Address::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap();
|
|
||||||
// // Real taker let maker_address =
|
|
||||||
// Address::from_str("0xCe79b081c0c924cb67848723ed3057234d10FC6b").unwrap(); // Real maker
|
|
||||||
// let maker_nonce = 1749483765992417u64; // Real nonce
|
|
||||||
// let taker_token = Address::from_str(&usdc.to_string()).unwrap();
|
|
||||||
// let maker_token = Address::from_str(&ondo.to_string()).unwrap();
|
|
||||||
// // Using the real order amounts
|
|
||||||
// let taker_amount = U256::from_str("200000000").unwrap(); // 200 USDC (6 decimals)
|
|
||||||
// let maker_amount = U256::from_str("237212396774431060000").unwrap(); // 237.21 ONDO (18
|
|
||||||
// decimals) let receiver =
|
|
||||||
// Address::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(); // Real receiver
|
|
||||||
// let packed_commands = U256::ZERO;
|
|
||||||
// let flags = U256::from_str(
|
|
||||||
// "51915842898789398998206002334703507894664330885127600393944965515693155942400",
|
|
||||||
// )
|
|
||||||
// .unwrap(); // Real flags
|
|
||||||
//
|
|
||||||
// // Encode using standard ABI encoding (not packed)
|
|
||||||
// let quote_data = (
|
|
||||||
// expiry,
|
|
||||||
// taker_address,
|
|
||||||
// maker_address,
|
|
||||||
// maker_nonce,
|
|
||||||
// taker_token,
|
|
||||||
// maker_token,
|
|
||||||
// taker_amount,
|
|
||||||
// maker_amount,
|
|
||||||
// receiver,
|
|
||||||
// packed_commands,
|
|
||||||
// flags,
|
|
||||||
// )
|
|
||||||
// .abi_encode();
|
|
||||||
//
|
|
||||||
// // Real signature from the order
|
|
||||||
// let signature =
|
|
||||||
// hex::decode("
|
|
||||||
// eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c"
|
|
||||||
// ).unwrap();
|
|
||||||
//
|
|
||||||
// // Build user_data with the quote and signature
|
|
||||||
// let user_data = build_bebop_calldata(
|
|
||||||
// BebopOrderType::Single,
|
|
||||||
// U256::from(0), // 0 means fill entire order
|
|
||||||
// "e_data,
|
|
||||||
// vec![(signature, 0)], // ETH_SIGN signature type (0)
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// let bebop_component = ProtocolComponent {
|
|
||||||
// id: String::from("bebop-rfq"),
|
|
||||||
// protocol_system: String::from("rfq:bebop"),
|
|
||||||
// static_attributes: HashMap::new(), // No static attributes needed
|
|
||||||
// ..Default::default()
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// let swap_usdc_ondo = Swap {
|
|
||||||
// component: bebop_component,
|
|
||||||
// token_in: usdc.clone(),
|
|
||||||
// token_out: ondo.clone(),
|
|
||||||
// split: 0f64,
|
|
||||||
// user_data: Some(user_data),
|
|
||||||
// protocol_state: None,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
|
||||||
//
|
|
||||||
// let solution = Solution {
|
|
||||||
// exact_out: false,
|
|
||||||
// given_token: weth,
|
|
||||||
// // Use ~0.099 WETH to get approximately 200 USDC from UniswapV3
|
|
||||||
// // This should leave only dust amount in the router after Bebop consumes 200
|
|
||||||
// // USDC
|
|
||||||
// given_amount: BigUint::from_str("99000000000000000").unwrap(), // 0.099 WETH
|
|
||||||
// checked_token: ondo,
|
|
||||||
// checked_amount: BigUint::from_str("237212396774431060000").unwrap(), /* Expected ONDO
|
|
||||||
// from Bebop order */ sender:
|
|
||||||
// Bytes::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(), /* Must match order
|
|
||||||
// taker_address */ receiver:
|
|
||||||
// Bytes::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(), /* Using the real order
|
|
||||||
// receiver */ swaps: vec![swap_weth_usdc, swap_usdc_ondo],
|
|
||||||
// ..Default::default()
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// let encoded_solution = encoder
|
|
||||||
// .encode_solutions(vec![solution.clone()])
|
|
||||||
// .unwrap()[0]
|
|
||||||
// .clone();
|
|
||||||
//
|
|
||||||
// let calldata = encode_tycho_router_call(
|
|
||||||
// eth_chain().id(),
|
|
||||||
// encoded_solution,
|
|
||||||
// &solution,
|
|
||||||
// &UserTransferType::TransferFrom,
|
|
||||||
// ð(),
|
|
||||||
// None,
|
|
||||||
// )
|
|
||||||
// .unwrap()
|
|
||||||
// .data;
|
|
||||||
//
|
|
||||||
// let hex_calldata = encode(&calldata);
|
|
||||||
// write_calldata_to_file("test_uniswap_v3_bebop", hex_calldata.as_str());
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use alloy::{hex, hex::encode};
|
|||||||
use num_bigint::{BigInt, BigUint};
|
use num_bigint::{BigInt, BigUint};
|
||||||
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
||||||
use tycho_execution::encoding::{
|
use tycho_execution::encoding::{
|
||||||
evm::utils::write_calldata_to_file,
|
evm::utils::{biguint_to_u256, write_calldata_to_file},
|
||||||
models::{Solution, Swap, UserTransferType},
|
models::{Solution, Swap, UserTransferType},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -597,7 +597,8 @@ fn test_single_encoding_strategy_bebop() {
|
|||||||
let partial_fill_offset = 12;
|
let partial_fill_offset = 12;
|
||||||
|
|
||||||
let calldata = Bytes::from_str("0x4dcebcba00000000000000000000000000000000000000000000000000000000689b548f0000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000067336cec42645f55059eff241cb02ea5cc52ff86000000000000000000000000000000000000000000000000279ead5d9685f25b000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be3000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000a8aea46aa4ec5c0f5000000000000000000000000d2068e04cf586f76eece7ba5beb779d7bb1474a100000000000000000000000000000000000000000000000000000000000000005230bcb979c81cebf94a3b5c08bcfa300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414ce40058ff07f11d9224c2c8d1e58369e4a90173856202d8d2a17da48058ad683dedb742eda0d4c0cf04cf1c09138898dd7fd06f97268ea7f74ef9b42d29bf4c1b00000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let calldata = Bytes::from_str("0x4dcebcba00000000000000000000000000000000000000000000000000000000689b548f0000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000067336cec42645f55059eff241cb02ea5cc52ff86000000000000000000000000000000000000000000000000279ead5d9685f25b000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be3000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000a8aea46aa4ec5c0f5000000000000000000000000d2068e04cf586f76eece7ba5beb779d7bb1474a100000000000000000000000000000000000000000000000000000000000000005230bcb979c81cebf94a3b5c08bcfa300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414ce40058ff07f11d9224c2c8d1e58369e4a90173856202d8d2a17da48058ad683dedb742eda0d4c0cf04cf1c09138898dd7fd06f97268ea7f74ef9b42d29bf4c1b00000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
let user_data = build_bebop_calldata(&calldata, partial_fill_offset);
|
let user_data =
|
||||||
|
build_bebop_calldata(&calldata, partial_fill_offset, biguint_to_u256(&amount_in));
|
||||||
|
|
||||||
let bebop_component = ProtocolComponent {
|
let bebop_component = ProtocolComponent {
|
||||||
id: String::from("bebop-rfq"),
|
id: String::from("bebop-rfq"),
|
||||||
@@ -662,7 +663,8 @@ fn test_single_encoding_strategy_bebop_aggregate() {
|
|||||||
let partial_fill_offset = 2;
|
let partial_fill_offset = 2;
|
||||||
|
|
||||||
let calldata = Bytes::from_str("0xa2f7489300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000689b78880000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000004c0000000000000000000000000d2068e04cf586f76eece7ba5beb779d7bb1474a100000000000000000000000000000000000000000000000000000000000005a060a5c2aaaaa2fe2cda34423cac76a84c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000002901f2d62bb356ca0000000000000000000000000000000000000000000000002901f2d62bb356cb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000044f83c726000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000589400da00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000003aa5f96046644f6e37a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000004b51a26526ddbeec60000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000417ab4332f2b091d87d56d04eee35dd49452782c782de71608c0425c5ae41f1d7e147173851c870d76720ce07d45cd8622352716b1c7965819ee2bf8c573c499ae1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410c8da2637aa929e11caff9afdfc4c489320c6dba77cc934d88ba8956e365fd1d48983087c6e474bbb828181cdfdd17317c4c9c3ee4bc98e3769d0c05cc7a285e1c00000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let calldata = Bytes::from_str("0xa2f7489300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000689b78880000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000004c0000000000000000000000000d2068e04cf586f76eece7ba5beb779d7bb1474a100000000000000000000000000000000000000000000000000000000000005a060a5c2aaaaa2fe2cda34423cac76a84c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000002901f2d62bb356ca0000000000000000000000000000000000000000000000002901f2d62bb356cb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000044f83c726000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000589400da00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000003aa5f96046644f6e37a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000004b51a26526ddbeec60000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000417ab4332f2b091d87d56d04eee35dd49452782c782de71608c0425c5ae41f1d7e147173851c870d76720ce07d45cd8622352716b1c7965819ee2bf8c573c499ae1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410c8da2637aa929e11caff9afdfc4c489320c6dba77cc934d88ba8956e365fd1d48983087c6e474bbb828181cdfdd17317c4c9c3ee4bc98e3769d0c05cc7a285e1c00000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
let user_data = build_bebop_calldata(&calldata, partial_fill_offset);
|
let user_data =
|
||||||
|
build_bebop_calldata(&calldata, partial_fill_offset, biguint_to_u256(&amount_in));
|
||||||
|
|
||||||
let bebop_component = ProtocolComponent {
|
let bebop_component = ProtocolComponent {
|
||||||
id: String::from("bebop-rfq"),
|
id: String::from("bebop-rfq"),
|
||||||
|
|||||||
Reference in New Issue
Block a user