chore: improve test assertion and function ordering
This commit is contained in:
@@ -140,161 +140,6 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Determines the actual taker amount to be filled for a Bebop order
|
|
||||||
* @notice This function handles two scenarios:
|
|
||||||
* 1. When filledTakerAmount is 0: Uses the full order amount if givenAmount is sufficient,
|
|
||||||
* otherwise returns givenAmount to partially fill the order
|
|
||||||
* 2. When filledTakerAmount > 0: Caps the fill at the minimum of filledTakerAmount and givenAmount
|
|
||||||
* to ensure we don't attempt to fill more than available
|
|
||||||
* @param givenAmount The amount of tokens available from the router for this swap
|
|
||||||
* @param orderTakerAmount The full taker amount specified in the Bebop order
|
|
||||||
* @param filledTakerAmount The requested fill amount (0 means fill entire order)
|
|
||||||
* @return actualFilledTakerAmount The amount that will actually be filled
|
|
||||||
*/
|
|
||||||
function _getActualFilledTakerAmount(
|
|
||||||
uint256 givenAmount,
|
|
||||||
uint256 orderTakerAmount,
|
|
||||||
uint256 filledTakerAmount
|
|
||||||
) internal pure returns (uint256 actualFilledTakerAmount) {
|
|
||||||
actualFilledTakerAmount = filledTakerAmount == 0
|
|
||||||
? (givenAmount >= orderTakerAmount ? orderTakerAmount : givenAmount)
|
|
||||||
: (filledTakerAmount > givenAmount ? givenAmount : filledTakerAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Executes a Single RFQ swap through Bebop settlement
|
|
||||||
function _executeSingleRFQ(
|
|
||||||
address tokenIn,
|
|
||||||
address tokenOut,
|
|
||||||
TransferType transferType,
|
|
||||||
uint256 givenAmount,
|
|
||||||
uint256 filledTakerAmount,
|
|
||||||
bytes memory quoteData,
|
|
||||||
bytes memory makerSignaturesData,
|
|
||||||
bool approvalNeeded
|
|
||||||
) internal virtual returns (uint256 amountOut) {
|
|
||||||
// Decode the order from quoteData
|
|
||||||
IBebopSettlement.Single memory order =
|
|
||||||
abi.decode(quoteData, (IBebopSettlement.Single));
|
|
||||||
|
|
||||||
// Decode the MakerSignature array (should contain exactly 1 signature for Single orders)
|
|
||||||
IBebopSettlement.MakerSignature[] memory signatures =
|
|
||||||
abi.decode(makerSignaturesData, (IBebopSettlement.MakerSignature[]));
|
|
||||||
|
|
||||||
// Validate that there is exactly one maker signature
|
|
||||||
if (signatures.length != 1) {
|
|
||||||
revert BebopExecutor__InvalidInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the maker signature from the first and only element of the array
|
|
||||||
IBebopSettlement.MakerSignature memory sig = signatures[0];
|
|
||||||
|
|
||||||
uint256 actualFilledTakerAmount = _getActualFilledTakerAmount(
|
|
||||||
givenAmount, order.taker_amount, filledTakerAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tokenIn != address(0)) {
|
|
||||||
// Transfer tokens to executor
|
|
||||||
_transfer(address(this), transferType, tokenIn, givenAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Approve Bebop settlement to spend tokens if needed
|
|
||||||
if (approvalNeeded) {
|
|
||||||
// slither-disable-next-line unused-return
|
|
||||||
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record balances before swap to calculate amountOut
|
|
||||||
uint256 balanceBefore = tokenOut == address(0)
|
|
||||||
? order.receiver.balance
|
|
||||||
: IERC20(tokenOut).balanceOf(order.receiver);
|
|
||||||
|
|
||||||
// Execute the swap with ETH value if needed
|
|
||||||
uint256 ethValue = tokenIn == address(0) ? actualFilledTakerAmount : 0;
|
|
||||||
|
|
||||||
// Use swapSingle since tokens are in the executor with approval
|
|
||||||
// slither-disable-next-line arbitrary-send-eth
|
|
||||||
IBebopSettlement(bebopSettlement).swapSingle{value: ethValue}(
|
|
||||||
order, sig, actualFilledTakerAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Calculate actual amount received
|
|
||||||
uint256 balanceAfter = tokenOut == address(0)
|
|
||||||
? order.receiver.balance
|
|
||||||
: IERC20(tokenOut).balanceOf(order.receiver);
|
|
||||||
|
|
||||||
amountOut = balanceAfter - balanceBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Executes an Aggregate RFQ swap through Bebop settlement
|
|
||||||
function _executeAggregateRFQ(
|
|
||||||
address tokenIn,
|
|
||||||
address tokenOut,
|
|
||||||
TransferType transferType,
|
|
||||||
uint256 givenAmount,
|
|
||||||
uint256 filledTakerAmount,
|
|
||||||
bytes memory quoteData,
|
|
||||||
bytes memory makerSignaturesData,
|
|
||||||
bool approvalNeeded
|
|
||||||
) internal virtual returns (uint256 amountOut) {
|
|
||||||
// Decode the Aggregate order
|
|
||||||
IBebopSettlement.Aggregate memory order =
|
|
||||||
abi.decode(quoteData, (IBebopSettlement.Aggregate));
|
|
||||||
|
|
||||||
// Decode the MakerSignature array (can contain multiple signatures for Aggregate orders)
|
|
||||||
IBebopSettlement.MakerSignature[] memory signatures =
|
|
||||||
abi.decode(makerSignaturesData, (IBebopSettlement.MakerSignature[]));
|
|
||||||
|
|
||||||
// Aggregate orders should have at least one signature
|
|
||||||
if (signatures.length == 0) {
|
|
||||||
revert BebopExecutor__InvalidInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tokenIn != address(0)) {
|
|
||||||
// Transfer tokens to executor
|
|
||||||
_transfer(address(this), transferType, tokenIn, givenAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Approve Bebop settlement to spend tokens if needed
|
|
||||||
if (approvalNeeded) {
|
|
||||||
// slither-disable-next-line unused-return
|
|
||||||
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record balance before swap
|
|
||||||
uint256 balanceBefore = tokenOut == address(0)
|
|
||||||
? order.receiver.balance
|
|
||||||
: IERC20(tokenOut).balanceOf(order.receiver);
|
|
||||||
|
|
||||||
// Execute the swap
|
|
||||||
uint256 ethValue = tokenIn == address(0) ? actualFilledTakerAmount : 0;
|
|
||||||
|
|
||||||
// Execute the swap (tokens are in executor, approved to settlement)
|
|
||||||
// slither-disable-next-line arbitrary-send-eth
|
|
||||||
IBebopSettlement(bebopSettlement).swapAggregate{value: ethValue}(
|
|
||||||
order, signatures, actualFilledTakerAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Calculate actual amount received
|
|
||||||
uint256 balanceAfter = tokenOut == address(0)
|
|
||||||
? order.receiver.balance
|
|
||||||
: IERC20(tokenOut).balanceOf(order.receiver);
|
|
||||||
|
|
||||||
amountOut = balanceAfter - balanceBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Decodes the packed calldata
|
/// @dev Decodes the packed calldata
|
||||||
function _decodeData(bytes calldata data)
|
function _decodeData(bytes calldata data)
|
||||||
internal
|
internal
|
||||||
@@ -349,4 +194,165 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
|||||||
// Extract approval flag
|
// Extract approval flag
|
||||||
approvalNeeded = data[82 + quoteDataLength + makerSignaturesLength] != 0;
|
approvalNeeded = data[82 + quoteDataLength + makerSignaturesLength] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Executes a Single RFQ swap through Bebop settlement
|
||||||
|
function _executeSingleRFQ(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
TransferType transferType,
|
||||||
|
uint256 givenAmount,
|
||||||
|
uint256 filledTakerAmount,
|
||||||
|
bytes memory quoteData,
|
||||||
|
bytes memory makerSignaturesData,
|
||||||
|
bool approvalNeeded
|
||||||
|
) internal virtual returns (uint256 amountOut) {
|
||||||
|
// Decode the order from quoteData
|
||||||
|
IBebopSettlement.Single memory order =
|
||||||
|
abi.decode(quoteData, (IBebopSettlement.Single));
|
||||||
|
|
||||||
|
// Decode the MakerSignature array (should contain exactly 1 signature for Single orders)
|
||||||
|
IBebopSettlement.MakerSignature[] memory signatures =
|
||||||
|
abi.decode(makerSignaturesData, (IBebopSettlement.MakerSignature[]));
|
||||||
|
|
||||||
|
// Validate that there is exactly one maker signature
|
||||||
|
if (signatures.length != 1) {
|
||||||
|
revert BebopExecutor__InvalidInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the maker signature from the first and only element of the array
|
||||||
|
IBebopSettlement.MakerSignature memory sig = signatures[0];
|
||||||
|
|
||||||
|
uint256 actualFilledTakerAmount = _getActualFilledTakerAmount(
|
||||||
|
givenAmount, order.taker_amount, filledTakerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tokenIn != address(0)) {
|
||||||
|
// Transfer tokens to executor
|
||||||
|
_transfer(address(this), transferType, tokenIn, givenAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approve Bebop settlement to spend tokens if needed
|
||||||
|
if (approvalNeeded) {
|
||||||
|
// slither-disable-next-line unused-return
|
||||||
|
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record balances before swap to calculate amountOut
|
||||||
|
uint256 balanceBefore = _balanceOf(tokenOut, order.receiver);
|
||||||
|
|
||||||
|
// Execute the swap with ETH value if needed
|
||||||
|
uint256 ethValue = tokenIn == address(0) ? actualFilledTakerAmount : 0;
|
||||||
|
|
||||||
|
// Use swapSingle since tokens are in the executor with approval
|
||||||
|
// slither-disable-next-line arbitrary-send-eth
|
||||||
|
IBebopSettlement(bebopSettlement).swapSingle{value: ethValue}(
|
||||||
|
order, sig, actualFilledTakerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate actual amount received
|
||||||
|
uint256 balanceAfter = _balanceOf(tokenOut, order.receiver);
|
||||||
|
|
||||||
|
amountOut = balanceAfter - balanceBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Executes an Aggregate RFQ swap through Bebop settlement
|
||||||
|
function _executeAggregateRFQ(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
TransferType transferType,
|
||||||
|
uint256 givenAmount,
|
||||||
|
uint256 filledTakerAmount,
|
||||||
|
bytes memory quoteData,
|
||||||
|
bytes memory makerSignaturesData,
|
||||||
|
bool approvalNeeded
|
||||||
|
) internal virtual returns (uint256 amountOut) {
|
||||||
|
// Decode the Aggregate order
|
||||||
|
IBebopSettlement.Aggregate memory order =
|
||||||
|
abi.decode(quoteData, (IBebopSettlement.Aggregate));
|
||||||
|
|
||||||
|
// Decode the MakerSignature array (can contain multiple signatures for Aggregate orders)
|
||||||
|
IBebopSettlement.MakerSignature[] memory signatures =
|
||||||
|
abi.decode(makerSignaturesData, (IBebopSettlement.MakerSignature[]));
|
||||||
|
|
||||||
|
// Aggregate orders should have at least one signature
|
||||||
|
if (signatures.length == 0) {
|
||||||
|
revert BebopExecutor__InvalidInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tokenIn != address(0)) {
|
||||||
|
// Transfer tokens to executor
|
||||||
|
_transfer(address(this), transferType, tokenIn, givenAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approve Bebop settlement to spend tokens if needed
|
||||||
|
if (approvalNeeded) {
|
||||||
|
// slither-disable-next-line unused-return
|
||||||
|
IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record balance before swap
|
||||||
|
uint256 balanceBefore = _balanceOf(tokenOut, order.receiver);
|
||||||
|
|
||||||
|
// Execute the swap
|
||||||
|
uint256 ethValue = tokenIn == address(0) ? actualFilledTakerAmount : 0;
|
||||||
|
|
||||||
|
// Execute the swap (tokens are in executor, approved to settlement)
|
||||||
|
// slither-disable-next-line arbitrary-send-eth
|
||||||
|
IBebopSettlement(bebopSettlement).swapAggregate{value: ethValue}(
|
||||||
|
order, signatures, actualFilledTakerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate actual amount received
|
||||||
|
uint256 balanceAfter = _balanceOf(tokenOut, order.receiver);
|
||||||
|
|
||||||
|
amountOut = balanceAfter - balanceBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Determines the actual taker amount to be filled for a Bebop order
|
||||||
|
* @notice This function handles two scenarios:
|
||||||
|
* 1. When filledTakerAmount is 0: Uses the full order amount if givenAmount is sufficient,
|
||||||
|
* otherwise returns givenAmount to partially fill the order
|
||||||
|
* 2. When filledTakerAmount > 0: Caps the fill at the minimum of filledTakerAmount and givenAmount
|
||||||
|
* to ensure we don't attempt to fill more than available
|
||||||
|
* @param givenAmount The amount of tokens available from the router for this swap
|
||||||
|
* @param orderTakerAmount The full taker amount specified in the Bebop order
|
||||||
|
* @param filledTakerAmount The requested fill amount (0 means fill entire order)
|
||||||
|
* @return actualFilledTakerAmount The amount that will actually be filled
|
||||||
|
*/
|
||||||
|
function _getActualFilledTakerAmount(
|
||||||
|
uint256 givenAmount,
|
||||||
|
uint256 orderTakerAmount,
|
||||||
|
uint256 filledTakerAmount
|
||||||
|
) internal pure returns (uint256 actualFilledTakerAmount) {
|
||||||
|
actualFilledTakerAmount = filledTakerAmount == 0
|
||||||
|
? (givenAmount >= orderTakerAmount ? orderTakerAmount : givenAmount)
|
||||||
|
: (filledTakerAmount > givenAmount ? givenAmount : filledTakerAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns the balance of a token or ETH for an account
|
||||||
|
/// @param token The token address, or address(0) for ETH
|
||||||
|
/// @param account The account to get the balance of
|
||||||
|
/// @return balance The balance of the token or ETH for the account
|
||||||
|
function _balanceOf(address token, address account)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return token == address(0)
|
||||||
|
? account.balance
|
||||||
|
: IERC20(token).balanceOf(account);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,9 +298,9 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
|||||||
function testSingleBebopIntegration() public {
|
function testSingleBebopIntegration() public {
|
||||||
// The calldata swaps 200 USDC for ONDO
|
// The calldata swaps 200 USDC for ONDO
|
||||||
// The receiver in the order is 0xc5564C13A157E6240659fb81882A28091add8670
|
// The receiver in the order is 0xc5564C13A157E6240659fb81882A28091add8670
|
||||||
address orderReceiver = 0xc5564C13A157E6240659fb81882A28091add8670;
|
address orderTaker = 0xc5564C13A157E6240659fb81882A28091add8670;
|
||||||
address maker = 0xCe79b081c0c924cb67848723ed3057234d10FC6b;
|
address maker = 0xCe79b081c0c924cb67848723ed3057234d10FC6b;
|
||||||
deal(USDC_ADDR, ALICE, 200 * 10 ** 6); // 200 USDC
|
deal(USDC_ADDR, orderTaker, 200 * 10 ** 6); // 200 USDC
|
||||||
uint256 expAmountOut = 237212396774431060000; // Expected ONDO amount from calldata
|
uint256 expAmountOut = 237212396774431060000; // Expected ONDO amount from calldata
|
||||||
|
|
||||||
// Fund the maker with ONDO and approve settlement
|
// Fund the maker with ONDO and approve settlement
|
||||||
@@ -308,7 +308,9 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
|||||||
vm.prank(maker);
|
vm.prank(maker);
|
||||||
IERC20(ONDO_ADDR).approve(BEBOP_SETTLEMENT, expAmountOut);
|
IERC20(ONDO_ADDR).approve(BEBOP_SETTLEMENT, expAmountOut);
|
||||||
|
|
||||||
vm.startPrank(ALICE);
|
uint256 ondoBefore = IERC20(ONDO_ADDR).balanceOf(orderTaker);
|
||||||
|
|
||||||
|
vm.startPrank(orderTaker);
|
||||||
IERC20(USDC_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
IERC20(USDC_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
||||||
|
|
||||||
// Load calldata from file
|
// Load calldata from file
|
||||||
@@ -318,9 +320,10 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
|||||||
(bool success,) = tychoRouterAddr.call(callData);
|
(bool success,) = tychoRouterAddr.call(callData);
|
||||||
|
|
||||||
// Check the receiver's balance (not ALICE, since the order specifies a different receiver)
|
// Check the receiver's balance (not ALICE, since the order specifies a different receiver)
|
||||||
uint256 finalBalance = IERC20(ONDO_ADDR).balanceOf(orderReceiver);
|
uint256 ondoReceived =
|
||||||
|
IERC20(ONDO_ADDR).balanceOf(orderTaker) - ondoBefore;
|
||||||
assertTrue(success, "Call Failed");
|
assertTrue(success, "Call Failed");
|
||||||
assertGe(finalBalance, expAmountOut);
|
assertEq(ondoReceived, expAmountOut);
|
||||||
assertEq(
|
assertEq(
|
||||||
IERC20(USDC_ADDR).balanceOf(tychoRouterAddr),
|
IERC20(USDC_ADDR).balanceOf(tychoRouterAddr),
|
||||||
0,
|
0,
|
||||||
@@ -363,7 +366,7 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
|||||||
uint256 finalBalance = IERC20(USDC_ADDR).balanceOf(orderTaker);
|
uint256 finalBalance = IERC20(USDC_ADDR).balanceOf(orderTaker);
|
||||||
|
|
||||||
assertTrue(success, "Call Failed");
|
assertTrue(success, "Call Failed");
|
||||||
assertGe(finalBalance, expAmountOut);
|
assertEq(finalBalance, expAmountOut);
|
||||||
assertEq(address(tychoRouterAddr).balance, 0, "ETH left in router");
|
assertEq(address(tychoRouterAddr).balance, 0, "ETH left in router");
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user