chore: include partialFillOffset as part of user_data
This commit is contained in:
@@ -112,6 +112,7 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
address tokenOut,
|
address tokenOut,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
bytes memory bebopCalldata,
|
bytes memory bebopCalldata,
|
||||||
|
uint8 partialFillOffset,
|
||||||
uint256 originalFilledTakerAmount,
|
uint256 originalFilledTakerAmount,
|
||||||
bool approvalNeeded
|
bool approvalNeeded
|
||||||
) = _decodeData(data);
|
) = _decodeData(data);
|
||||||
@@ -119,7 +120,7 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
// Modify the filledTakerAmount in the calldata
|
// Modify the filledTakerAmount in the calldata
|
||||||
// If the filledTakerAmount is the same as the original, the original calldata is returned
|
// If the filledTakerAmount is the same as the original, the original calldata is returned
|
||||||
bytes memory finalCalldata = _modifyFilledTakerAmount(
|
bytes memory finalCalldata = _modifyFilledTakerAmount(
|
||||||
bebopCalldata, givenAmount, originalFilledTakerAmount
|
bebopCalldata, givenAmount, originalFilledTakerAmount, partialFillOffset
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transfer tokens if needed
|
// Transfer tokens if needed
|
||||||
@@ -157,13 +158,14 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
address tokenOut,
|
address tokenOut,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
bytes memory bebopCalldata,
|
bytes memory bebopCalldata,
|
||||||
|
uint8 partialFillOffset,
|
||||||
uint256 originalFilledTakerAmount,
|
uint256 originalFilledTakerAmount,
|
||||||
bool approvalNeeded
|
bool approvalNeeded
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Need at least 78 bytes for the minimum fixed fields
|
// Need at least 79 bytes for the minimum fixed fields
|
||||||
// 20 + 20 + 1 + 4 (calldata length) + 32 (original amount) + 1 (approval) = 78
|
// 20 + 20 + 1 + 4 (calldata length) + 1 (offset) + 32 (original amount) + 1 (approval) = 79
|
||||||
if (data.length < 78) revert BebopExecutor__InvalidDataLength();
|
if (data.length < 79) revert BebopExecutor__InvalidDataLength();
|
||||||
|
|
||||||
// Decode fixed fields
|
// Decode fixed fields
|
||||||
tokenIn = address(bytes20(data[0:20]));
|
tokenIn = address(bytes20(data[0:20]));
|
||||||
@@ -172,58 +174,44 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
|
|
||||||
// Get bebop calldata length and validate
|
// Get bebop calldata length and validate
|
||||||
uint32 bebopCalldataLength = uint32(bytes4(data[41:45]));
|
uint32 bebopCalldataLength = uint32(bytes4(data[41:45]));
|
||||||
if (data.length != 78 + bebopCalldataLength) {
|
if (data.length != 79 + bebopCalldataLength) {
|
||||||
revert BebopExecutor__InvalidDataLength();
|
revert BebopExecutor__InvalidDataLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract bebop calldata
|
// Extract bebop calldata
|
||||||
bebopCalldata = data[45:45 + bebopCalldataLength];
|
bebopCalldata = data[45:45 + bebopCalldataLength];
|
||||||
|
|
||||||
|
// Extract partial fill offset
|
||||||
|
partialFillOffset = uint8(data[45 + bebopCalldataLength]);
|
||||||
|
|
||||||
// Extract original amount in
|
// Extract original amount in
|
||||||
originalFilledTakerAmount = uint256(
|
originalFilledTakerAmount = uint256(
|
||||||
bytes32(data[45 + bebopCalldataLength:77 + bebopCalldataLength])
|
bytes32(data[46 + bebopCalldataLength:78 + bebopCalldataLength])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Extract approval flag
|
// Extract approval flag
|
||||||
approvalNeeded = data[77 + bebopCalldataLength] != 0;
|
approvalNeeded = data[78 + bebopCalldataLength] != 0;
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Determines the actual taker amount to be filled for a Bebop order
|
|
||||||
/// @notice The encoder ensures filledTakerAmount is never 0 by extracting from order data when needed.
|
|
||||||
/// This function simply caps the fill amount at the available tokens from the router.
|
|
||||||
/// @param givenAmount The amount of tokens available from the router for this swap
|
|
||||||
/// @param filledTakerAmount The requested fill amount (guaranteed to be non-zero by encoder)
|
|
||||||
/// @return actualFilledTakerAmount The amount that will actually be filled
|
|
||||||
function _getActualFilledTakerAmount(
|
|
||||||
uint256 givenAmount,
|
|
||||||
uint256 filledTakerAmount
|
|
||||||
) internal pure returns (uint256 actualFilledTakerAmount) {
|
|
||||||
actualFilledTakerAmount =
|
|
||||||
filledTakerAmount > givenAmount ? givenAmount : filledTakerAmount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Modifies the filledTakerAmount in the bebop calldata to handle slippage
|
/// @dev Modifies the filledTakerAmount in the bebop calldata to handle slippage
|
||||||
/// @param bebopCalldata The original calldata for the bebop settlement
|
/// @param bebopCalldata The original calldata for the bebop settlement
|
||||||
/// @param givenAmount The actual amount available from the router
|
/// @param givenAmount The actual amount available from the router
|
||||||
/// @param originalFilledTakerAmount The original amount expected when the quote was generated
|
/// @param originalFilledTakerAmount The original amount expected when the quote was generated
|
||||||
|
/// @param partialFillOffset The offset from Bebop API indicating where filledTakerAmount is located
|
||||||
/// @return The modified calldata with updated filledTakerAmount
|
/// @return The modified calldata with updated filledTakerAmount
|
||||||
function _modifyFilledTakerAmount(
|
function _modifyFilledTakerAmount(
|
||||||
bytes memory bebopCalldata,
|
bytes memory bebopCalldata,
|
||||||
uint256 givenAmount,
|
uint256 givenAmount,
|
||||||
uint256 originalFilledTakerAmount
|
uint256 originalFilledTakerAmount,
|
||||||
|
uint8 partialFillOffset
|
||||||
) public pure returns (bytes memory) {
|
) public pure returns (bytes memory) {
|
||||||
bytes4 selector = _getSelector(bebopCalldata);
|
// Use the offset from Bebop API to locate filledTakerAmount
|
||||||
|
// Position = 4 bytes (selector) + offset * 32 bytes
|
||||||
|
uint256 filledTakerAmountPos = 4 + uint256(partialFillOffset) * 32;
|
||||||
|
|
||||||
// The position of filledTakerAmount differs between swapSingle and swapAggregate
|
// Cap the fill amount at what we actually have available
|
||||||
// due to how Solidity encodes structs:
|
|
||||||
// - swapSingle: Single struct is encoded inline (no offset), so filledTakerAmount is at position 388
|
|
||||||
// - swapAggregate: Aggregate struct uses offset (has arrays), so filledTakerAmount is at position 68
|
|
||||||
uint256 filledTakerAmountPos =
|
|
||||||
selector == SWAP_SINGLE_SELECTOR ? 388 : 68;
|
|
||||||
|
|
||||||
// Calculate new filledTakerAmount using _getActualFilledTakerAmount
|
|
||||||
uint256 newFilledTakerAmount =
|
uint256 newFilledTakerAmount =
|
||||||
_getActualFilledTakerAmount(givenAmount, originalFilledTakerAmount);
|
originalFilledTakerAmount > givenAmount ? givenAmount : originalFilledTakerAmount;
|
||||||
|
|
||||||
// If the new filledTakerAmount is the same as the original, return the original calldata
|
// If the new filledTakerAmount is the same as the original, return the original calldata
|
||||||
if (newFilledTakerAmount == originalFilledTakerAmount) {
|
if (newFilledTakerAmount == originalFilledTakerAmount) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -100,6 +100,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(12), // partialFillOffset for swapSingle (388 = 4 + 12*32)
|
||||||
originalAmountIn,
|
originalAmountIn,
|
||||||
uint8(1) // approvalNeeded: true
|
uint8(1) // approvalNeeded: true
|
||||||
);
|
);
|
||||||
@@ -110,6 +111,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
address tokenOut,
|
address tokenOut,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
bytes memory decodedBebopCalldata,
|
bytes memory decodedBebopCalldata,
|
||||||
|
uint8 decodedPartialFillOffset,
|
||||||
uint256 decodedOriginalAmountIn,
|
uint256 decodedOriginalAmountIn,
|
||||||
bool decodedApprovalNeeded
|
bool decodedApprovalNeeded
|
||||||
) = bebopExecutor.decodeParams(params);
|
) = bebopExecutor.decodeParams(params);
|
||||||
@@ -126,6 +128,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
keccak256(bebopCalldata),
|
keccak256(bebopCalldata),
|
||||||
"bebopCalldata mismatch"
|
"bebopCalldata mismatch"
|
||||||
);
|
);
|
||||||
|
assertEq(decodedPartialFillOffset, 12, "partialFillOffset mismatch");
|
||||||
assertEq(
|
assertEq(
|
||||||
decodedOriginalAmountIn,
|
decodedOriginalAmountIn,
|
||||||
originalAmountIn,
|
originalAmountIn,
|
||||||
@@ -212,6 +215,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(12), // partialFillOffset for swapSingle (388 = 4 + 12*32)
|
||||||
testData.order.taker_amount, // originalAmountIn (matches what encoder would produce)
|
testData.order.taker_amount, // originalAmountIn (matches what encoder would produce)
|
||||||
uint8(1) // approvalNeeded: true
|
uint8(1) // approvalNeeded: true
|
||||||
);
|
);
|
||||||
@@ -307,6 +311,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(12), // partialFillOffset for swapSingle (388 = 4 + 12*32)
|
||||||
testData.order.taker_amount, // originalAmountIn (full order amount)
|
testData.order.taker_amount, // originalAmountIn (full order amount)
|
||||||
uint8(1) // approvalNeeded: true
|
uint8(1) // approvalNeeded: true
|
||||||
);
|
);
|
||||||
@@ -441,6 +446,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(2), // partialFillOffset for swapAggregate (68 = 4 + 2*32)
|
||||||
totalTakerAmount, // originalAmountIn
|
totalTakerAmount, // originalAmountIn
|
||||||
uint8(0) // approvalNeeded: false for native ETH
|
uint8(0) // approvalNeeded: false for native ETH
|
||||||
);
|
);
|
||||||
@@ -580,6 +586,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(2), // partialFillOffset for swapAggregate (68 = 4 + 2*32)
|
||||||
totalTakerAmount, // originalAmountIn (full order amount)
|
totalTakerAmount, // originalAmountIn (full order amount)
|
||||||
uint8(0) // approvalNeeded: false for native ETH
|
uint8(0) // approvalNeeded: false for native ETH
|
||||||
);
|
);
|
||||||
@@ -628,6 +635,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(12), // partialFillOffset for swapSingle (388 = 4 + 12*32)
|
||||||
originalAmountIn,
|
originalAmountIn,
|
||||||
uint8(1) // approvalNeeded: true
|
uint8(1) // approvalNeeded: true
|
||||||
);
|
);
|
||||||
@@ -703,6 +711,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(12), // partialFillOffset for swapSingle (388 = 4 + 12*32)
|
||||||
uint256(200000000), // originalAmountIn
|
uint256(200000000), // originalAmountIn
|
||||||
uint8(1) // approvalNeeded: true
|
uint8(1) // approvalNeeded: true
|
||||||
);
|
);
|
||||||
@@ -823,6 +832,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer),
|
uint8(RestrictTransferFrom.TransferType.Transfer),
|
||||||
uint32(bebopCalldata.length),
|
uint32(bebopCalldata.length),
|
||||||
bebopCalldata,
|
bebopCalldata,
|
||||||
|
uint8(2), // partialFillOffset for swapAggregate (68 = 4 + 2*32)
|
||||||
ethAmount, // originalAmountIn
|
ethAmount, // originalAmountIn
|
||||||
uint8(0) // approvalNeeded: false for native ETH
|
uint8(0) // approvalNeeded: false for native ETH
|
||||||
);
|
);
|
||||||
@@ -1098,7 +1108,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
|
|
||||||
bytes memory modifiedCalldata = bebopExecutor
|
bytes memory modifiedCalldata = bebopExecutor
|
||||||
.exposed_modifyFilledTakerAmount(
|
.exposed_modifyFilledTakerAmount(
|
||||||
originalCalldata, givenAmount, originalAmountIn
|
originalCalldata, givenAmount, originalAmountIn, 12 // partialFillOffset for swapSingle
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decode the modified calldata to verify the filledTakerAmount was updated
|
// Decode the modified calldata to verify the filledTakerAmount was updated
|
||||||
@@ -1174,7 +1184,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
|
|
||||||
bytes memory modifiedCalldata = bebopExecutor
|
bytes memory modifiedCalldata = bebopExecutor
|
||||||
.exposed_modifyFilledTakerAmount(
|
.exposed_modifyFilledTakerAmount(
|
||||||
originalCalldata, givenAmount, originalAmountIn
|
originalCalldata, givenAmount, originalAmountIn, 2 // partialFillOffset for swapAggregate
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decode the modified calldata to verify the filledTakerAmount was updated
|
// Decode the modified calldata to verify the filledTakerAmount was updated
|
||||||
@@ -1233,7 +1243,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
// So we'll test that it properly sets the value we want
|
// So we'll test that it properly sets the value we want
|
||||||
bytes memory modifiedCalldata = bebopExecutor
|
bytes memory modifiedCalldata = bebopExecutor
|
||||||
.exposed_modifyFilledTakerAmount(
|
.exposed_modifyFilledTakerAmount(
|
||||||
originalCalldata, givenAmount, originalAmountIn
|
originalCalldata, givenAmount, originalAmountIn, 12 // partialFillOffset for swapSingle
|
||||||
);
|
);
|
||||||
|
|
||||||
// Extract the new filledTakerAmount
|
// Extract the new filledTakerAmount
|
||||||
@@ -1249,7 +1259,7 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|||||||
// Normal test - amounts match so calldata should be unchanged
|
// Normal test - amounts match so calldata should be unchanged
|
||||||
bytes memory modifiedCalldata = bebopExecutor
|
bytes memory modifiedCalldata = bebopExecutor
|
||||||
.exposed_modifyFilledTakerAmount(
|
.exposed_modifyFilledTakerAmount(
|
||||||
originalCalldata, givenAmount, originalAmountIn
|
originalCalldata, givenAmount, originalAmountIn, 12 // partialFillOffset for swapSingle
|
||||||
);
|
);
|
||||||
|
|
||||||
assertEq(
|
assertEq(
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ pragma solidity ^0.8.10;
|
|||||||
|
|
||||||
import "../../src/executors/BebopExecutor.sol";
|
import "../../src/executors/BebopExecutor.sol";
|
||||||
import {Test, console} from "forge-std/Test.sol";
|
import {Test, console} from "forge-std/Test.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/Address.sol";
|
||||||
|
|
||||||
contract BebopExecutorHarness is BebopExecutor, Test {
|
contract BebopExecutorHarness is BebopExecutor, Test {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
using Address for address;
|
||||||
|
|
||||||
constructor(address _bebopSettlement, address _permit2)
|
constructor(address _bebopSettlement, address _permit2)
|
||||||
BebopExecutor(_bebopSettlement, _permit2)
|
BebopExecutor(_bebopSettlement, _permit2)
|
||||||
@@ -56,6 +58,7 @@ contract BebopExecutorHarness is BebopExecutor, Test {
|
|||||||
address tokenOut,
|
address tokenOut,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
bytes memory bebopCalldata,
|
bytes memory bebopCalldata,
|
||||||
|
uint8 partialFillOffset,
|
||||||
uint256 originalFilledTakerAmount,
|
uint256 originalFilledTakerAmount,
|
||||||
bool approvalNeeded
|
bool approvalNeeded
|
||||||
)
|
)
|
||||||
@@ -63,22 +66,24 @@ contract BebopExecutorHarness is BebopExecutor, Test {
|
|||||||
return _decodeData(data);
|
return _decodeData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose the internal getActualFilledTakerAmount function for testing
|
// No longer needed since we inlined the logic
|
||||||
function exposed_getActualFilledTakerAmount(
|
function exposed_getActualFilledTakerAmount(
|
||||||
uint256 givenAmount,
|
uint256 givenAmount,
|
||||||
uint256 filledTakerAmount
|
uint256 filledTakerAmount
|
||||||
) external pure returns (uint256 actualFilledTakerAmount) {
|
) external pure returns (uint256 actualFilledTakerAmount) {
|
||||||
return _getActualFilledTakerAmount(givenAmount, filledTakerAmount);
|
// Inline the simple logic here for backward compatibility
|
||||||
|
actualFilledTakerAmount = filledTakerAmount > givenAmount ? givenAmount : filledTakerAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose the internal modifyFilledTakerAmount function for testing
|
// Expose the internal modifyFilledTakerAmount function for testing
|
||||||
function exposed_modifyFilledTakerAmount(
|
function exposed_modifyFilledTakerAmount(
|
||||||
bytes memory bebopCalldata,
|
bytes memory bebopCalldata,
|
||||||
uint256 givenAmount,
|
uint256 givenAmount,
|
||||||
uint256 originalFilledTakerAmount
|
uint256 originalFilledTakerAmount,
|
||||||
|
uint8 partialFillOffset
|
||||||
) external pure returns (bytes memory) {
|
) external pure returns (bytes memory) {
|
||||||
return _modifyFilledTakerAmount(
|
return _modifyFilledTakerAmount(
|
||||||
bebopCalldata, givenAmount, originalFilledTakerAmount
|
bebopCalldata, givenAmount, originalFilledTakerAmount, partialFillOffset
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +100,7 @@ contract BebopExecutorHarness is BebopExecutor, Test {
|
|||||||
,
|
,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
bytes memory bebopCalldata,
|
bytes memory bebopCalldata,
|
||||||
|
, // partialFillOffset not needed in test harness
|
||||||
uint256 originalFilledTakerAmount,
|
uint256 originalFilledTakerAmount,
|
||||||
) = _decodeData(data);
|
) = _decodeData(data);
|
||||||
|
|
||||||
@@ -133,8 +139,9 @@ contract BebopExecutorHarness is BebopExecutor, Test {
|
|||||||
expiry = order.expiry;
|
expiry = order.expiry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inline the simple logic since _getActualFilledTakerAmount was removed
|
||||||
uint256 actualFilledTakerAmount =
|
uint256 actualFilledTakerAmount =
|
||||||
_getActualFilledTakerAmount(givenAmount, originalFilledTakerAmount);
|
originalFilledTakerAmount > givenAmount ? givenAmount : originalFilledTakerAmount;
|
||||||
|
|
||||||
// For testing: transfer tokens from executor to taker address
|
// For testing: transfer tokens from executor to taker address
|
||||||
// This simulates the taker having the tokens with approval
|
// This simulates the taker having the tokens with approval
|
||||||
@@ -164,6 +171,7 @@ contract BebopExecutorHarness is BebopExecutor, Test {
|
|||||||
uint256 currentTimestamp = block.timestamp;
|
uint256 currentTimestamp = block.timestamp;
|
||||||
vm.warp(expiry - 1); // Set timestamp to just before expiry
|
vm.warp(expiry - 1); // Set timestamp to just before expiry
|
||||||
|
|
||||||
|
// Call the parent's internal _swap function
|
||||||
calculatedAmount = _swap(givenAmount, data);
|
calculatedAmount = _swap(givenAmount, data);
|
||||||
|
|
||||||
// Restore original timestamp
|
// Restore original timestamp
|
||||||
|
|||||||
@@ -670,8 +670,9 @@ impl BebopSwapEncoder {
|
|||||||
|
|
||||||
/// Extract the total taker amount from a Bebop aggregate order calldata
|
/// Extract the total taker amount from a Bebop aggregate order calldata
|
||||||
fn extract_aggregate_taker_amount(bebop_calldata: &[u8]) -> Option<U256> {
|
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
|
// Minimum size check: 4 (selector) + 32 (order offset) + 32 (signatures offset) + 32
|
||||||
// This ensures we can safely read the offset to the order parameter
|
// (filledTakerAmount) = 100 bytes This ensures we can safely read the offset to the order
|
||||||
|
// parameter
|
||||||
if bebop_calldata.len() < 100 {
|
if bebop_calldata.len() < 100 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -803,15 +804,18 @@ impl SwapEncoder for BebopSwapEncoder {
|
|||||||
EncodingError::InvalidInput("Bebop swaps require user_data with calldata".to_string())
|
EncodingError::InvalidInput("Bebop swaps require user_data with calldata".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// User data should contain the complete Bebop calldata
|
// User data format: partialFillOffset (1 byte) + bebop_calldata
|
||||||
if user_data.len() < 4 {
|
if user_data.len() < 5 {
|
||||||
return Err(EncodingError::InvalidInput(
|
return Err(EncodingError::InvalidInput(
|
||||||
"User data too short to contain 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];
|
||||||
|
|
||||||
// The calldata should be for either swapSingle or swapAggregate
|
// The calldata should be for either swapSingle or swapAggregate
|
||||||
let bebop_calldata = user_data.to_vec();
|
let bebop_calldata = user_data[1..].to_vec();
|
||||||
|
|
||||||
// Extract the original filledTakerAmount from the calldata
|
// Extract the original filledTakerAmount from the calldata
|
||||||
// Both swapSingle and swapAggregate have filledTakerAmount as the last parameter
|
// Both swapSingle and swapAggregate have filledTakerAmount as the last parameter
|
||||||
@@ -862,13 +866,15 @@ impl SwapEncoder for BebopSwapEncoder {
|
|||||||
|
|
||||||
// Encode packed data for the executor
|
// Encode packed data for the executor
|
||||||
// Format: token_in | token_out | transfer_type | bebop_calldata_length |
|
// Format: token_in | token_out | transfer_type | bebop_calldata_length |
|
||||||
// bebop_calldata | original_filled_taker_amount | approval_needed
|
// bebop_calldata | partial_fill_offset | original_filled_taker_amount |
|
||||||
|
// approval_needed
|
||||||
let args = (
|
let args = (
|
||||||
token_in,
|
token_in,
|
||||||
token_out,
|
token_out,
|
||||||
(encoding_context.transfer_type as u8).to_be_bytes(),
|
(encoding_context.transfer_type as u8).to_be_bytes(),
|
||||||
(bebop_calldata.len() as u32).to_be_bytes(),
|
(bebop_calldata.len() as u32).to_be_bytes(),
|
||||||
&bebop_calldata[..],
|
&bebop_calldata[..],
|
||||||
|
partial_fill_offset.to_be_bytes(),
|
||||||
original_filled_taker_amount.to_be_bytes::<32>(),
|
original_filled_taker_amount.to_be_bytes::<32>(),
|
||||||
(approval_needed as u8).to_be_bytes(),
|
(approval_needed as u8).to_be_bytes(),
|
||||||
);
|
);
|
||||||
@@ -1953,6 +1959,10 @@ mod tests {
|
|||||||
+ "0000000000000000000000000000000000000000000000000000000bebc200" // filledTakerAmount = 200000000
|
+ "0000000000000000000000000000000000000000000000000000000bebc200" // filledTakerAmount = 200000000
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
// Prepend the partialFillOffset (12 for swapSingle)
|
||||||
|
let mut user_data = vec![12u8]; // partialFillOffset = 12
|
||||||
|
user_data.extend_from_slice(&bebop_calldata);
|
||||||
|
|
||||||
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"),
|
||||||
@@ -1968,7 +1978,7 @@ mod tests {
|
|||||||
token_in: token_in.clone(),
|
token_in: token_in.clone(),
|
||||||
token_out: token_out.clone(),
|
token_out: token_out.clone(),
|
||||||
split: 0f64,
|
split: 0f64,
|
||||||
user_data: Some(Bytes::from(bebop_calldata.clone())),
|
user_data: Some(Bytes::from(user_data)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let encoding_context = EncodingContext {
|
let encoding_context = EncodingContext {
|
||||||
@@ -2008,7 +2018,75 @@ mod tests {
|
|||||||
hex_swap.contains("0000000000000000000000000000000000000000000000000000000bebc200")
|
hex_swap.contains("0000000000000000000000000000000000000000000000000000000bebc200")
|
||||||
); // 200000000 in hex
|
); // 200000000 in hex
|
||||||
|
|
||||||
|
// Verify the partialFillOffset byte appears after the bebop calldata
|
||||||
|
// Look for the pattern: end of bebop calldata (bebc200) followed by offset (0c)
|
||||||
|
assert!(hex_swap.contains("bebc2000c")); // filledTakerAmount followed by offset byte
|
||||||
|
|
||||||
write_calldata_to_file("test_encode_bebop_calldata_forwarding", hex_swap.as_str());
|
write_calldata_to_file("test_encode_bebop_calldata_forwarding", hex_swap.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_bebop_with_aggregate_offset() {
|
||||||
|
use alloy::hex;
|
||||||
|
|
||||||
|
// Create mock Bebop calldata for swapAggregate
|
||||||
|
let bebop_calldata = hex::decode(
|
||||||
|
"a2f74893".to_owned() // swapAggregate selector
|
||||||
|
+ "0000000000000000000000000000000000000000000000000000000000000001" // mock order data
|
||||||
|
+ "0000000000000000000000000000000000000000000000000000000000000002" // mock signature data
|
||||||
|
+ "00000000000000000000000000000000000000000000000000000000003d0900" // filledTakerAmount
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
// Prepend the partialFillOffset (2 for swapAggregate)
|
||||||
|
let mut user_data = vec![2u8]; // partialFillOffset = 2 for aggregate
|
||||||
|
user_data.extend_from_slice(&bebop_calldata);
|
||||||
|
|
||||||
|
let bebop_component = ProtocolComponent {
|
||||||
|
id: String::from("bebop-rfq"),
|
||||||
|
protocol_system: String::from("rfq:bebop"),
|
||||||
|
static_attributes: HashMap::new(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let token_in = Bytes::from("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); // WETH
|
||||||
|
let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC
|
||||||
|
|
||||||
|
let swap = Swap {
|
||||||
|
component: bebop_component,
|
||||||
|
token_in: token_in.clone(),
|
||||||
|
token_out: token_out.clone(),
|
||||||
|
split: 0f64,
|
||||||
|
user_data: Some(Bytes::from(user_data)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let encoding_context = EncodingContext {
|
||||||
|
receiver: Bytes::from("0xc5564C13A157E6240659fb81882A28091add8670"),
|
||||||
|
exact_out: false,
|
||||||
|
router_address: Some(Bytes::zero(20)),
|
||||||
|
group_token_in: token_in.clone(),
|
||||||
|
group_token_out: token_out.clone(),
|
||||||
|
transfer_type: TransferType::Transfer,
|
||||||
|
};
|
||||||
|
|
||||||
|
let encoder = BebopSwapEncoder::new(
|
||||||
|
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||||
|
TychoCoreChain::Ethereum.into(),
|
||||||
|
Some(HashMap::from([(
|
||||||
|
"bebop_settlement_address".to_string(),
|
||||||
|
"0xbbbbbBB520d69a9775E85b458C58c648259FAD5F".to_string(),
|
||||||
|
)])),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let encoded_swap = encoder
|
||||||
|
.encode_swap(&swap, &encoding_context)
|
||||||
|
.unwrap();
|
||||||
|
let hex_swap = encode(&encoded_swap);
|
||||||
|
|
||||||
|
// Verify the partialFillOffset byte (02 = 2 in hex) appears after the bebop calldata
|
||||||
|
assert!(hex_swap.contains("3d090002")); // filledTakerAmount followed by offset byte
|
||||||
|
|
||||||
|
write_calldata_to_file("test_encode_bebop_aggregate_offset", hex_swap.as_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user