feat: support multi and aggregate orders

This commit is contained in:
pedrobergamini
2025-06-04 19:41:13 -03:00
parent 946c4391e8
commit ad0a9991da
7 changed files with 684 additions and 41 deletions

View File

@@ -103,17 +103,14 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
/// @notice Bebop-specific errors
error BebopExecutor__SettlementFailed();
error BebopExecutor__UnsupportedOrderType(uint8 orderType);
error BebopExecutor__InvalidDataLength();
/// @notice The Bebop settlement contract address
address public immutable bebopSettlement;
constructor(
address _bebopSettlement,
address _permit2
) RestrictTransferFrom(_permit2) {
constructor(address _bebopSettlement, address _permit2)
RestrictTransferFrom(_permit2)
{
bebopSettlement = _bebopSettlement;
}
@@ -156,8 +153,24 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
signatureType,
signature
);
} else {
revert BebopExecutor__UnsupportedOrderType(uint8(orderType));
} else if (orderType == OrderType.Multi) {
calculatedAmount = _executeMultiRFQ(
tokenIn,
tokenOut,
givenAmount,
quoteData,
signatureType,
signature
);
} else if (orderType == OrderType.Aggregate) {
calculatedAmount = _executeAggregateRFQ(
tokenIn,
tokenOut,
givenAmount,
quoteData,
signatureType,
signature
);
}
}
@@ -210,6 +223,184 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
amountOut = balanceAfter - balanceBefore;
}
/// @dev Executes a Multi RFQ swap through Bebop settlement
function _executeMultiRFQ(
address tokenIn,
address tokenOut,
uint256 amountIn,
bytes memory quoteData,
uint8 signatureType,
bytes memory signature
) private returns (uint256 amountOut) {
// Decode the Multi order
IBebopSettlement.Multi memory order =
abi.decode(quoteData, (IBebopSettlement.Multi));
// Create signature struct
IBebopSettlement.MakerSignature memory sig = IBebopSettlement
.MakerSignature({
signatureType: signatureType,
signatureBytes: signature
});
// Find which token index we're swapping
uint256 tokenInIndex = type(uint256).max;
uint256 tokenOutIndex = type(uint256).max;
for (uint256 i = 0; i < order.taker_tokens.length; i++) {
if (order.taker_tokens[i] == tokenIn) {
tokenInIndex = i;
break;
}
}
for (uint256 i = 0; i < order.maker_tokens.length; i++) {
if (order.maker_tokens[i] == tokenOut) {
tokenOutIndex = i;
break;
}
}
// Prepare filled amounts array (only fill the token we're swapping)
uint256[] memory filledTakerAmounts =
new uint256[](order.taker_tokens.length);
filledTakerAmounts[tokenInIndex] = amountIn;
// 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) ? amountIn : 0;
try IBebopSettlement(bebopSettlement).swapMulti{value: ethValue}(
order, sig, filledTakerAmounts
) {
// Success
} catch {
revert BebopExecutor__SettlementFailed();
}
// 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,
uint256 amountIn,
bytes memory quoteData,
uint8 signatureType,
bytes memory signatureData
) private returns (uint256 amountOut) {
// For aggregate orders, we need to decode both the order and multiple signatures
// The signatureData contains all maker signatures encoded
(
IBebopSettlement.Aggregate memory order,
IBebopSettlement.MakerSignature[] memory signatures
) = _decodeAggregateData(quoteData, signatureType, signatureData);
// Find which token index we're swapping
uint256 tokenInIndex = type(uint256).max;
for (uint256 i = 0; i < order.taker_tokens.length; i++) {
if (order.taker_tokens[i] == tokenIn) {
tokenInIndex = i;
break;
}
}
// Prepare filled amounts array
uint256[] memory filledTakerAmounts =
new uint256[](order.taker_tokens.length);
filledTakerAmounts[tokenInIndex] = amountIn;
// Record balance before swap for all possible output tokens from all makers
uint256 balanceBefore = tokenOut == address(0)
? order.receiver.balance
: IERC20(tokenOut).balanceOf(order.receiver);
// Execute the swap
uint256 ethValue = tokenIn == address(0) ? amountIn : 0;
try IBebopSettlement(bebopSettlement).swapAggregate{value: ethValue}(
order, signatures, filledTakerAmounts
) {
// Success
} catch {
revert BebopExecutor__SettlementFailed();
}
// Calculate actual amount received
uint256 balanceAfter = tokenOut == address(0)
? order.receiver.balance
: IERC20(tokenOut).balanceOf(order.receiver);
amountOut = balanceAfter - balanceBefore;
}
/// @dev Decodes aggregate order data and signatures
function _decodeAggregateData(
bytes memory quoteData,
uint8 signatureType,
bytes memory signatureData
)
private
pure
returns (
IBebopSettlement.Aggregate memory order,
IBebopSettlement.MakerSignature[] memory signatures
)
{
order = abi.decode(quoteData, (IBebopSettlement.Aggregate));
// Decode multiple signatures
// First 4 bytes contain the number of signatures
uint32 numSignatures;
assembly {
numSignatures := mload(add(signatureData, 0x20))
numSignatures := and(numSignatures, 0xffffffff)
}
signatures = new IBebopSettlement.MakerSignature[](numSignatures);
uint256 offset = 4;
for (uint256 i = 0; i < numSignatures; i++) {
// Each signature is prefixed with its length
uint32 sigLength;
assembly {
let data := add(signatureData, add(0x20, offset))
sigLength := mload(data)
sigLength := and(sigLength, 0xffffffff)
}
offset += 4;
// Extract signature bytes
bytes memory sigBytes = new bytes(sigLength);
assembly {
let src := add(signatureData, add(0x20, offset))
let dst := add(sigBytes, 0x20)
// Copy sigLength bytes from src to dst
let end := add(dst, sigLength)
for { } lt(dst, end) { dst := add(dst, 0x20) src := add(src, 0x20) } {
mstore(dst, mload(src))
}
}
signatures[i] = IBebopSettlement.MakerSignature({
signatureType: signatureType,
signatureBytes: sigBytes
});
offset += sigLength;
}
}
/// @dev Decodes quote data into Bebop order and signature structures
function _decodeQuoteData(
bytes memory quoteData,
@@ -253,7 +444,8 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
// Get the variable lengths so we know what to expect
uint32 quoteDataLength = uint32(bytes4(data[42:46]));
uint32 signatureLength = uint32(bytes4(data[47 + quoteDataLength:51 + quoteDataLength]));
uint32 signatureLength =
uint32(bytes4(data[47 + quoteDataLength:51 + quoteDataLength]));
// Make sure we got exactly what we expected, no more no less
uint256 expectedLength = 52 + quoteDataLength + signatureLength;
@@ -266,14 +458,15 @@ contract BebopExecutor is IExecutor, IExecutorErrors, RestrictTransferFrom {
tokenOut = address(bytes20(data[20:40]));
transferType = TransferType(uint8(data[40]));
orderType = OrderType(uint8(data[41]));
// Quote data starts after the length field
quoteData = data[46:46 + quoteDataLength];
// Signature stuff comes after the quote data
signatureType = uint8(data[46 + quoteDataLength]);
signature = data[51 + quoteDataLength:51 + quoteDataLength + signatureLength];
signature =
data[51 + quoteDataLength:51 + quoteDataLength + signatureLength];
// Last byte tells us if we need approval
approvalNeeded = data[51 + quoteDataLength + signatureLength] != 0;
}

View File

@@ -19,7 +19,8 @@ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
import {IUnlockCallback} from
"@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {SafeCast as V4SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {SafeCast as V4SafeCast} from
"@uniswap/v4-core/src/libraries/SafeCast.sol";
import {TransientStateLibrary} from
"@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import "../RestrictTransferFrom.sol";