Merge branch 'refs/heads/main' into feat/bebop-rfq-encoder-and-executor
This commit is contained in:
120
foundry/src/executors/HashflowExecutor.sol
Normal file
120
foundry/src/executors/HashflowExecutor.sol
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "../RestrictTransferFrom.sol";
|
||||
import "@interfaces/IExecutor.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
|
||||
error HashflowExecutor__InvalidHashflowRouter();
|
||||
error HashflowExecutor__InvalidDataLength();
|
||||
|
||||
interface IHashflowRouter {
|
||||
struct RFQTQuote {
|
||||
address pool;
|
||||
address externalAccount;
|
||||
address trader;
|
||||
address effectiveTrader;
|
||||
address baseToken;
|
||||
address quoteToken;
|
||||
uint256 effectiveBaseTokenAmount;
|
||||
uint256 baseTokenAmount;
|
||||
uint256 quoteTokenAmount;
|
||||
uint256 quoteExpiry;
|
||||
uint256 nonce;
|
||||
bytes32 txid;
|
||||
bytes signature; // ECDSA signature of the quote, 65 bytes
|
||||
}
|
||||
|
||||
function tradeRFQT(RFQTQuote calldata quote) external payable;
|
||||
}
|
||||
|
||||
contract HashflowExecutor is IExecutor, RestrictTransferFrom {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address public constant HASHFLOW_ROUTER =
|
||||
0x55084eE0fEf03f14a305cd24286359A35D735151;
|
||||
address public constant NATIVE_TOKEN =
|
||||
0x0000000000000000000000000000000000000000;
|
||||
|
||||
constructor(address _permit2) RestrictTransferFrom(_permit2) {}
|
||||
|
||||
function swap(uint256 givenAmount, bytes calldata data)
|
||||
external
|
||||
payable
|
||||
returns (uint256 calculatedAmount)
|
||||
{
|
||||
(
|
||||
IHashflowRouter.RFQTQuote memory quote,
|
||||
bool approvalNeeded,
|
||||
TransferType transferType
|
||||
) = _decodeData(data);
|
||||
|
||||
// Slippage checks
|
||||
if (givenAmount > quote.baseTokenAmount) {
|
||||
// Do not transfer more than the quote's maximum permitted amount.
|
||||
givenAmount = quote.baseTokenAmount;
|
||||
}
|
||||
quote.effectiveBaseTokenAmount = givenAmount;
|
||||
|
||||
if (approvalNeeded && quote.baseToken != NATIVE_TOKEN) {
|
||||
// slither-disable-next-line unused-return
|
||||
IERC20(quote.baseToken).forceApprove(
|
||||
HASHFLOW_ROUTER, type(uint256).max
|
||||
);
|
||||
}
|
||||
|
||||
uint256 ethValue = 0;
|
||||
if (quote.baseToken == NATIVE_TOKEN) {
|
||||
ethValue = quote.effectiveBaseTokenAmount;
|
||||
}
|
||||
_transfer(
|
||||
address(this), transferType, address(quote.baseToken), givenAmount
|
||||
);
|
||||
uint256 balanceBefore = _balanceOf(quote.quoteToken);
|
||||
IHashflowRouter(HASHFLOW_ROUTER).tradeRFQT{value: ethValue}(quote);
|
||||
uint256 balanceAfter = _balanceOf(quote.quoteToken);
|
||||
calculatedAmount = balanceAfter - balanceBefore;
|
||||
}
|
||||
|
||||
function _decodeData(bytes calldata data)
|
||||
internal
|
||||
pure
|
||||
returns (
|
||||
IHashflowRouter.RFQTQuote memory quote,
|
||||
bool approvalNeeded,
|
||||
TransferType transferType
|
||||
)
|
||||
{
|
||||
if (data.length != 347) {
|
||||
revert HashflowExecutor__InvalidDataLength();
|
||||
}
|
||||
|
||||
approvalNeeded = data[0] != 0;
|
||||
transferType = TransferType(uint8(data[1]));
|
||||
|
||||
quote.pool = address(bytes20(data[2:22]));
|
||||
quote.externalAccount = address(bytes20(data[22:42]));
|
||||
quote.trader = address(bytes20(data[42:62]));
|
||||
quote.effectiveTrader = address(bytes20(data[62:82]));
|
||||
quote.baseToken = address(bytes20(data[82:102]));
|
||||
quote.quoteToken = address(bytes20(data[102:122]));
|
||||
quote.effectiveBaseTokenAmount = 0; // Not included in the calldata, set in the swap function
|
||||
quote.baseTokenAmount = uint256(bytes32(data[122:154]));
|
||||
quote.quoteTokenAmount = uint256(bytes32(data[154:186]));
|
||||
quote.quoteExpiry = uint256(bytes32(data[186:218]));
|
||||
quote.nonce = uint256(bytes32(data[218:250]));
|
||||
quote.txid = bytes32(data[250:282]);
|
||||
quote.signature = data[282:347];
|
||||
}
|
||||
|
||||
function _balanceOf(address token)
|
||||
internal
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
balance = token == NATIVE_TOKEN
|
||||
? address(this).balance
|
||||
: IERC20(token).balanceOf(address(this));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user