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
|
||||
function _decodeData(bytes calldata data)
|
||||
internal
|
||||
@@ -349,4 +194,165 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
|
||||
// Extract approval flag
|
||||
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 {
|
||||
// The calldata swaps 200 USDC for ONDO
|
||||
// The receiver in the order is 0xc5564C13A157E6240659fb81882A28091add8670
|
||||
address orderReceiver = 0xc5564C13A157E6240659fb81882A28091add8670;
|
||||
address orderTaker = 0xc5564C13A157E6240659fb81882A28091add8670;
|
||||
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
|
||||
|
||||
// Fund the maker with ONDO and approve settlement
|
||||
@@ -308,7 +308,9 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
||||
vm.prank(maker);
|
||||
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);
|
||||
|
||||
// Load calldata from file
|
||||
@@ -318,9 +320,10 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
// 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");
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(ondoReceived, expAmountOut);
|
||||
assertEq(
|
||||
IERC20(USDC_ADDR).balanceOf(tychoRouterAddr),
|
||||
0,
|
||||
@@ -363,7 +366,7 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
||||
uint256 finalBalance = IERC20(USDC_ADDR).balanceOf(orderTaker);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(finalBalance, expAmountOut);
|
||||
assertEq(address(tychoRouterAddr).balance, 0, "ETH left in router");
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user