feat: support multi and aggregate orders
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user