chore: create internal _swap method for harness usage

This commit is contained in:
pedrobergamini
2025-08-04 14:51:58 -03:00
parent 3c7a846ff4
commit 7ef587575a
2 changed files with 69 additions and 116 deletions

View File

@@ -97,6 +97,13 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
virtual virtual
override override
returns (uint256 calculatedAmount) returns (uint256 calculatedAmount)
{
calculatedAmount = _swap(givenAmount, data);
}
function _swap(uint256 givenAmount, bytes calldata data)
internal
returns (uint256 calculatedAmount)
{ {
// Decode the packed data // Decode the packed data
( (

View File

@@ -18,12 +18,10 @@ contract BebopExecutorHarness is BebopExecutor, Test {
returns ( returns (
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
RestrictTransferFrom.TransferType transferType, TransferType transferType,
BebopExecutor.OrderType orderType, bytes memory bebopCalldata,
uint256 filledTakerAmount, uint256 originalFilledTakerAmount,
bytes memory quoteData, bool approvalNeeded
bytes memory makerSignaturesData,
bool // approvalNeeded - unused in test harness
) )
{ {
return _decodeData(data); return _decodeData(data);
@@ -32,32 +30,65 @@ contract BebopExecutorHarness is BebopExecutor, Test {
// Expose the internal getActualFilledTakerAmount function for testing // Expose the internal getActualFilledTakerAmount function for testing
function exposed_getActualFilledTakerAmount( function exposed_getActualFilledTakerAmount(
uint256 givenAmount, uint256 givenAmount,
uint256 orderTakerAmount,
uint256 filledTakerAmount uint256 filledTakerAmount
) external pure returns (uint256 actualFilledTakerAmount) { ) external pure returns (uint256 actualFilledTakerAmount) {
return _getActualFilledTakerAmount( return _getActualFilledTakerAmount(givenAmount, filledTakerAmount);
givenAmount, orderTakerAmount, filledTakerAmount }
// Expose the internal modifyFilledTakerAmount function for testing
function exposed_modifyFilledTakerAmount(
bytes memory bebopCalldata,
uint256 givenAmount,
uint256 originalFilledTakerAmount
) external pure returns (bytes memory) {
return _modifyFilledTakerAmount(
bebopCalldata, givenAmount, originalFilledTakerAmount
); );
} }
// Override to prank the taker address before calling the real settlement // Override to prank the taker address before calling the real settlement
function _executeSingleRFQ( function swap(uint256 givenAmount, bytes calldata data)
address tokenIn, external
address tokenOut, payable
TransferType transferType, override
uint256 givenAmount, returns (uint256 calculatedAmount)
uint256 filledTakerAmount, {
bytes memory quoteData, // Decode the packed data
bytes memory makerSignaturesData, (
bool approvalNeeded address tokenIn,
) internal virtual override returns (uint256 amountOut) { ,
// Decode the order from quoteData TransferType transferType,
IBebopSettlement.Single memory order = bytes memory bebopCalldata,
abi.decode(quoteData, (IBebopSettlement.Single)); uint256 originalFilledTakerAmount,
) = _decodeData(data);
uint256 actualFilledTakerAmount = _getActualFilledTakerAmount( uint256 actualFilledTakerAmount =
givenAmount, order.taker_amount, filledTakerAmount _getActualFilledTakerAmount(givenAmount, originalFilledTakerAmount);
);
// Extract taker address and expiry from bebop calldata
address takerAddress;
uint256 expiry;
// Both swapSingle and swapAggregate have the same order structure position
// Read the offset to the order struct (first parameter after selector)
uint256 orderOffset;
assembly {
orderOffset := mload(add(bebopCalldata, 36)) // 4 (selector) + 32 (offset)
}
// Navigate to the order struct data
// Order struct starts at: 4 (selector) + orderOffset
uint256 orderDataStart = 4 + orderOffset;
// Extract expiry (first field of the order struct)
assembly {
expiry := mload(add(bebopCalldata, add(orderDataStart, 32)))
}
// Extract taker_address (second field of the order struct)
assembly {
takerAddress := mload(add(bebopCalldata, add(orderDataStart, 64)))
}
// 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
@@ -65,115 +96,30 @@ contract BebopExecutorHarness is BebopExecutor, Test {
_transfer( _transfer(
address(this), transferType, tokenIn, actualFilledTakerAmount address(this), transferType, tokenIn, actualFilledTakerAmount
); );
IERC20(tokenIn).safeTransfer( IERC20(tokenIn).safeTransfer(takerAddress, actualFilledTakerAmount);
order.taker_address, actualFilledTakerAmount
);
// Approve settlement from taker's perspective // Approve settlement from taker's perspective
// Stop any existing prank first // Stop any existing prank first
vm.stopPrank(); vm.stopPrank();
vm.startPrank(order.taker_address); vm.startPrank(takerAddress);
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max); IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
vm.stopPrank(); vm.stopPrank();
} else { } else {
vm.stopPrank(); vm.stopPrank();
// For native ETH, send it to the taker address // For native ETH, send it to the taker address
payable(order.taker_address).transfer(actualFilledTakerAmount); payable(takerAddress).transfer(actualFilledTakerAmount);
} }
// IMPORTANT: Prank as the taker address to pass the settlement validation // IMPORTANT: Prank as the taker address to pass the settlement validation
vm.stopPrank(); vm.stopPrank();
vm.startPrank(order.taker_address); vm.startPrank(takerAddress);
// Set block timestamp to ensure order is valid regardless of fork block // Set block timestamp to ensure order is valid regardless of fork block
uint256 currentTimestamp = block.timestamp; uint256 currentTimestamp = block.timestamp;
vm.warp(order.expiry - 1); // Set timestamp to just before expiry vm.warp(expiry - 1); // Set timestamp to just before expiry
// Execute the single swap, let's test the actual settlement logic // Execute the single swap, let's test the actual settlement logic
amountOut = super._executeSingleRFQ( calculatedAmount = _swap(givenAmount, data);
tokenIn,
tokenOut,
TransferType.None, // We set transfer type to none for testing in order to keep the taker's balance unchanged as it will execute the swap
givenAmount,
filledTakerAmount,
quoteData,
makerSignaturesData,
approvalNeeded
);
// Restore original timestamp
vm.warp(currentTimestamp);
vm.stopPrank();
}
// Override to execute aggregate orders through the real settlement
function _executeAggregateRFQ(
address tokenIn,
address tokenOut,
TransferType transferType,
uint256 givenAmount,
uint256 filledTakerAmount,
bytes memory quoteData,
bytes memory makerSignaturesData,
bool approvalNeeded
) internal virtual override returns (uint256 amountOut) {
// Decode the Aggregate order
IBebopSettlement.Aggregate memory order =
abi.decode(quoteData, (IBebopSettlement.Aggregate));
// For aggregate orders, calculate total taker amount across all amounts of the 2D array
uint256 totalTakerAmount = 0;
for (uint256 i = 0; i < order.taker_amounts.length; i++) {
for (uint256 j = 0; j < order.taker_amounts[i].length; j++) {
totalTakerAmount += order.taker_amounts[i][j];
}
}
uint256 actualFilledTakerAmount = _getActualFilledTakerAmount(
givenAmount, totalTakerAmount, filledTakerAmount
);
// For testing: transfer tokens from executor to taker address
// This simulates the taker having the tokens with approval
if (tokenIn != address(0)) {
_transfer(
address(this), transferType, tokenIn, actualFilledTakerAmount
);
IERC20(tokenIn).safeTransfer(
order.taker_address, actualFilledTakerAmount
);
// Approve settlement from taker's perspective
// Stop any existing prank first
vm.stopPrank();
vm.startPrank(order.taker_address);
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
vm.stopPrank();
} else {
vm.stopPrank();
// For native ETH, send it to the taker address
payable(order.taker_address).transfer(actualFilledTakerAmount);
}
// IMPORTANT: Prank as the taker address to pass the settlement validation
vm.stopPrank();
vm.startPrank(order.taker_address);
// Set block timestamp to ensure order is valid regardless of fork block
uint256 currentTimestamp = block.timestamp;
vm.warp(order.expiry - 1); // Set timestamp to just before expiry
// Execute the aggregate swap, let's test the actual settlement logic
amountOut = super._executeAggregateRFQ(
tokenIn,
tokenOut,
TransferType.None, // We set transfer type to none for testing in order to keep the taker's balance unchanged as it will execute the swap
givenAmount,
filledTakerAmount,
quoteData,
makerSignaturesData,
approvalNeeded
);
// Restore original timestamp // Restore original timestamp
vm.warp(currentTimestamp); vm.warp(currentTimestamp);