Merge pull request #198 from propeller-heads/audit/dc/one-transfer-from-only

feat: Restrict transferFrom
This commit is contained in:
dianacarvalho1
2025-05-21 15:57:16 +01:00
committed by GitHub
33 changed files with 852 additions and 708 deletions

4
Cargo.lock generated
View File

@@ -4445,9 +4445,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tycho-common"
version = "0.66.4"
version = "0.70.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5131fdb21cbd754822b0947fc6c763494531837ba8bb34123f6c7f4f89cb69f7"
checksum = "5237d0e4ab6979a1ca9cdb749a2a97240ca6dc716c0da6f42543960d3141255a"
dependencies = [
"anyhow",
"async-trait",

View File

@@ -32,7 +32,7 @@ clap = { version = "4.5.3", features = ["derive"] }
alloy = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true }
alloy-sol-types = { version = "0.8.14", optional = true }
alloy-primitives = { version = "0.8.9", optional = true }
tycho-common = "^0.66.4"
tycho-common = ">0.66.4"
once_cell = "1.20.2"
[dev-dependencies]

View File

@@ -10,7 +10,6 @@ error Dispatcher__InvalidDataLength();
/**
* @title Dispatcher - Dispatch execution to external contracts
* @author PropellerHeads Devs
* @dev Provides the ability to delegate execution of swaps to external
* contracts. This allows dynamically adding new supported protocols
* without needing to upgrade any contracts. External contracts will

View File

@@ -0,0 +1,133 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
import "@openzeppelin/contracts/utils/Address.sol";
error RestrictTransferFrom__AddressZero();
error RestrictTransferFrom__ExceededTransferFromAllowance(
uint256 allowedAmount, uint256 amountAttempted
);
error RestrictTransferFrom__UnknownTransferType();
/**
* @title RestrictTransferFrom - Restrict transferFrom upto allowed amount of token
* @dev Restricts to one `transferFrom` (using `permit2` or regular `transferFrom`)
* per swap, while ensuring that the `transferFrom` is only performed on the input
* token upto input amount, from the msg.sender's wallet that calls the main swap
* method. Reverts if `transferFrom`s are attempted above this allowed amount.
*/
contract RestrictTransferFrom {
using SafeERC20 for IERC20;
IAllowanceTransfer public immutable permit2;
// keccak256("Dispatcher#TOKEN_IN_SLOT")
uint256 private constant _TOKEN_IN_SLOT =
0x66f353cfe8e3cbe0d03292348fbf0fca32e6e07fa0c2a52b4aac22193ac3b894;
// keccak256("Dispatcher#AMOUNT_ALLOWED_SLOT")
uint256 private constant _AMOUNT_ALLOWED_SLOT =
0xc76591aca92830b1554f3dcc7893e7519ec7c57bd4e64fec0c546d9078033291;
// keccak256("Dispatcher#IS_PERMIT2_SLOT")
uint256 private constant _IS_PERMIT2_SLOT =
0x3162c9d1175ca0ca7441f87984fdac41bbfdb13246f42c8bb4414d345da39e2a;
// keccak256("Dispatcher#SENDER_SLOT")
uint256 private constant _SENDER_SLOT =
0x5dcc7974be5cb30f183f878073999aaa6620995b9e052ab5a713071ff60ae9b5;
// keccak256("Dispatcher#AMOUNT_SPENT_SLOT")
uint256 private constant _AMOUNT_SPENT_SLOT =
0x56044a5eb3aa5bd3ad908b7f15d1e8cb830836bb4ad178a0bf08955c94c40d30;
constructor(address _permit2) {
if (_permit2 == address(0)) {
revert RestrictTransferFrom__AddressZero();
}
permit2 = IAllowanceTransfer(_permit2);
}
enum TransferType {
TransferFrom,
Transfer,
None
}
/**
* @dev This function is used to store the transfer information in the
* contract's storage. This is done as the first step in the swap process in TychoRouter.
*/
// slither-disable-next-line assembly
function _tstoreTransferFromInfo(
address tokenIn,
uint256 amountIn,
bool isPermit2,
bool transferFromNeeded
) internal {
uint256 amountAllowed = amountIn;
if (!transferFromNeeded) {
amountAllowed = 0;
}
assembly {
tstore(_TOKEN_IN_SLOT, tokenIn)
tstore(_AMOUNT_ALLOWED_SLOT, amountAllowed)
tstore(_IS_PERMIT2_SLOT, isPermit2)
tstore(_SENDER_SLOT, caller())
tstore(_AMOUNT_SPENT_SLOT, 0)
}
}
/**
* @dev This function is used to transfer the tokens from the sender to the receiver.
* This function is called within the Executor contracts.
* If the TransferType is TransferFrom, it will check if the amount is within the allowed amount and transfer those funds from the user.
* If the TransferType is Transfer, it will transfer the funds from the TychoRouter to the receiver.
* If the TransferType is None, it will do nothing.
*/
// slither-disable-next-line assembly
function _transfer(
address receiver,
TransferType transferType,
address tokenIn,
uint256 amount
) internal {
if (transferType == TransferType.TransferFrom) {
bool isPermit2;
address sender;
uint256 amountSpent;
uint256 amountAllowed;
assembly {
tokenIn := tload(_TOKEN_IN_SLOT)
amountAllowed := tload(_AMOUNT_ALLOWED_SLOT)
isPermit2 := tload(_IS_PERMIT2_SLOT)
sender := tload(_SENDER_SLOT)
amountSpent := tload(_AMOUNT_SPENT_SLOT)
}
uint256 amountAttempted = amountSpent + amount;
if (amountAttempted > amountAllowed) {
revert RestrictTransferFrom__ExceededTransferFromAllowance(
amountAllowed, amountAttempted
);
}
assembly {
tstore(_AMOUNT_SPENT_SLOT, amountAttempted)
}
if (isPermit2) {
// Permit2.permit is already called from the TychoRouter
permit2.transferFrom(sender, receiver, uint160(amount), tokenIn);
} else {
// slither-disable-next-line arbitrary-send-erc20
IERC20(tokenIn).safeTransferFrom(sender, receiver, amount);
}
} else if (transferType == TransferType.Transfer) {
if (tokenIn == address(0)) {
Address.sendValue(payable(receiver), amount);
} else {
IERC20(tokenIn).safeTransfer(receiver, amount);
}
} else if (transferType == TransferType.None) {
return;
} else {
revert RestrictTransferFrom__UnknownTransferType();
}
}
}

View File

@@ -14,6 +14,7 @@ import "@permit2/src/interfaces/IAllowanceTransfer.sol";
import "./Dispatcher.sol";
import {LibSwap} from "../lib/LibSwap.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {RestrictTransferFrom} from "./RestrictTransferFrom.sol";
// ✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷
// ✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷
@@ -65,8 +66,13 @@ error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount);
error TychoRouter__InvalidDataLength();
error TychoRouter__UndefinedMinAmountOut();
contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
IAllowanceTransfer public immutable permit2;
contract TychoRouter is
AccessControl,
Dispatcher,
Pausable,
ReentrancyGuard,
RestrictTransferFrom
{
IWETH private immutable _weth;
using SafeERC20 for IERC20;
@@ -87,7 +93,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address indexed token, uint256 amount, address indexed receiver
);
constructor(address _permit2, address weth) {
constructor(address _permit2, address weth)
RestrictTransferFrom(_permit2)
{
if (_permit2 == address(0) || weth == address(0)) {
revert TychoRouter__AddressZero();
}
@@ -115,6 +123,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
* @param nTokens The total number of tokens involved in the swap graph (used to initialize arrays for internal calculations).
* @param receiver The address to receive the output tokens.
* @param transferFromNeeded If false, the contract will assume that the input token is already transferred to the contract and don't allow any transferFroms
* @param swaps Encoded swap graph data containing details of each swap.
*
* @return amountOut The total amount of the output token received by the receiver.
@@ -128,13 +137,18 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bool unwrapEth,
uint256 nTokens,
address receiver,
bool transferFromNeeded,
bytes calldata swaps
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
_tstoreTransferFromInfo(tokenIn, amountIn, false, transferFromNeeded);
return _splitSwapChecked(
amountIn,
tokenIn,
tokenOut,
minAmountOut,
initialBalanceTokenOut,
wrapEth,
unwrapEth,
nTokens,
@@ -182,16 +196,19 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bytes calldata signature,
bytes calldata swaps
) external payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
// For native ETH, assume funds already in our router. Else, handle approval.
if (tokenIn != address(0)) {
permit2.permit(msg.sender, permitSingle, signature);
}
_tstoreTransferFromInfo(tokenIn, amountIn, true, true);
return _splitSwapChecked(
amountIn,
tokenIn,
tokenOut,
minAmountOut,
initialBalanceTokenOut,
wrapEth,
unwrapEth,
nTokens,
@@ -218,6 +235,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
* @param wrapEth If true, wraps the input token (native ETH) into WETH.
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
* @param receiver The address to receive the output tokens.
* @param transferFromNeeded If false, the contract will assume that the input token is already transferred to the contract and don't allow any transferFroms
* @param swaps Encoded swap graph data containing details of each swap.
*
* @return amountOut The total amount of the output token received by the receiver.
@@ -230,13 +248,18 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bool wrapEth,
bool unwrapEth,
address receiver,
bool transferFromNeeded,
bytes calldata swaps
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
_tstoreTransferFromInfo(tokenIn, amountIn, false, transferFromNeeded);
return _sequentialSwapChecked(
amountIn,
tokenIn,
tokenOut,
minAmountOut,
initialBalanceTokenOut,
wrapEth,
unwrapEth,
receiver,
@@ -280,16 +303,20 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bytes calldata signature,
bytes calldata swaps
) external payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
// For native ETH, assume funds already in our router. Else, handle approval.
if (tokenIn != address(0)) {
permit2.permit(msg.sender, permitSingle, signature);
}
_tstoreTransferFromInfo(tokenIn, amountIn, true, true);
return _sequentialSwapChecked(
amountIn,
tokenIn,
tokenOut,
minAmountOut,
initialBalanceTokenOut,
wrapEth,
unwrapEth,
receiver,
@@ -313,6 +340,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
* @param wrapEth If true, wraps the input token (native ETH) into WETH.
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
* @param receiver The address to receive the output tokens.
* @param transferFromNeeded If false, the contract will assume that the input token is already transferred to the contract and don't allow any transferFroms
* @param swapData Encoded swap details.
*
* @return amountOut The total amount of the output token received by the receiver.
@@ -325,13 +353,18 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bool wrapEth,
bool unwrapEth,
address receiver,
bool transferFromNeeded,
bytes calldata swapData
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
_tstoreTransferFromInfo(tokenIn, amountIn, false, transferFromNeeded);
return _singleSwap(
amountIn,
tokenIn,
tokenOut,
minAmountOut,
initialBalanceTokenOut,
wrapEth,
unwrapEth,
receiver,
@@ -375,16 +408,19 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bytes calldata signature,
bytes calldata swapData
) external payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
// For native ETH, assume funds already in our router. Else, handle approval.
if (tokenIn != address(0)) {
permit2.permit(msg.sender, permitSingle, signature);
}
_tstoreTransferFromInfo(tokenIn, amountIn, true, true);
return _singleSwap(
amountIn,
tokenIn,
tokenOut,
minAmountOut,
initialBalanceTokenOut,
wrapEth,
unwrapEth,
receiver,
@@ -405,6 +441,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address tokenIn,
address tokenOut,
uint256 minAmountOut,
uint256 initialBalanceTokenOut,
bool wrapEth,
bool unwrapEth,
uint256 nTokens,
@@ -424,7 +461,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth);
}
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _splitSwap(amountIn, nTokens, swaps);
if (amountOut < minAmountOut) {
@@ -459,6 +495,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address tokenIn,
address tokenOut,
uint256 minAmountOut,
uint256 initialBalanceTokenOut,
bool wrapEth,
bool unwrapEth,
address receiver,
@@ -480,7 +517,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
(address executor, bytes calldata protocolData) =
swap_.decodeSingleSwap();
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _callSwapOnExecutor(executor, amountIn, protocolData);
if (amountOut < minAmountOut) {
@@ -515,6 +551,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address tokenIn,
address tokenOut,
uint256 minAmountOut,
uint256 initialBalanceTokenOut,
bool wrapEth,
bool unwrapEth,
address receiver,
@@ -533,7 +570,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth);
}
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _sequentialSwap(amountIn, swaps);
if (amountOut < minAmountOut) {

View File

@@ -10,16 +10,16 @@ import {
import {IAsset} from "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol";
// slither-disable-next-line solc-version
import {IVault} from "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
error BalancerV2Executor__InvalidDataLength();
contract BalancerV2Executor is IExecutor, TokenTransfer {
contract BalancerV2Executor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20;
address private constant VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
constructor(address _permit2) TokenTransfer(_permit2) {}
constructor(address _permit2) RestrictTransferFrom(_permit2) {}
// slither-disable-next-line locked-ether
function swap(uint256 givenAmount, bytes calldata data)
@@ -32,21 +32,13 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
IERC20 tokenOut,
bytes32 poolId,
address receiver,
bool needsApproval,
bool approvalNeeded,
TransferType transferType
) = _decodeData(data);
_transfer(
address(tokenIn),
msg.sender,
// Receiver can never be the pool, since the pool expects funds in the router contract
// Thus, this call will only ever be used to transfer funds from the user into the router.
address(this),
givenAmount,
transferType
);
_transfer(address(this), transferType, address(tokenIn), givenAmount);
if (needsApproval) {
if (approvalNeeded) {
// slither-disable-next-line unused-return
tokenIn.forceApprove(VAULT, type(uint256).max);
}
@@ -81,7 +73,7 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
IERC20 tokenOut,
bytes32 poolId,
address receiver,
bool needsApproval,
bool approvalNeeded,
TransferType transferType
)
{
@@ -93,7 +85,7 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
tokenOut = IERC20(address(bytes20(data[20:40])));
poolId = bytes32(data[40:72]);
receiver = address(bytes20(data[72:92]));
needsApproval = uint8(data[92]) > 0;
approvalNeeded = data[92] != 0;
transferType = TransferType(uint8(data[93]));
}
}

View File

@@ -3,8 +3,8 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenTransfer.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
error CurveExecutor__AddressZero();
error CurveExecutor__InvalidDataLength();
@@ -35,13 +35,13 @@ interface CryptoPoolETH {
// slither-disable-end naming-convention
}
contract CurveExecutor is IExecutor, TokenTransfer {
contract CurveExecutor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20;
address public immutable nativeToken;
constructor(address _nativeToken, address _permit2)
TokenTransfer(_permit2)
RestrictTransferFrom(_permit2)
{
if (_nativeToken == address(0)) {
revert CurveExecutor__AddressZero();
@@ -64,25 +64,16 @@ contract CurveExecutor is IExecutor, TokenTransfer {
uint8 poolType,
int128 i,
int128 j,
bool tokenApprovalNeeded,
bool approvalNeeded,
TransferType transferType,
address receiver
) = _decodeData(data);
_transfer(
tokenIn,
msg.sender,
// Receiver can never be the pool, since the pool expects funds in the router contract
// Thus, this call will only ever be used to transfer funds from the user into the router.
address(this),
amountIn,
transferType
);
if (tokenApprovalNeeded && tokenIn != nativeToken) {
if (approvalNeeded && tokenIn != nativeToken) {
// slither-disable-next-line unused-return
IERC20(tokenIn).forceApprove(address(pool), type(uint256).max);
}
_transfer(address(this), transferType, tokenIn, amountIn);
/// Inspired by Curve's router contract: https://github.com/curvefi/curve-router-ng/blob/9ab006ca848fc7f1995b6fbbecfecc1e0eb29e2a/contracts/Router.vy#L44
uint256 balanceBefore = _balanceOf(tokenOut);
@@ -133,7 +124,7 @@ contract CurveExecutor is IExecutor, TokenTransfer {
uint8 poolType,
int128 i,
int128 j,
bool tokenApprovalNeeded,
bool approvalNeeded,
TransferType transferType,
address receiver
)
@@ -144,7 +135,7 @@ contract CurveExecutor is IExecutor, TokenTransfer {
poolType = uint8(data[60]);
i = int128(uint128(uint8(data[61])));
j = int128(uint128(uint8(data[62])));
tokenApprovalNeeded = data[63] != 0;
approvalNeeded = data[63] != 0;
transferType = TransferType(uint8(data[64]));
receiver = address(bytes20(data[65:85]));
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IExecutor} from "@interfaces/IExecutor.sol";
import {ICallback} from "@interfaces/ICallback.sol";
import {ICore} from "@ekubo/interfaces/ICore.sol";
@@ -11,14 +11,15 @@ import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol";
import {LibBytes} from "@solady/utils/LibBytes.sol";
import {Config, EkuboPoolKey} from "@ekubo/types/poolKey.sol";
import {MAX_SQRT_RATIO, MIN_SQRT_RATIO} from "@ekubo/types/sqrtRatio.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
import "@openzeppelin/contracts/utils/Address.sol";
contract EkuboExecutor is
IExecutor,
ILocker,
IPayer,
ICallback,
TokenTransfer
RestrictTransferFrom
{
error EkuboExecutor__InvalidDataLength();
error EkuboExecutor__CoreOnly();
@@ -26,13 +27,17 @@ contract EkuboExecutor is
ICore immutable core;
uint256 constant POOL_DATA_OFFSET = 77;
uint256 constant POOL_DATA_OFFSET = 57;
uint256 constant HOP_BYTE_LEN = 52;
bytes4 constant LOCKED_SELECTOR = 0xb45a3c0e; // locked(uint256)
bytes4 constant PAY_CALLBACK_SELECTOR = 0x599d0714; // payCallback(uint256,address)
constructor(address _core, address _permit2) TokenTransfer(_permit2) {
using SafeERC20 for IERC20;
constructor(address _core, address _permit2)
RestrictTransferFrom(_permit2)
{
core = ICore(_core);
}
@@ -41,16 +46,11 @@ contract EkuboExecutor is
payable
returns (uint256 calculatedAmount)
{
if (data.length < 93) revert EkuboExecutor__InvalidDataLength();
if (data.length < 92) revert EkuboExecutor__InvalidDataLength();
// amountIn must be at most type(int128).MAX
calculatedAmount = uint256(
_lock(
bytes.concat(
bytes16(uint128(amountIn)), bytes20(msg.sender), data
)
)
);
calculatedAmount =
uint256(_lock(bytes.concat(bytes16(uint128(amountIn)), data)));
}
function handleCallback(bytes calldata raw)
@@ -125,11 +125,9 @@ contract EkuboExecutor is
function _locked(bytes calldata swapData) internal returns (int128) {
int128 nextAmountIn = int128(uint128(bytes16(swapData[0:16])));
uint128 tokenInDebtAmount = uint128(nextAmountIn);
address sender = address(bytes20(swapData[16:36]));
uint8 transferType = uint8(swapData[36]);
address receiver = address(bytes20(swapData[37:57]));
address tokenIn = address(bytes20(swapData[57:77]));
TransferType transferType = TransferType(uint8(swapData[16]));
address receiver = address(bytes20(swapData[17:37]));
address tokenIn = address(bytes20(swapData[37:57]));
address nextTokenIn = tokenIn;
@@ -163,17 +161,14 @@ contract EkuboExecutor is
offset += HOP_BYTE_LEN;
}
_pay(tokenIn, tokenInDebtAmount, sender, transferType);
_pay(tokenIn, tokenInDebtAmount, transferType);
core.withdraw(nextTokenIn, receiver, uint128(nextAmountIn));
return nextAmountIn;
}
function _pay(
address token,
uint128 amount,
address sender,
uint8 transferType
) internal {
function _pay(address token, uint128 amount, TransferType transferType)
internal
{
address target = address(core);
if (token == NATIVE_TOKEN_ADDRESS) {
@@ -186,11 +181,10 @@ contract EkuboExecutor is
mstore(free, shl(224, 0x0c11dedd))
mstore(add(free, 4), token)
mstore(add(free, 36), shl(128, amount))
mstore(add(free, 52), shl(96, sender))
mstore(add(free, 72), shl(248, transferType))
mstore(add(free, 52), shl(248, transferType))
// 4 (selector) + 32 (token) + 16 (amount) + 20 (sender) + 1 (transferType) = 73
if iszero(call(gas(), target, 0, free, 73, 0, 0)) {
// 4 (selector) + 32 (token) + 16 (amount) + 1 (transferType) = 53
if iszero(call(gas(), target, 0, free, 53, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
@@ -201,9 +195,8 @@ contract EkuboExecutor is
function _payCallback(bytes calldata payData) internal {
address token = address(bytes20(payData[12:32])); // This arg is abi-encoded
uint128 amount = uint128(bytes16(payData[32:48]));
address sender = address(bytes20(payData[48:68]));
TransferType transferType = TransferType(uint8(payData[68]));
_transfer(token, sender, address(core), amount, transferType);
TransferType transferType = TransferType(uint8(payData[48]));
_transfer(address(core), transferType, token, amount);
}
// To receive withdrawals from Core

View File

@@ -3,18 +3,21 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenTransfer.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
error MaverickV2Executor__InvalidDataLength();
error MaverickV2Executor__InvalidTarget();
error MaverickV2Executor__InvalidFactory();
contract MaverickV2Executor is IExecutor, TokenTransfer {
contract MaverickV2Executor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20;
address public immutable factory;
constructor(address _factory, address _permit2) TokenTransfer(_permit2) {
constructor(address _factory, address _permit2)
RestrictTransferFrom(_permit2)
{
if (_factory == address(0)) {
revert MaverickV2Executor__InvalidFactory();
}
@@ -47,9 +50,8 @@ contract MaverickV2Executor is IExecutor, TokenTransfer {
tickLimit: tickLimit
});
_transfer(
address(tokenIn), msg.sender, target, givenAmount, transferType
);
_transfer(target, transferType, address(tokenIn), givenAmount);
// slither-disable-next-line unused-return
(, calculatedAmount) = pool.swap(receiver, swapParams, "");
}

View File

@@ -1,75 +0,0 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
error TokenTransfer__AddressZero();
error TokenTransfer__InvalidTransferType();
contract TokenTransfer {
using SafeERC20 for IERC20;
IAllowanceTransfer public immutable permit2;
enum TransferType {
// Assume funds are in the TychoRouter - transfer into the pool
TRANSFER_TO_PROTOCOL,
// Assume funds are in msg.sender's wallet - transferFrom into the pool
TRANSFER_FROM_TO_PROTOCOL,
// Assume funds are in msg.sender's wallet - permit2TransferFrom into the pool
TRANSFER_PERMIT2_TO_PROTOCOL,
// Assume funds are in msg.sender's wallet - but the pool requires it to be
// in the router contract when calling swap - transferFrom into the router
// contract
TRANSFER_FROM_TO_ROUTER,
// Assume funds are in msg.sender's wallet - but the pool requires it to be
// in the router contract when calling swap - transferFrom into the router
// contract using permit2
TRANSFER_PERMIT2_TO_ROUTER,
// Assume funds have already been transferred into the pool. Do nothing.
NONE
}
constructor(address _permit2) {
if (_permit2 == address(0)) {
revert TokenTransfer__AddressZero();
}
permit2 = IAllowanceTransfer(_permit2);
}
function _transfer(
address tokenIn,
address sender,
address receiver,
uint256 amount,
TransferType transferType
) internal {
if (transferType == TransferType.NONE) {
return;
} else if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
if (tokenIn == address(0)) {
payable(receiver).transfer(amount);
} else {
IERC20(tokenIn).safeTransfer(receiver, amount);
}
} else if (transferType == TransferType.TRANSFER_FROM_TO_PROTOCOL) {
// slither-disable-next-line arbitrary-send-erc20
IERC20(tokenIn).safeTransferFrom(sender, receiver, amount);
} else if (transferType == TransferType.TRANSFER_PERMIT2_TO_PROTOCOL) {
// Permit2.permit is already called from the TychoRouter
permit2.transferFrom(sender, receiver, uint160(amount), tokenIn);
} else if (transferType == TransferType.TRANSFER_FROM_TO_ROUTER) {
// slither-disable-next-line arbitrary-send-erc20
IERC20(tokenIn).safeTransferFrom(sender, address(this), amount);
} else if (transferType == TransferType.TRANSFER_PERMIT2_TO_ROUTER) {
// Permit2.permit is already called from the TychoRouter
permit2.transferFrom(
sender, address(this), uint160(amount), tokenIn
);
} else {
revert TokenTransfer__InvalidTransferType();
}
}
}

View File

@@ -4,7 +4,7 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol";
import "./TokenTransfer.sol";
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
error UniswapV2Executor__InvalidDataLength();
error UniswapV2Executor__InvalidTarget();
@@ -12,7 +12,7 @@ error UniswapV2Executor__InvalidFactory();
error UniswapV2Executor__InvalidInitCode();
error UniswapV2Executor__InvalidFee();
contract UniswapV2Executor is IExecutor, TokenTransfer {
contract UniswapV2Executor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20;
address public immutable factory;
@@ -25,7 +25,7 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
bytes32 _initCode,
address _permit2,
uint256 _feeBps
) TokenTransfer(_permit2) {
) RestrictTransferFrom(_permit2) {
if (_factory == address(0)) {
revert UniswapV2Executor__InvalidFactory();
}
@@ -59,9 +59,8 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
_verifyPairAddress(target);
calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne);
_transfer(
address(tokenIn), msg.sender, target, givenAmount, transferType
);
_transfer(target, transferType, address(tokenIn), givenAmount);
IUniswapV2Pair pool = IUniswapV2Pair(target);
if (zeroForOne) {
@@ -88,7 +87,7 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
inToken = IERC20(address(bytes20(data[0:20])));
target = address(bytes20(data[20:40]));
receiver = address(bytes20(data[40:60]));
zeroForOne = uint8(data[60]) > 0;
zeroForOne = data[60] != 0;
transferType = TransferType(uint8(data[61]));
}

View File

@@ -5,15 +5,15 @@ import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@interfaces/ICallback.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
import "@openzeppelin/contracts/utils/Address.sol";
error UniswapV3Executor__InvalidDataLength();
error UniswapV3Executor__InvalidFactory();
error UniswapV3Executor__InvalidTarget();
error UniswapV3Executor__InvalidInitCode();
error UniswapV3Executor__InvalidTransferType(uint8 transferType);
contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
contract UniswapV3Executor is IExecutor, ICallback, RestrictTransferFrom {
using SafeERC20 for IERC20;
uint160 private constant MIN_SQRT_RATIO = 4295128739;
@@ -25,7 +25,7 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
address private immutable self;
constructor(address _factory, bytes32 _initCode, address _permit2)
TokenTransfer(_permit2)
RestrictTransferFrom(_permit2)
{
if (_factory == address(0)) {
revert UniswapV3Executor__InvalidFactory();
@@ -97,21 +97,14 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
abi.decode(msgData[4:68], (int256, int256));
address tokenIn = address(bytes20(msgData[132:152]));
// Transfer type does not exist
if (uint8(msgData[175]) > uint8(TransferType.NONE)) {
revert UniswapV3Executor__InvalidTransferType(uint8(msgData[175]));
}
TransferType transferType = TransferType(uint8(msgData[175]));
address sender = address(bytes20(msgData[176:196]));
verifyCallback(msgData[132:]);
uint256 amountOwed =
amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
_transfer(tokenIn, sender, msg.sender, amountOwed, transferType);
_transfer(msg.sender, transferType, tokenIn, amountOwed);
return abi.encode(amountOwed, tokenIn);
}
@@ -162,10 +155,8 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
address tokenOut,
uint24 fee,
TransferType transferType
) internal view returns (bytes memory) {
return abi.encodePacked(
tokenIn, tokenOut, fee, uint8(transferType), msg.sender
);
) internal pure returns (bytes memory) {
return abi.encodePacked(tokenIn, tokenOut, fee, uint8(transferType));
}
function _verifyPairAddress(

View File

@@ -3,7 +3,6 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import {ICallback} from "@interfaces/ICallback.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
import {
IERC20,
SafeERC20
@@ -23,6 +22,8 @@ import {IUnlockCallback} from
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {TransientStateLibrary} from
"@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import "../RestrictTransferFrom.sol";
import "@openzeppelin/contracts/utils/Address.sol";
error UniswapV4Executor__InvalidDataLength();
error UniswapV4Executor__NotPoolManager();
@@ -37,7 +38,7 @@ contract UniswapV4Executor is
IExecutor,
IUnlockCallback,
ICallback,
TokenTransfer
RestrictTransferFrom
{
using SafeERC20 for IERC20;
using CurrencyLibrary for Currency;
@@ -47,8 +48,8 @@ contract UniswapV4Executor is
IPoolManager public immutable poolManager;
address private immutable _self;
bytes4 constant SWAP_EXACT_INPUT_SINGLE_SELECTOR = 0x8bc6d0d7;
bytes4 constant SWAP_EXACT_INPUT_SELECTOR = 0xaf90aeb1;
bytes4 constant SWAP_EXACT_INPUT_SINGLE_SELECTOR = 0x6022fbcd;
bytes4 constant SWAP_EXACT_INPUT_SELECTOR = 0x044f0d3d;
struct UniswapV4Pool {
address intermediaryToken;
@@ -57,7 +58,7 @@ contract UniswapV4Executor is
}
constructor(IPoolManager _poolManager, address _permit2)
TokenTransfer(_permit2)
RestrictTransferFrom(_permit2)
{
poolManager = _poolManager;
_self = address(this);
@@ -100,7 +101,6 @@ contract UniswapV4Executor is
key,
zeroForOne,
amountIn,
msg.sender,
transferType,
receiver,
bytes("")
@@ -123,7 +123,6 @@ contract UniswapV4Executor is
currencyIn,
path,
amountIn,
msg.sender,
transferType,
receiver
);
@@ -153,7 +152,7 @@ contract UniswapV4Executor is
tokenIn = address(bytes20(data[0:20]));
tokenOut = address(bytes20(data[20:40]));
zeroForOne = (data[40] != 0);
zeroForOne = data[40] != 0;
transferType = TransferType(uint8(data[41]));
receiver = address(bytes20(data[42:62]));
@@ -239,8 +238,7 @@ contract UniswapV4Executor is
* @param poolKey The key of the pool to swap in.
* @param zeroForOne Whether the swap is from token0 to token1 (true) or vice versa (false).
* @param amountIn The amount of tokens to swap in.
* @param sender The address of the sender.
* @param transferType The type of transfer in to use.
* @param transferType The type of action necessary to pay back the pool.
* @param receiver The address of the receiver.
* @param hookData Additional data for hook contracts.
*/
@@ -248,7 +246,6 @@ contract UniswapV4Executor is
PoolKey memory poolKey,
bool zeroForOne,
uint128 amountIn,
address sender,
TransferType transferType,
address receiver,
bytes calldata hookData
@@ -262,7 +259,7 @@ contract UniswapV4Executor is
if (amount > amountIn) {
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
}
_settle(currencyIn, amount, sender, transferType);
_settle(currencyIn, amount, transferType);
Currency currencyOut =
zeroForOne ? poolKey.currency1 : poolKey.currency0;
@@ -275,15 +272,13 @@ contract UniswapV4Executor is
* @param currencyIn The currency of the input token.
* @param path The path to swap along.
* @param amountIn The amount of tokens to swap in.
* @param sender The address of the sender.
* @param transferType The type of transfer in to use.
* @param transferType The type of action necessary to pay back the pool.
* @param receiver The address of the receiver.
*/
function swapExactInput(
Currency currencyIn,
PathKey[] calldata path,
uint128 amountIn,
address sender,
TransferType transferType,
address receiver
) external returns (uint128) {
@@ -315,7 +310,7 @@ contract UniswapV4Executor is
if (amount > amountIn) {
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
}
_settle(currencyIn, amount, sender, transferType);
_settle(currencyIn, amount, transferType);
_take(
swapCurrencyIn, // at the end of the loop this is actually currency out
@@ -387,14 +382,12 @@ contract UniswapV4Executor is
* @dev The implementing contract must ensure that the `payer` is a secure address.
* @param currency The currency to settle.
* @param amount The amount to send.
* @param sender The address of the payer.
* @param transferType The type of transfer to use.
* @param transferType The type of action necessary to pay back the pool.
* @dev Returns early if the amount is 0.
*/
function _settle(
Currency currency,
uint256 amount,
address sender,
TransferType transferType
) internal {
if (amount == 0) return;
@@ -404,11 +397,10 @@ contract UniswapV4Executor is
poolManager.settle{value: amount}();
} else {
_transfer(
Currency.unwrap(currency),
sender,
address(poolManager),
amount,
transferType
transferType,
Currency.unwrap(currency),
amount
);
// slither-disable-next-line unused-return
poolManager.settle();

View File

@@ -26,7 +26,7 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
USDE_ADDR,
USDT_ADDR,
true,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL,
RestrictTransferFrom.TransferType.TransferFrom,
ALICE,
pools
);
@@ -55,7 +55,7 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
// This test has two uniswap v4 hops that will be executed inside of the V4 pool manager
// USDE -> USDT -> WBTC
uint256 amountIn = 100 ether;
deal(USDE_ADDR, tychoRouterAddr, amountIn);
deal(USDE_ADDR, ALICE, amountIn);
UniswapV4Executor.UniswapV4Pool[] memory pools =
new UniswapV4Executor.UniswapV4Pool[](2);
@@ -74,7 +74,7 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
USDE_ADDR,
WBTC_ADDR,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL,
RestrictTransferFrom.TransferType.TransferFrom,
ALICE,
pools
);
@@ -82,8 +82,18 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
bytes memory swap =
encodeSingleSwap(address(usv4Executor), protocolData);
vm.startPrank(ALICE);
IERC20(USDE_ADDR).approve(tychoRouterAddr, amountIn);
tychoRouter.singleSwap(
amountIn, USDE_ADDR, WBTC_ADDR, 118280, false, false, ALICE, swap
amountIn,
USDE_ADDR,
WBTC_ADDR,
118280,
false,
false,
ALICE,
true,
swap
);
assertEq(IERC20(WBTC_ADDR).balanceOf(ALICE), 118281);
@@ -262,7 +272,7 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
ALICE,
DAI_WETH_USV3,
zeroForOne,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
encodeSingleSwap(address(usv3Executor), protocolData);

View File

@@ -8,25 +8,21 @@ import "./executors/UniswapV4Utils.sol";
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
function _getSequentialSwaps(bool permit2)
internal
view
returns (bytes[] memory)
{
function _getSequentialSwaps() internal view returns (bytes[] memory) {
// Trade 1 WETH for USDC through DAI with 2 swaps on Uniswap V2
// 1 WETH -> DAI -> USDC
// (univ2) (univ2)
TokenTransfer.TransferType transferType = permit2
? TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
: TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL;
bytes[] memory swaps = new bytes[](2);
// WETH -> DAI
swaps[0] = encodeSequentialSwap(
address(usv2Executor),
encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, transferType
WETH_ADDR,
WETH_DAI_POOL,
DAI_USDC_POOL, // receiver (direct to next pool)
false,
RestrictTransferFrom.TransferType.TransferFrom // transfer to protocol from router
)
);
@@ -38,7 +34,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
DAI_USDC_POOL,
ALICE,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.None // funds already sent to pool
)
);
return swaps;
@@ -55,7 +51,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps(true);
bytes[] memory swaps = _getSequentialSwaps();
tychoRouter.sequentialSwapPermit2(
amountIn,
WETH_ADDR,
@@ -82,7 +78,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps(false);
bytes[] memory swaps = _getSequentialSwaps();
tychoRouter.sequentialSwap(
amountIn,
WETH_ADDR,
@@ -91,6 +87,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
false,
false,
ALICE,
true,
pleEncode(swaps)
);
@@ -107,7 +104,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps(false);
bytes[] memory swaps = _getSequentialSwaps();
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
tychoRouter.sequentialSwap(
amountIn,
@@ -117,6 +114,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
false,
false,
ALICE,
true,
pleEncode(swaps)
);
}
@@ -129,7 +127,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn - 1);
bytes[] memory swaps = _getSequentialSwaps(false);
bytes[] memory swaps = _getSequentialSwaps();
vm.expectRevert();
tychoRouter.sequentialSwap(
amountIn,
@@ -139,6 +137,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
false,
false,
ALICE,
true,
pleEncode(swaps)
);
}
@@ -154,7 +153,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps(true);
bytes[] memory swaps = _getSequentialSwaps();
uint256 minAmountOut = 3000 * 1e18;
@@ -204,9 +203,9 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
encodeUniswapV2Swap(
WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
DAI_USDC_POOL,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
)
);
@@ -218,7 +217,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
DAI_USDC_POOL,
ALICE,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.None
)
);
@@ -266,7 +265,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
DAI_USDC_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
)
);
@@ -278,7 +277,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
)
);
@@ -315,7 +314,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
tychoRouterAddr,
USDC_WETH_USV3,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap(
@@ -324,7 +323,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
tychoRouterAddr,
USDC_WETH_USV3_2,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes[] memory swaps = new bytes[](2);

View File

@@ -26,7 +26,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
@@ -67,7 +67,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
@@ -82,6 +82,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
false,
false,
ALICE,
true,
swap
);
@@ -108,7 +109,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.None
);
bytes memory swap =
@@ -116,7 +117,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
tychoRouter.singleSwap(
amountIn, WETH_ADDR, DAI_ADDR, 0, false, false, ALICE, swap
amountIn, WETH_ADDR, DAI_ADDR, 0, false, false, ALICE, true, swap
);
}
@@ -134,7 +135,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
@@ -150,6 +151,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
false,
false,
ALICE,
true,
swap
);
}
@@ -169,7 +171,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
@@ -192,6 +194,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
false,
false,
ALICE,
true,
swap
);
}
@@ -218,7 +221,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer // ETH has already been transferred to router
);
bytes memory swap =
@@ -261,7 +264,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
@@ -287,6 +290,49 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
vm.stopPrank();
}
function testSingleSwapNoTransferNeededIllegalTransfer() public {
// Tokens are already in the router, there is no need to transfer them.
// Failure because there will be an attempt on an illegal transfer.
uint256 amountIn = 1 ether;
deal(WETH_ADDR, address(tychoRouter), amountIn);
vm.startPrank(ALICE);
// Approve the tokenIn to be transferred to the router
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR,
WETH_DAI_POOL,
ALICE,
false,
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
encodeSingleSwap(address(usv2Executor), protocolData);
vm.expectRevert(
abi.encodeWithSelector(
RestrictTransferFrom__ExceededTransferFromAllowance.selector,
0, // allowed amount
amountIn // attempted amount
)
);
uint256 amountOut = tychoRouter.singleSwap(
amountIn,
WETH_ADDR,
DAI_ADDR,
2000 * 1e18,
false,
false,
ALICE,
false,
swap
);
vm.stopPrank();
}
function testSingleSwapIntegration() public {
// Tests swapping WETH -> DAI on a USV2 pool with regular approvals
deal(WETH_ADDR, ALICE, 1 ether);

View File

@@ -2,13 +2,13 @@
pragma solidity ^0.8.26;
import "@src/executors/UniswapV4Executor.sol";
import {TychoRouter} from "@src/TychoRouter.sol";
import {TychoRouter, RestrictTransferFrom} from "@src/TychoRouter.sol";
import "./TychoRouterTestSetup.sol";
import "./executors/UniswapV4Utils.sol";
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
function _getSplitSwaps(bool permit2)
function _getSplitSwaps(bool transferFrom)
private
view
returns (bytes[] memory)
@@ -19,10 +19,9 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
// -> WBTC ->
// (univ2) (univ2)
bytes[] memory swaps = new bytes[](4);
TokenTransfer.TransferType inTransferType = permit2
? TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
: TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL;
RestrictTransferFrom.TransferType transferType = transferFrom
? RestrictTransferFrom.TransferType.TransferFrom
: RestrictTransferFrom.TransferType.Transfer;
// WETH -> WBTC (60%)
swaps[0] = encodeSplitSwap(
@@ -31,11 +30,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(0xffffff * 60) / 100, // 60%
address(usv2Executor),
encodeUniswapV2Swap(
WETH_ADDR,
WETH_WBTC_POOL,
tychoRouterAddr,
false,
inTransferType
WETH_ADDR, WETH_WBTC_POOL, tychoRouterAddr, false, transferType
)
);
// WBTC -> USDC
@@ -49,7 +44,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
USDC_WBTC_POOL,
ALICE,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
)
);
// WETH -> DAI
@@ -59,7 +54,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint24(0),
address(usv2Executor),
encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, inTransferType
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, transferType
)
);
@@ -74,7 +69,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
DAI_USDC_POOL,
ALICE,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
)
);
@@ -85,9 +80,8 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
// Trade 1 WETH for USDC through DAI and WBTC - see _getSplitSwaps for more info
uint256 amountIn = 1 ether;
deal(WETH_ADDR, ALICE, amountIn);
deal(WETH_ADDR, address(tychoRouterAddr), amountIn);
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes[] memory swaps = _getSplitSwaps(false);
tychoRouter.exposedSplitSwap(amountIn, 4, pleEncode(swaps));
vm.stopPrank();
@@ -138,7 +132,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSplitSwaps(false);
bytes[] memory swaps = _getSplitSwaps(true);
tychoRouter.splitSwap(
amountIn,
@@ -149,6 +143,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
false,
4,
ALICE,
true,
pleEncode(swaps)
);
@@ -165,7 +160,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes[] memory swaps = _getSplitSwaps(false);
bytes[] memory swaps = _getSplitSwaps(true);
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
tychoRouter.splitSwap(
@@ -177,6 +172,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
false,
4,
ALICE,
true,
pleEncode(swaps)
);
vm.stopPrank();
@@ -190,7 +186,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE);
// Approve less than the amountIn
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1);
bytes[] memory swaps = _getSplitSwaps(false);
bytes[] memory swaps = _getSplitSwaps(true);
vm.expectRevert();
tychoRouter.splitSwap(
@@ -202,6 +198,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
false,
2,
ALICE,
true,
pleEncode(swaps)
);
@@ -270,7 +267,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
ALICE,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory swap = encodeSplitSwap(
@@ -319,7 +316,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap = encodeSplitSwap(
@@ -365,10 +362,10 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
// │ │
// └─ (USV3, 40% split) ──> WETH ─┘
uint256 amountIn = 100 * 10 ** 6;
deal(USDC_ADDR, ALICE, amountIn);
// Assume funds have already been transferred to tychoRouter
deal(USDC_ADDR, tychoRouterAddr, amountIn);
vm.startPrank(ALICE);
// Approve the TychoRouter to spend USDC
IERC20(USDC_ADDR).approve(tychoRouterAddr, amountIn);
bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap(
USDC_ADDR,
@@ -376,7 +373,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tychoRouterAddr,
USDC_WETH_USV3,
true,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory usdcWethV3Pool2ZeroOneData = encodeUniswapV3Swap(
@@ -385,7 +382,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tychoRouterAddr,
USDC_WETH_USV3_2,
true,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory wethUsdcV2OneZeroData = encodeUniswapV2Swap(
@@ -393,7 +390,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
USDC_WETH_USV2,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes[] memory swaps = new bytes[](3);
@@ -426,6 +423,74 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99654537);
}
function testSplitInputIllegalTransfers() public {
// This test attempts to perform multiple `transferFrom`s - which is not
// permitted by the TychoRouter.
//
// The flow is:
// ┌─ (USV3, 60% split) ───┐
// │ │
// USDC ──────┤ ├────> WETH
// │ │
// └─ (USV3, 40% split) ───┘
uint256 amountIn = 100 * 10 ** 6;
// Assume funds have already been transferred to tychoRouter
deal(USDC_ADDR, ALICE, amountIn);
vm.startPrank(ALICE);
IERC20(USDC_ADDR).approve(tychoRouterAddr, amountIn);
bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap(
USDC_ADDR,
WETH_ADDR,
tychoRouterAddr,
USDC_WETH_USV3,
true,
RestrictTransferFrom.TransferType.Transfer
);
bytes memory usdcWethV3Pool2ZeroOneData = encodeUniswapV3Swap(
USDC_ADDR,
WETH_ADDR,
tychoRouterAddr,
USDC_WETH_USV3_2,
true,
RestrictTransferFrom.TransferType.Transfer
);
bytes[] memory swaps = new bytes[](2);
// USDC -> WETH (60% split)
swaps[0] = encodeSplitSwap(
uint8(0),
uint8(1),
(0xffffff * 60) / 100, // 60%
address(usv3Executor),
usdcWethV3Pool1ZeroOneData
);
// USDC -> WETH (40% remainder)
swaps[1] = encodeSplitSwap(
uint8(0),
uint8(1),
uint24(0),
address(usv3Executor),
usdcWethV3Pool2ZeroOneData
);
vm.expectRevert();
tychoRouter.splitSwap(
amountIn,
USDC_ADDR,
WETH_ADDR,
1,
false,
false,
2,
ALICE,
true,
pleEncode(swaps)
);
vm.stopPrank();
}
function testSplitOutputCyclicSwapInternalMethod() public {
// This test has start and end tokens that are the same
// The flow is:
@@ -443,7 +508,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
USDC_WETH_USV2,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory usdcWethV3Pool1OneZeroData = encodeUniswapV3Swap(
@@ -452,7 +517,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tychoRouterAddr,
USDC_WETH_USV3,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap(
@@ -461,7 +526,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tychoRouterAddr,
USDC_WETH_USV3_2,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes[] memory swaps = new bytes[](3);
@@ -504,7 +569,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
USDC_MAG7_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
bytes memory swap = encodeSplitSwap(

View File

@@ -28,6 +28,17 @@ contract TychoRouterExposed is TychoRouter {
return _unwrapETH(amount);
}
function tstoreExposed(
address tokenIn,
uint256 amountIn,
bool isPermit2,
bool transferFromNeeded
) external {
_tstoreTransferFromInfo(
tokenIn, amountIn, isPermit2, transferFromNeeded
);
}
function exposedSplitSwap(
uint256 amountIn,
uint256 nTokens,
@@ -185,7 +196,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
address target,
address receiver,
bool zero2one,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) internal pure returns (bytes memory) {
return
abi.encodePacked(tokenIn, target, receiver, zero2one, transferType);
@@ -197,7 +208,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
address receiver,
address target,
bool zero2one,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) internal view returns (bytes memory) {
IUniswapV3Pool pool = IUniswapV3Pool(target);
return abi.encodePacked(

View File

@@ -1,28 +1,29 @@
test_uniswap_v3_uniswap_v2:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000bf00692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb8004375dff511095cc5a197a54140a24efef3a416cbcdf9626bc03e24f779434178a73a0b4bad62ed000100525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010500
test_single_encoding_strategy_ekubo:20144a070000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000071a0cb889707d426a7a386870a03bc70d1b069759805cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000000000000000
test_uniswap_v3_uniswap_v3:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000100692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010000000000000000000000
test_balancer_v2_uniswap_v2:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010300525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20105000000000000000000000000000000000000000000000000
test_sequential_swap_strategy_encoder_no_permit2:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000100525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20105000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006852b03300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3b00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041558653365a24c992e17bbf18a802b91eb3cf936eeb1f214d3e5e6cad70ba57070c7e92db5c3f0399d7da505798950313ce1d0c399e54cca0049868ee0f0005501b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006852b03300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c34556c17e3de4636f371033dc793b59f217facb4643ee3113acf72d3c2d7a3d22cf66c34d8d9c7bc119ac2b0fb2781a2097aa743fd77718f2a4e5ef5f92acbf1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000
test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041c55b2e21396823d694e6da247eee51ef542d57d3c5901911b85b3c195d89a56d03e85c23251af0d3981b0e07ca05caaaca029ae5efba9d58178be2f38d08cced1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000200525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20105000000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_no_permit2:20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_single_swap_strategy_encoder_no_transfer_in:20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3b00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160a5490c6e80921cf7b1ad8e7a59a02febd09780fa88c17070663c5a78635116227c299592fac934afa013d95f8711a70fdeb54fc07a2c22352f9cf263924d931c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330105cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000
test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ff87cdaae49fca40cccacadeab4710cdbd41d4901ba28cdabf36dbe07b198887506a6d4960e95f586b5d39011f74d4d57d61a7585ca7b8bdd86f550a9973e5571b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010200692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000000000
test_single_encoding_strategy_curve_st_eth:20144a070000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010005cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041c55b2e21396823d694e6da247eee51ef542d57d3c5901911b85b3c195d89a56d03e85c23251af0d3981b0e07ca05caaaca029ae5efba9d58178be2f38d08cced1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200020000000000000000000000000000
test_single_encoding_strategy_curve:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000103cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410e67daacc0a64ee7e7a57211b347b4af6d4d8865acbb9f549395fe42cf5ea87e1ac51293305eef8aebba46d510052e6fdea42e2caf1c34624260108f32ea6a641b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501020000000000000000000000000000
test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417b597f22dfb861c6ac1ec78eecab17b5e8b8e389494a9d3c2f27efbc35dbd73846d4dc5fef1b9530c4d3b8d7065fbc4d190821c1b5efbfd41ce388163e3812911c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ff87cdaae49fca40cccacadeab4710cdbd41d4901ba28cdabf36dbe07b198887506a6d4960e95f586b5d39011f74d4d57d61a7585ca7b8bdd86f550a9973e5571b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950102006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000
test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ff87cdaae49fca40cccacadeab4710cdbd41d4901ba28cdabf36dbe07b198887506a6d4960e95f586b5d39011f74d4d57d61a7585ca7b8bdd86f550a9973e5571b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80102005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000
test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c55b2e21396823d694e6da247eee51ef542d57d3c5901911b85b3c195d89a56d03e85c23251af0d3981b0e07ca05caaaca029ae5efba9d58178be2f38d08cced1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950002005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950002005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010000000000000000000000000000000000000000000000000000000000
test_uniswap_v3_curve:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000100691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000105cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000
test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004153ce45df3a0b2c8061d920d172d594ee02cf37b1967001935d3a6055700a300f79f68cda34bd3c6f64bfd154a113a43aafd15e6443dcc402e5b6ac9263e6ff031c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501020072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010500691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001053ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598003ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000
test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0105
test_ekubo_encode_swap_multi:00ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001
test_single_encoding_strategy_maverick:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc201000000000000000000000000000000
test_encode_maverick_v2:40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00
test_uniswap_v3_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000bf00692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb8004375dff511095cc5a197a54140a24efef3a416cbcdf9626bc03e24f779434178a73a0b4bad62ed000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010200
test_single_encoding_strategy_ekubo:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000071a0cb889707d426a7a386870a03bc70d1b069759802cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000000000000000
test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000
test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041b70914df20e4b2675adc5a61d26b959e0fd21e7cce6503b884d5ba9e1db2c82164112e4dc362b9a3ef794a03fcfebe0710700028b79abe8b7de525288ac904991c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c8000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c33e9da2d2a5bea1087e27f42b9255fcaea06efa76558843f6192d571b15c09c5818e1a4b3a111bb6accc4cb8b583c318d2386e0e94cc87b9fcc4065db0d65611c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000
test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041f59744bfe3e154be7c72429eb93db7ef2ec00344feeb1376195a2e3f133437f7533c3df955de146e229e4a37fda22acbe11d9393215d14cf1e19334aebbf35e61b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000
test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000
test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e5679a7262a283109d2e312e4ffbec563f501f347efc00418f3a6701492635977583d4e2ff29904a33496b64f1bc2ac73796a546a495ab110cca86e6de1655b61c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000
test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160aa554efc922689a7f426a984f9fc278aa948b9c78f4e7cc0a5f177e5fb6859632b92d3326f710603d43d3a8ea5753c58a61316fd1bff8e0b388f74c98993b91b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000
test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000
test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000
test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000
test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a81d713c715dd501edf102a3c3a755a931e3c72e7322d66b620ece81d7c98bae3d46479041445649eeefdb6ccc09c2b933cd37eda3ae8c99034e84f5570eb2c31c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000
test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102
test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001
test_single_encoding_strategy_maverick:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_encode_maverick_v2:40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01
test_encode_uniswap_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0001

View File

@@ -46,7 +46,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
WETH_BAL_POOL_ID,
address(2),
true,
TokenTransfer.TransferType.NONE
RestrictTransferFrom.TransferType.None
);
(
@@ -55,7 +55,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
bytes32 poolId,
address receiver,
bool needsApproval,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = balancerV2Exposed.decodeParams(params);
assertEq(address(tokenIn), WETH_ADDR);
@@ -63,7 +63,9 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
assertEq(poolId, WETH_BAL_POOL_ID);
assertEq(receiver, address(2));
assertEq(needsApproval, true);
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE));
assertEq(
uint8(transferType), uint8(RestrictTransferFrom.TransferType.None)
);
}
function testDecodeParamsInvalidDataLength() public {
@@ -82,7 +84,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
WETH_BAL_POOL_ID,
BOB,
true,
TokenTransfer.TransferType.NONE
RestrictTransferFrom.TransferType.Transfer
);
deal(WETH_ADDR, address(balancerV2Exposed), amountIn);
@@ -104,7 +106,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
bytes32 poolId,
address receiver,
bool needsApproval,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = balancerV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), WETH_ADDR);
@@ -112,7 +114,9 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
assertEq(poolId, WETH_BAL_POOL_ID);
assertEq(receiver, BOB);
assertEq(needsApproval, true);
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE));
assertEq(
uint8(transferType), uint8(RestrictTransferFrom.TransferType.None)
);
}
function testSwapIntegration() public {

View File

@@ -37,7 +37,7 @@ contract CurveExecutorExposed is CurveExecutor {
int128 i,
int128 j,
bool tokenApprovalNeeded,
TokenTransfer.TransferType transferType,
RestrictTransferFrom.TransferType transferType,
address receiver
)
{
@@ -68,7 +68,7 @@ contract CurveExecutorTest is Test, Constants {
uint8(2),
uint8(0),
true,
TokenTransfer.TransferType.NONE,
RestrictTransferFrom.TransferType.None,
ALICE
);
@@ -80,7 +80,7 @@ contract CurveExecutorTest is Test, Constants {
int128 i,
int128 j,
bool tokenApprovalNeeded,
TokenTransfer.TransferType transferType,
RestrictTransferFrom.TransferType transferType,
address receiver
) = curveExecutorExposed.decodeData(data);
@@ -91,7 +91,9 @@ contract CurveExecutorTest is Test, Constants {
assertEq(i, 2);
assertEq(j, 0);
assertEq(tokenApprovalNeeded, true);
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE));
assertEq(
uint8(transferType), uint8(RestrictTransferFrom.TransferType.None)
);
assertEq(receiver, ALICE);
}
@@ -100,7 +102,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(DAI_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = _getData(DAI_ADDR, USDC_ADDR, TRIPOOL, 1, ALICE);
bytes memory data = _getData(
DAI_ADDR,
USDC_ADDR,
TRIPOOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -113,8 +122,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(ETH_ADDR_FOR_CURVE, STETH_ADDR, STETH_POOL, 1, ALICE);
bytes memory data = _getData(
ETH_ADDR_FOR_CURVE,
STETH_ADDR,
STETH_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -130,8 +145,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(WETH_ADDR, WBTC_ADDR, TRICRYPTO2_POOL, 3, ALICE);
bytes memory data = _getData(
WETH_ADDR,
WBTC_ADDR,
TRICRYPTO2_POOL,
3,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -144,7 +165,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 100 * 10 ** 6;
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = _getData(USDC_ADDR, SUSD_ADDR, SUSD_POOL, 1, ALICE);
bytes memory data = _getData(
USDC_ADDR,
SUSD_ADDR,
SUSD_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -157,8 +185,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(FRAX_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(FRAX_ADDR, USDC_ADDR, FRAX_USDC_POOL, 1, ALICE);
bytes memory data = _getData(
FRAX_ADDR,
USDC_ADDR,
FRAX_USDC_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -171,8 +205,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 100 * 10 ** 6;
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(USDC_ADDR, USDE_ADDR, USDE_USDC_POOL, 1, ALICE);
bytes memory data = _getData(
USDC_ADDR,
USDE_ADDR,
USDE_USDC_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -185,8 +225,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 100 * 10 ** 6;
deal(DOLA_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(DOLA_ADDR, FRAXPYUSD_POOL, DOLA_FRAXPYUSD_POOL, 1, ALICE);
bytes memory data = _getData(
DOLA_ADDR,
FRAXPYUSD_POOL,
DOLA_FRAXPYUSD_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -200,8 +246,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 initialBalance = address(ALICE).balance; // this address already has some ETH assigned to it
deal(XYO_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(XYO_ADDR, ETH_ADDR_FOR_CURVE, ETH_XYO_POOL, 2, ALICE);
bytes memory data = _getData(
XYO_ADDR,
ETH_ADDR_FOR_CURVE,
ETH_XYO_POOL,
2,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -214,8 +266,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1000 ether;
deal(BSGG_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(BSGG_ADDR, USDT_ADDR, BSGG_USDT_POOL, 2, ALICE);
bytes memory data = _getData(
BSGG_ADDR,
USDT_ADDR,
BSGG_USDT_POOL,
2,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -228,8 +286,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(WETH_ADDR, USDC_ADDR, TRICRYPTO_POOL, 2, ALICE);
bytes memory data = _getData(
WETH_ADDR,
USDC_ADDR,
TRICRYPTO_POOL,
2,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -242,8 +306,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(UWU_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(UWU_ADDR, WETH_ADDR, UWU_WETH_POOL, 2, ALICE);
bytes memory data = _getData(
UWU_ADDR,
WETH_ADDR,
UWU_WETH_POOL,
2,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -256,8 +326,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 1 ether;
deal(USDT_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(USDT_ADDR, CRVUSD_ADDR, CRVUSD_USDT_POOL, 1, ALICE);
bytes memory data = _getData(
USDT_ADDR,
CRVUSD_ADDR,
CRVUSD_USDT_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -270,8 +346,14 @@ contract CurveExecutorTest is Test, Constants {
uint256 amountIn = 100 * 10 ** 9; // 9 decimals
deal(WTAO_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(WTAO_ADDR, WSTTAO_ADDR, WSTTAO_WTAO_POOL, 1, ALICE);
bytes memory data = _getData(
WTAO_ADDR,
WSTTAO_ADDR,
WSTTAO_WTAO_POOL,
1,
ALICE,
RestrictTransferFrom.TransferType.None
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
@@ -284,7 +366,8 @@ contract CurveExecutorTest is Test, Constants {
address tokenOut,
address pool,
uint8 poolType,
address receiver
address receiver,
RestrictTransferFrom.TransferType transferType
) internal view returns (bytes memory data) {
(int128 i, int128 j) = _getIndexes(tokenIn, tokenOut, pool);
data = abi.encodePacked(
@@ -295,7 +378,7 @@ contract CurveExecutorTest is Test, Constants {
uint8(uint256(uint128(i))),
uint8(uint256(uint128(j))),
true,
TokenTransfer.TransferType.NONE,
transferType,
receiver
);
}

View File

@@ -3,7 +3,7 @@ pragma solidity ^0.8.26;
import "../TestUtils.sol";
import {Constants} from "../Constants.sol";
import {EkuboExecutor, TokenTransfer} from "@src/executors/EkuboExecutor.sol";
import "@src/executors/EkuboExecutor.sol";
import {ICore} from "@ekubo/interfaces/ICore.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol";
@@ -45,7 +45,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
uint256 usdcBalanceBeforeExecutor = USDC.balanceOf(address(executor));
bytes memory data = abi.encodePacked(
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL), // transferType (transfer from executor to core)
uint8(RestrictTransferFrom.TransferType.Transfer), // transfer type (transfer from executor to core)
address(executor), // receiver
NATIVE_TOKEN_ADDRESS, // tokenIn
USDC_ADDR, // tokenOut
@@ -82,7 +82,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
uint256 ethBalanceBeforeExecutor = address(executor).balance;
bytes memory data = abi.encodePacked(
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL), // transferType (transfer from executor to core)
uint8(RestrictTransferFrom.TransferType.Transfer), // transferNeeded (transfer from executor to core)
address(executor), // receiver
USDC_ADDR, // tokenIn
NATIVE_TOKEN_ADDRESS, // tokenOut
@@ -140,7 +140,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
// Same test case as in swap_encoder::tests::ekubo::test_encode_swap_multi
function testMultiHopSwap() public {
bytes memory data = abi.encodePacked(
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL), // transferType
uint8(RestrictTransferFrom.TransferType.Transfer), // transferNeeded (transfer from executor to core)
address(executor), // receiver
NATIVE_TOKEN_ADDRESS, // tokenIn
USDC_ADDR, // tokenOut of 1st swap

View File

@@ -17,7 +17,7 @@ contract MaverickV2ExecutorExposed is MaverickV2Executor {
IERC20 tokenIn,
address target,
address receiver,
TransferType transferType
RestrictTransferFrom.TransferType transferType
)
{
return _decodeData(data);
@@ -43,14 +43,14 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
GHO_ADDR,
GHO_USDC_POOL,
address(2),
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
(
IERC20 tokenIn,
address target,
address receiver,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = maverickV2Exposed.decodeParams(params);
assertEq(address(tokenIn), GHO_ADDR);
@@ -58,7 +58,7 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
assertEq(receiver, address(2));
assertEq(
uint8(transferType),
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
uint8(RestrictTransferFrom.TransferType.Transfer)
);
}
@@ -76,7 +76,7 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
GHO_ADDR,
GHO_USDC_POOL,
BOB,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
deal(GHO_ADDR, address(maverickV2Exposed), amountIn);
@@ -98,7 +98,7 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
IERC20 tokenIn,
address pool,
address receiver,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = maverickV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), GHO_ADDR);
@@ -106,7 +106,7 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
assertEq(receiver, BOB);
assertEq(
uint8(transferType),
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
uint8(RestrictTransferFrom.TransferType.Transfer)
);
}

View File

@@ -1,11 +1,11 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
import "../TestUtils.sol";
import "@src/executors/UniswapV2Executor.sol";
import "@src/executors/TokenTransfer.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol";
import {Permit2TestHelper} from "../Permit2TestHelper.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
contract UniswapV2ExecutorExposed is UniswapV2Executor {
constructor(
@@ -23,7 +23,7 @@ contract UniswapV2ExecutorExposed is UniswapV2Executor {
address target,
address receiver,
bool zeroForOne,
TransferType transferType
RestrictTransferFrom.TransferType transferType
)
{
return _decodeData(data);
@@ -52,7 +52,7 @@ contract FakeUniswapV2Pool {
}
}
contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
contract UniswapV2ExecutorTest is Constants, Permit2TestHelper, TestUtils {
using SafeERC20 for IERC20;
UniswapV2ExecutorExposed uniswapV2Exposed;
@@ -60,7 +60,6 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
UniswapV2ExecutorExposed pancakeswapV2Exposed;
IERC20 WETH = IERC20(WETH_ADDR);
IERC20 DAI = IERC20(DAI_ADDR);
IAllowanceTransfer permit2;
function setUp() public {
uint256 forkBlock = 17323404;
@@ -80,7 +79,6 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
PERMIT2_ADDRESS,
25
);
permit2 = IAllowanceTransfer(PERMIT2_ADDRESS);
}
function testDecodeParams() public view {
@@ -89,7 +87,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
address(2),
address(3),
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
(
@@ -97,7 +95,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
address target,
address receiver,
bool zeroForOne,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = uniswapV2Exposed.decodeParams(params);
assertEq(address(tokenIn), WETH_ADDR);
@@ -105,8 +103,8 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
assertEq(receiver, address(3));
assertEq(zeroForOne, false);
assertEq(
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL),
uint8(transferType)
uint8(transferType),
uint8(RestrictTransferFrom.TransferType.Transfer)
);
}
@@ -163,7 +161,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
WETH_DAI_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
RestrictTransferFrom.TransferType.Transfer
);
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -173,59 +171,6 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
assertGe(finalBalance, amountOut);
}
function testSwapWithTransferFrom() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL)
);
deal(WETH_ADDR, address(this), amountIn);
IERC20(WETH_ADDR).approve(address(uniswapV2Exposed), amountIn);
uniswapV2Exposed.swap(amountIn, protocolData);
uint256 finalBalance = DAI.balanceOf(BOB);
assertGe(finalBalance, amountOut);
}
function testSwapWithPermit2TransferFrom() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
ALICE,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL)
);
deal(WETH_ADDR, ALICE, amountIn);
vm.startPrank(ALICE);
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(
WETH_ADDR, address(uniswapV2Exposed), amountIn
);
// Assume the permit2.approve method will be called from the TychoRouter
// Replicate this scenario in this test.
permit2.permit(ALICE, permitSingle, signature);
uniswapV2Exposed.swap(amountIn, protocolData);
vm.stopPrank();
uint256 finalBalance = DAI.balanceOf(ALICE);
assertGe(finalBalance, amountOut);
}
function testSwapNoTransfer() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
@@ -235,7 +180,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
WETH_DAI_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.NONE)
RestrictTransferFrom.TransferType.None
);
deal(WETH_ADDR, address(this), amountIn);
@@ -248,27 +193,29 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
function testDecodeIntegration() public view {
bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f564000000000000000000000000000000000000000010000";
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f564000000000000000000000000000000000000000010001";
(
IERC20 tokenIn,
address target,
address receiver,
bool zeroForOne,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = uniswapV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), WETH_ADDR);
assertEq(target, 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
assertEq(receiver, 0x0000000000000000000000000000000000000001);
assertEq(zeroForOne, false);
// TRANSFER = 0
assertEq(0, uint8(transferType));
assertEq(
uint8(transferType),
uint8(RestrictTransferFrom.TransferType.Transfer)
);
}
function testSwapIntegration() public {
bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000";
loadCallDataFromFile("test_encode_uniswap_v2");
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -287,7 +234,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
fakePool,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
RestrictTransferFrom.TransferType.Transfer
);
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -307,7 +254,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
USDC_MAG7_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
RestrictTransferFrom.TransferType.Transfer
);
deal(BASE_USDC, address(uniswapV2Exposed), amountIn);

View File

@@ -22,7 +22,7 @@ contract UniswapV3ExecutorExposed is UniswapV3Executor {
address receiver,
address target,
bool zeroForOne,
TransferType transferType
RestrictTransferFrom.TransferType transferType
)
{
return _decodeData(data);
@@ -71,7 +71,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
address(2),
address(3),
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
(
@@ -81,7 +81,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
address receiver,
address target,
bool zeroForOne,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) = uniswapV3Exposed.decodeData(data);
assertEq(tokenIn, WETH_ADDR);
@@ -92,7 +92,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
assertEq(zeroForOne, false);
assertEq(
uint8(transferType),
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
uint8(RestrictTransferFrom.TransferType.Transfer)
);
}
@@ -109,7 +109,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
address(this),
DAI_WETH_USV3,
zeroForOne,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
uint256 amountOut = uniswapV3Exposed.swap(amountIn, data);
@@ -150,7 +150,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
WETH_ADDR,
DAI_ADDR,
poolFee,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL,
RestrictTransferFrom.TransferType.Transfer,
address(uniswapV3Exposed)
);
uint256 dataOffset = 3; // some offset
@@ -184,7 +184,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
address(this),
fakePool,
zeroForOne,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
RestrictTransferFrom.TransferType.Transfer
);
vm.expectRevert(UniswapV3Executor__InvalidTarget.selector);
@@ -197,7 +197,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
address receiver,
address target,
bool zero2one,
TokenTransfer.TransferType transferType
RestrictTransferFrom.TransferType transferType
) internal view returns (bytes memory) {
IUniswapV3Pool pool = IUniswapV3Pool(target);
return abi.encodePacked(

View File

@@ -4,7 +4,6 @@ pragma solidity ^0.8.26;
import "../../src/executors/UniswapV4Executor.sol";
import "../TestUtils.sol";
import "./UniswapV4Utils.sol";
import "@src/executors/TokenTransfer.sol";
import "@src/executors/UniswapV4Executor.sol";
import {Constants} from "../Constants.sol";
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
@@ -22,7 +21,7 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
address tokenIn,
address tokenOut,
bool zeroForOne,
TokenTransfer.TransferType transferType,
RestrictTransferFrom.TransferType transferType,
address receiver,
UniswapV4Pool[] memory pools
)
@@ -53,8 +52,6 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
int24 tickSpacing1 = 60;
uint24 pool2Fee = 1000;
int24 tickSpacing2 = -10;
TokenTransfer.TransferType transferType =
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL;
UniswapV4Executor.UniswapV4Pool[] memory pools =
new UniswapV4Executor.UniswapV4Pool[](2);
@@ -70,14 +67,19 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
});
bytes memory data = UniswapV4Utils.encodeExactInput(
USDE_ADDR, USDT_ADDR, zeroForOne, transferType, ALICE, pools
USDE_ADDR,
USDT_ADDR,
zeroForOne,
RestrictTransferFrom.TransferType.Transfer,
ALICE,
pools
);
(
address tokenIn,
address tokenOut,
bool zeroForOneDecoded,
TokenTransfer.TransferType transferTypeDecoded,
RestrictTransferFrom.TransferType transferType,
address receiver,
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
) = uniswapV4Exposed.decodeData(data);
@@ -85,7 +87,10 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
assertEq(tokenIn, USDE_ADDR);
assertEq(tokenOut, USDT_ADDR);
assertEq(zeroForOneDecoded, zeroForOne);
assertEq(uint8(transferTypeDecoded), uint8(transferType));
assertEq(
uint8(transferType),
uint8(RestrictTransferFrom.TransferType.Transfer)
);
assertEq(receiver, ALICE);
assertEq(decodedPools.length, 2);
assertEq(decodedPools[0].intermediaryToken, USDT_ADDR);
@@ -115,7 +120,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
USDE_ADDR,
USDT_ADDR,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL,
RestrictTransferFrom.TransferType.Transfer,
ALICE,
pools
);
@@ -172,7 +177,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
USDE_ADDR,
WBTC_ADDR,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL,
RestrictTransferFrom.TransferType.Transfer,
ALICE,
pools
);

View File

@@ -8,7 +8,7 @@ library UniswapV4Utils {
address tokenIn,
address tokenOut,
bool zeroForOne,
UniswapV4Executor.TransferType transferType,
RestrictTransferFrom.TransferType transferType,
address receiver,
UniswapV4Executor.UniswapV4Pool[] memory pools
) public pure returns (bytes memory) {

View File

@@ -34,11 +34,12 @@ pub static IN_TRANSFER_REQUIRED_PROTOCOLS: LazyLock<HashSet<&'static str>> = Laz
set
});
// The protocols here are a subset of the ones defined in IN_TRANSFER_REQUIRED_PROTOCOLS. The tokens
// can not be sent directly from the previous pool into a pool of this protocol. The tokens need to
// be sent to the router and only then transferred into the pool. This is the case for uniswap v3
// because of the callback logic. The only way for this to work it would be to call the second swap
// during the callback of the first swap. This is currently not supported.
// The protocols here are a subset of the ones defined in IN_TRANSFER_REQUIRED_PROTOCOLS. The in
// transfer needs to be performed inside the callback logic. This means, the tokens can not be sent
// directly from the previous pool into a pool of this protocol. The tokens need to be sent to the
// router and only then transferred into the pool. This is the case for uniswap v3 because of the
// callback logic. The only way for this to work it would be to call the second swap during the
// callback of the first swap. This is currently not supported.
pub static CALLBACK_CONSTRAINED_PROTOCOLS: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
let mut set = HashSet::new();
set.insert("uniswap_v3");

View File

@@ -33,6 +33,7 @@ use crate::encoding::{
/// * `selector`: String, the selector for the swap function in the router contract
/// * `router_address`: Address of the router to be used to execute swaps
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
/// * `token_in_already_in_router`: bool, whether the token in is already in the router
#[derive(Clone)]
pub struct SingleSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
@@ -40,6 +41,7 @@ pub struct SingleSwapStrategyEncoder {
selector: String,
router_address: Bytes,
transfer_optimization: TransferOptimization,
token_in_already_in_router: bool,
}
impl SingleSwapStrategyEncoder {
@@ -55,10 +57,10 @@ impl SingleSwapStrategyEncoder {
} else {
(
None,
"singleSwap(uint256,address,address,uint256,bool,bool,address,bytes)".to_string(),
"singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
.to_string(),
)
};
let permit2_is_active = permit2.is_some();
Ok(Self {
permit2,
selector,
@@ -67,10 +69,10 @@ impl SingleSwapStrategyEncoder {
transfer_optimization: TransferOptimization::new(
chain.native_token()?,
chain.wrapped_token()?,
permit2_is_active,
token_in_already_in_router,
router_address,
),
token_in_already_in_router,
})
}
@@ -125,16 +127,16 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
let swap_receiver =
if !unwrap { solution.receiver.clone() } else { self.router_address.clone() };
let transfer_type = self
let transfer = self
.transfer_optimization
.get_transfer_type(grouped_swap.clone(), solution.given_token.clone(), wrap, false);
.get_transfers(grouped_swap.clone(), solution.given_token.clone(), wrap, false);
let encoding_context = EncodingContext {
receiver: swap_receiver.clone(),
exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer_type: transfer_type.clone(),
transfer_type: transfer,
};
let mut grouped_protocol_data: Vec<u8> = vec![];
@@ -178,6 +180,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
wrap,
unwrap,
bytes_to_address(&solution.receiver)?,
!self.token_in_already_in_router,
swap_data,
)
.abi_encode()
@@ -210,6 +213,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
/// * `sequential_swap_validator`: SequentialSwapValidator, responsible for checking validity of
/// sequential swap solutions
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
/// * `token_in_already_in_router`: bool, whether the token in is already in the router
#[derive(Clone)]
pub struct SequentialSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
@@ -220,6 +224,7 @@ pub struct SequentialSwapStrategyEncoder {
wrapped_address: Bytes,
sequential_swap_validator: SequentialSwapValidator,
transfer_optimization: TransferOptimization,
token_in_already_in_router: bool,
}
impl SequentialSwapStrategyEncoder {
@@ -235,11 +240,10 @@ impl SequentialSwapStrategyEncoder {
} else {
(
None,
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bytes)"
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
.to_string(),
)
};
let permit2_is_active = permit2.is_some();
Ok(Self {
permit2,
selector,
@@ -251,10 +255,10 @@ impl SequentialSwapStrategyEncoder {
transfer_optimization: TransferOptimization::new(
chain.native_token()?,
chain.wrapped_token()?,
permit2_is_active,
token_in_already_in_router,
router_address,
),
token_in_already_in_router,
})
}
@@ -311,9 +315,10 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
.transfer_optimization
.get_receiver(solution.receiver.clone(), next_swap)?;
next_in_between_swap_optimization_allowed = next_swap_optimization;
let transfer_type = self
let transfer = self
.transfer_optimization
.get_transfer_type(
.get_transfers(
grouped_swap.clone(),
solution.given_token.clone(),
wrap,
@@ -325,7 +330,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer_type: transfer_type.clone(),
transfer_type: transfer,
};
let mut grouped_protocol_data: Vec<u8> = vec![];
@@ -374,6 +379,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
wrap,
unwrap,
bytes_to_address(&solution.receiver)?,
!self.token_in_already_in_router,
encoded_swaps,
)
.abi_encode()
@@ -406,6 +412,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
/// solutions
/// * `router_address`: Address of the router to be used to execute swaps
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
/// * `token_in_already_in_router`: bool, whether the token in is already in the router
#[derive(Clone)]
pub struct SplitSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
@@ -416,6 +423,7 @@ pub struct SplitSwapStrategyEncoder {
split_swap_validator: SplitSwapValidator,
router_address: Bytes,
transfer_optimization: TransferOptimization,
token_in_already_in_router: bool,
}
impl SplitSwapStrategyEncoder {
@@ -431,11 +439,10 @@ impl SplitSwapStrategyEncoder {
} else {
(
None,
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bytes)"
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
.to_string(),
)
};
let permit2_is_active = permit2.is_some();
Ok(Self {
permit2,
selector,
@@ -447,10 +454,10 @@ impl SplitSwapStrategyEncoder {
transfer_optimization: TransferOptimization::new(
chain.native_token()?,
chain.wrapped_token()?,
permit2_is_active,
token_in_already_in_router,
router_address,
),
token_in_already_in_router,
})
}
@@ -553,16 +560,16 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
} else {
self.router_address.clone()
};
let transfer_type = self
let transfer = self
.transfer_optimization
.get_transfer_type(grouped_swap.clone(), solution.given_token.clone(), wrap, false);
.get_transfers(grouped_swap.clone(), solution.given_token.clone(), wrap, false);
let encoding_context = EncodingContext {
receiver: swap_receiver.clone(),
exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer_type: transfer_type.clone(),
transfer_type: transfer,
};
let mut grouped_protocol_data: Vec<u8> = vec![];
@@ -621,6 +628,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
unwrap,
U256::from(tokens_len),
bytes_to_address(&solution.receiver)?,
!self.token_in_already_in_router,
encoded_swaps,
)
.abi_encode()
@@ -750,7 +758,7 @@ mod tests {
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
let expected_input = [
"30ace1b1", // Function selector
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount out
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
&expected_min_amount_encoded, // min amount out
@@ -772,7 +780,7 @@ mod tests {
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"02", // transfer type
"00", // transfer type TransferFrom
"0000000000000000000000000000", // padding
));
let hex_calldata = encode(&calldata);
@@ -839,7 +847,7 @@ mod tests {
.unwrap();
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
let expected_input = [
"20144a07", // Function selector
"5c4b639c", // Function selector
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
@@ -847,7 +855,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000001", // transfer from needed
"0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
// Swap data
@@ -856,8 +865,8 @@ mod tests {
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"01", // transfer type
"0000000000000000000000000000", // padding
"00", // transfer type TransferFrom
"0000000000000000000000000000", // padding
]
.join("");
@@ -921,7 +930,7 @@ mod tests {
.unwrap();
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
let expected_input = [
"20144a07", // Function selector
"5c4b639c", // Function selector
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
@@ -929,7 +938,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000000", // transfer from not needed
"0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
// Swap data
@@ -938,7 +948,7 @@ mod tests {
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"00", // transfer type
"01", // transfer type Transfer
"0000000000000000000000000000", // padding
]
.join("");
@@ -1191,7 +1201,7 @@ mod tests {
let hex_calldata = encode(&calldata);
let expected = String::from(concat!(
"e8a980d7", /* function selector */
"e21dd0d3", /* function selector */
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou
@@ -1199,7 +1209,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", /* length ple
"0000000000000000000000000000000000000000000000000000000000000001", /* transfer from needed */
"0000000000000000000000000000000000000000000000000000000000000120", /* length ple
* encode */
"00000000000000000000000000000000000000000000000000000000000000a8",
// swap 1
@@ -1209,7 +1220,7 @@ mod tests {
"bb2b8038a1640196fbe3e38816f3e67cba72d940", // component id
"004375dff511095cc5a197a54140a24efef3a416", // receiver (next pool)
"00", // zero to one
"01", // transfer type
"00", // transfer type TransferFrom
// swap 2
"0052", // swap length
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
@@ -1217,7 +1228,7 @@ mod tests {
"004375dff511095cc5a197a54140a24efef3a416", // component id
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user)
"01", // zero to one
"05", // transfer type - None
"02", // transfer type None
"000000000000000000000000000000000000000000000000", // padding
));
@@ -1336,7 +1347,7 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one
"02", // transfer type
"00", // transfer type TransferFrom
"0069", // ple encoded swaps
"2e234dae75c793f67a35089c9d99245e1c58470b", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
@@ -1345,7 +1356,7 @@ mod tests {
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one
"00", // transfer type
"01", // transfer type Transfer
"00000000000000000000", // padding
]
.join("");
@@ -2062,7 +2073,7 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one
"02", // transfer type
"00", // transfer type TransferFrom
"006e", // ple encoded swaps
"00", // token in index
"01", // token out index
@@ -2074,7 +2085,7 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"01", // zero2one
"02", // transfer type
"00", // transfer type TransferFrom
"0057", // ple encoded swaps
"01", // token in index
"00", // token out index
@@ -2084,7 +2095,7 @@ mod tests {
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id,
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"00", // transfer type
"01", // transfer type Transfer
"00000000000000" // padding
]
.join("");
@@ -2224,7 +2235,7 @@ mod tests {
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"01", // zero2one
"02", // transfer type
"00", // transfer type TransferFrom
"006e", // ple encoded swaps
"01", // token in index
"00", // token out index
@@ -2236,7 +2247,7 @@ mod tests {
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"00", // zero2one
"00", // transfer type
"01", // transfer type Transfer
"006e", // ple encoded swaps
"01", // token in index
"00", // token out index
@@ -2248,7 +2259,7 @@ mod tests {
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one
"00", // transfer type
"01", // transfer type Transfer
"00000000000000" // padding
]
.join("");
@@ -2625,7 +2636,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in
"6982508145454ce325ddbe47a25d4ec3d2311933", // group token in
"00", // zero2one
"02", // transfer type (transfer to router)
"00", // transfer type TransferFrom
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
// First pool params
"0000000000000000000000000000000000000000", // intermediary token (ETH)

View File

@@ -16,7 +16,6 @@ use crate::encoding::{
pub struct TransferOptimization {
native_token: Bytes,
wrapped_token: Bytes,
permit2: bool,
token_in_already_in_router: bool,
router_address: Bytes,
}
@@ -25,69 +24,58 @@ impl TransferOptimization {
pub fn new(
native_token: Bytes,
wrapped_token: Bytes,
permit2: bool,
token_in_already_in_router: bool,
router_address: Bytes,
) -> Self {
TransferOptimization {
native_token,
wrapped_token,
permit2,
token_in_already_in_router,
router_address,
}
}
/// Returns the transfer method that should be used for the given swap and solution.
pub fn get_transfer_type(
/// Returns the transfer type that should be used for the current transfer.
pub fn get_transfers(
&self,
swap: SwapGroup,
given_token: Bytes,
wrap: bool,
in_between_swap_optimization: bool,
) -> TransferType {
let is_first_swap = swap.token_in == given_token;
let in_transfer_required: bool =
IN_TRANSFER_REQUIRED_PROTOCOLS.contains(&swap.protocol_system.as_str());
let is_first_swap = swap.token_in == given_token;
if swap.token_in == self.native_token {
// Funds are already in router. All protocols currently take care of native transfers.
TransferType::None
} else if (swap.token_in == self.wrapped_token) && wrap {
// Wrapping already happened in the router so we can just use a normal transfer.
TransferType::TransferToProtocol
// Wrapping already happened in the router so, we just do a normal transfer.
TransferType::Transfer
} else if is_first_swap {
if in_transfer_required {
if self.token_in_already_in_router {
// Transfer from router to pool.
TransferType::TransferToProtocol
} else if self.permit2 {
// Transfer from swapper to pool using permit2.
TransferType::TransferPermit2ToProtocol
TransferType::Transfer
} else {
// Transfer from swapper to pool.
TransferType::TransferFromToProtocol
// Transfer from swapper to pool
TransferType::TransferFrom
}
// in transfer is not necessary for these protocols. Only make a transfer if the
// tokens are not already in the router
// in transfer is not necessary for these protocols. Only make a transfer from the
// swapper to the router if the tokens are not already in the router
} else if !self.token_in_already_in_router {
if self.permit2 {
// Transfer from swapper to router using permit2.
TransferType::TransferPermit2ToRouter
} else {
// Transfer from swapper to router.
TransferType::TransferFromToRouter
}
// Transfer from swapper to router using.
TransferType::TransferFrom
} else {
TransferType::None
}
// all other swaps
// all other swaps that not the first one
} else if !in_transfer_required || in_between_swap_optimization {
// funds should already be in the router or in the next pool
TransferType::None
} else {
TransferType::TransferToProtocol
TransferType::Transfer
}
}
@@ -155,146 +143,63 @@ mod tests {
Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f")
}
#[test]
fn test_first_swap_transfer_from_permit2() {
#[rstest]
// First swap tests
// WETH -(univ2)-> DAI we expect a transfer from the user to the protocol
#[case(weth(), weth(), "uniswap_v2".to_string(), false, false,false, TransferType::TransferFrom)]
// Native token swap. No transfer is needed
#[case(eth(), eth(), "uniswap_v2".to_string(),false, false,false, TransferType::None)]
// ETH -(wrap)-> WETH -(univ2)-> DAI. Only a transfer from the router into the protocol is
// needed
#[case(eth(), weth(), "uniswap_v2".to_string(),true, false,false,TransferType::Transfer)]
// USDC -(univ2)-> DAI and the tokens are already in the router. Only a transfer from the router
// to the protocol is needed
#[case(usdc(), usdc(), "uniswap_v2".to_string(),false, true,false, TransferType::Transfer)]
// USDC -(curve)-> DAI and the tokens are already in the router. No transfer is needed
#[case(usdc(), usdc(), "vm:curve".to_string(),false, true, false,TransferType::None)]
// other swaps tests
// tokens need to be transferred into the pool
#[case(weth(), usdc(), "uniswap_v2".to_string(), false, false,false, TransferType::Transfer)]
// tokens are already in the pool (optimization)
#[case(weth(), usdc(), "uniswap_v2".to_string(), false, false, true, TransferType::None)]
// tokens are already in the router and don't need a transfer
#[case(weth(), usdc(), "vm:curve".to_string(), false, false, false, TransferType::None)]
fn test_get_transfers(
#[case] given_token: Bytes,
#[case] swap_token_in: Bytes,
#[case] protocol: String,
#[case] wrap: bool,
#[case] token_in_already_in_router: bool,
#[case] in_between_swap_optimization: bool,
#[case] expected_transfer: TransferType,
) {
// The swap token is the same as the given token, which is not the native token
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: weth(),
let swaps = vec![Swap {
component: ProtocolComponent {
protocol_system: "uniswap_v2".to_string(),
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
..Default::default()
},
token_in: swap_token_in.clone(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), true, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), weth(), false, false);
assert_eq!(transfer_method, TransferType::TransferPermit2ToProtocol);
}
#[test]
fn test_first_swap_transfer_from() {
// The swap token is the same as the given token, which is not the native token
}];
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: weth(),
protocol_system: protocol,
token_in: swap_token_in,
token_out: dai(),
split: 0f64,
swaps: vec![],
swaps,
};
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), weth(), false, false);
assert_eq!(transfer_method, TransferType::TransferFromToProtocol);
}
#[test]
fn test_first_swap_native() {
// The swap token is the same as the given token, and it's the native token.
// No transfer action is needed.
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: eth(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), eth(), false, false);
assert_eq!(transfer_method, TransferType::None);
}
#[test]
fn test_first_swap_wrapped() {
// The swap token is NOT the same as the given token, but we are wrapping.
// Since the swap's token in is the wrapped token - this is the first swap.
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: weth(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), eth(), true, false);
assert_eq!(transfer_method, TransferType::TransferToProtocol);
}
#[test]
fn test_not_first_swap() {
// The swap token is NOT the same as the given token, and we are NOT wrapping.
// Thus, this is not the first swap.
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: usdc(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), weth(), false, false);
assert_eq!(transfer_method, TransferType::TransferToProtocol);
}
#[test]
fn test_not_first_swap_funds_in_router() {
// Not the first swap and the protocol requires the funds to be in the router (which they
// already are, so the transfer type is None)
let swap = SwapGroup {
protocol_system: "vm:curve".to_string(),
token_in: usdc(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), weth(), false, false);
assert_eq!(transfer_method, TransferType::None);
}
#[test]
fn test_not_first_swap_in_between_swap_optimization() {
// Not the first swap and the in between swaps are optimized. The funds should already be in
// the next pool or in the router
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: usdc(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), weth(), false, true);
assert_eq!(transfer_method, TransferType::None);
}
#[test]
fn test_first_swap_tokens_already_in_router_optimization() {
// It is the first swap, tokens are already in the router and the protocol requires the
// transfer in
let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(),
token_in: usdc(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, true, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), usdc(), false, false);
assert_eq!(transfer_method, TransferType::TransferToProtocol);
}
#[test]
fn test_first_swap_tokens_already_in_router_no_transfer_needed_optimization() {
// It is the first swap, tokens are already in the router and the protocol does not require
// the transfer in
let swap = SwapGroup {
protocol_system: "vm:curve".to_string(),
token_in: usdc(),
token_out: dai(),
split: 0f64,
swaps: vec![],
};
let optimization = TransferOptimization::new(eth(), weth(), false, true, router_address());
let transfer_method = optimization.get_transfer_type(swap.clone(), usdc(), false, false);
assert_eq!(transfer_method, TransferType::None);
let optimization =
TransferOptimization::new(eth(), weth(), token_in_already_in_router, router_address());
let transfer = optimization.get_transfers(
swap.clone(),
given_token,
wrap,
in_between_swap_optimization,
);
assert_eq!(transfer, expected_transfer);
}
fn receiver() -> Bytes {
@@ -319,7 +224,7 @@ mod tests {
#[case] expected_receiver: Bytes,
#[case] expected_optimization: bool,
) {
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address());
let optimization = TransferOptimization::new(eth(), weth(), false, router_address());
let next_swap = if protocol.is_none() {
None

View File

@@ -592,7 +592,7 @@ mod tests {
#[test]
fn test_encode_uniswap_v2() {
let usv2_pool = ProtocolComponent {
id: String::from("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
id: String::from("0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11"),
..Default::default()
};
@@ -605,12 +605,12 @@ mod tests {
split: 0f64,
};
let encoding_context = EncodingContext {
receiver: Bytes::from("0x0000000000000000000000000000000000000001"),
receiver: Bytes::from("0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e"), // BOB
exact_out: false,
router_address: Some(Bytes::zero(20)),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV2SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
@@ -628,15 +628,16 @@ mod tests {
// in token
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
// component id
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
"a478c2975ab1ea89e8196811f51a7b7ade33eb11",
// receiver
"0000000000000000000000000000000000000001",
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
// zero for one
"00",
// transfer type (transfer)
"00",
// transfer type Transfer
"01",
))
);
write_calldata_to_file("test_encode_uniswap_v2", hex_swap.as_str());
}
}
@@ -668,7 +669,7 @@ mod tests {
router_address: Some(Bytes::zero(20)),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV3SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
@@ -695,8 +696,8 @@ mod tests {
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
// zero for one
"00",
// transfer type (transfer)
"00",
// transfer type Transfer
"01",
))
);
}
@@ -759,8 +760,8 @@ mod tests {
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
// approval needed
"01",
// transfer type
"05"
// transfer type None
"02"
))
);
write_calldata_to_file("test_encode_balancer_v2", hex_swap.as_str());
@@ -804,7 +805,7 @@ mod tests {
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV4SwapEncoder::new(
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
@@ -826,8 +827,8 @@ mod tests {
"dac17f958d2ee523a2206206994597c13d831ec7",
// zero for one
"01",
// transfer type
"00",
// transfer type Transfer
"01",
// receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
// pool params:
@@ -875,7 +876,7 @@ mod tests {
group_token_in: group_token_in.clone(),
// Token out is the same as the group token out
group_token_out: token_out.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV4SwapEncoder::new(
@@ -918,7 +919,7 @@ mod tests {
router_address: Some(router_address.clone()),
group_token_in: usde_address.clone(),
group_token_out: wbtc_address.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
// Setup - First sequence: USDE -> USDT
@@ -996,8 +997,8 @@ mod tests {
"2260fac5e5542a773aa44fbcfedf7c193bc2c599",
// zero for one
"01",
// transfer type
"00",
// transfer type Transfer
"01",
// receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
// pool params:
@@ -1053,7 +1054,7 @@ mod tests {
group_token_out: token_out.clone(),
exact_out: false,
router_address: Some(Bytes::default()),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let encoder =
@@ -1069,8 +1070,8 @@ mod tests {
assert_eq!(
hex_swap,
concat!(
// transfer type
"00",
// transfer type Transfer
"01",
// receiver
"ca4f73fe97d0b987a0d12b39bbd562c779bab6f6",
// group token in
@@ -1099,7 +1100,7 @@ mod tests {
group_token_out: group_token_out.clone(),
exact_out: false,
router_address: Some(Bytes::default()),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let first_swap = Swap {
@@ -1149,8 +1150,8 @@ mod tests {
combined_hex,
// transfer type
concat!(
// transfer type
"00",
// transfer type Transfer
"01",
// receiver
"ca4f73fe97d0b987a0d12b39bbd562c779bab6f6",
// group token in
@@ -1332,10 +1333,10 @@ mod tests {
"01",
// approval needed
"01",
// transfer type
"05",
// transfer type None
"02",
// receiver,
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
))
);
}
@@ -1403,10 +1404,10 @@ mod tests {
"00",
// approval needed
"01",
// transfer type
"05",
// transfer type None
"02",
// receiver
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
))
);
}
@@ -1484,10 +1485,10 @@ mod tests {
"01",
// approval needed
"01",
// transfer type
"05",
// transfer type None
"02",
// receiver
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
))
);
}
@@ -1516,7 +1517,7 @@ mod tests {
router_address: Some(Bytes::default()),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: TransferType::Transfer,
};
let encoder = MaverickV2SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
@@ -1539,8 +1540,8 @@ mod tests {
"14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67",
// receiver
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
// transfer from router to protocol
"00",
// transfer true
"01",
))
.to_lowercase()
);

View File

@@ -6,7 +6,7 @@ use tycho_common::Bytes;
use crate::encoding::{
errors::EncodingError,
evm::{
constants::GROUPABLE_PROTOCOLS,
constants::{GROUPABLE_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS},
group_swaps::group_swaps,
strategy_encoder::strategy_encoders::{
SequentialSwapStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder,
@@ -273,13 +273,20 @@ impl TychoExecutorEncoder {
let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() {
let transfer = if IN_TRANSFER_REQUIRED_PROTOCOLS
.contains(&swap.component.protocol_system.as_str())
{
TransferType::Transfer
} else {
TransferType::None
};
let encoding_context = EncodingContext {
receiver: receiver.clone(),
exact_out: solution.exact_out,
router_address: None,
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer_type: TransferType::TransferToProtocol,
transfer_type: transfer,
};
let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?;
grouped_protocol_data.extend(protocol_data);
@@ -461,7 +468,7 @@ mod tests {
Bytes::from_str("0x3ede3eca2a72b3aecc820e955b36f38437d01395").unwrap()
);
// single swap selector
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "20144a07");
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "5c4b639c");
}
#[test]
@@ -486,7 +493,7 @@ mod tests {
let transactions = transactions.unwrap();
assert_eq!(transactions.len(), 1);
// single swap selector
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "20144a07");
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "5c4b639c");
}
#[test]
@@ -533,7 +540,7 @@ mod tests {
assert_eq!(transactions.len(), 1);
assert_eq!(transactions[0].value, eth_amount_in);
// sequential swap selector
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "e8a980d7");
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "e21dd0d3");
}
#[test]
@@ -1059,8 +1066,8 @@ mod tests {
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
// zero for one
"00",
// transfer type
"00",
// transfer true
"01",
))
);
}
@@ -1147,8 +1154,8 @@ mod tests {
"6982508145454ce325ddbe47a25d4ec3d2311933",
// zero for one
"00",
// transfer type
"00",
// transfer type Transfer
"01",
// receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
// first pool intermediary token (ETH)

View File

@@ -96,27 +96,6 @@ pub struct Transaction {
pub data: Vec<u8>,
}
/// Represents the type of transfer to be performed into the pool.
///
/// # Fields
///
/// * `TransferToProtocol`: Transfer the token from the router into the protocol.
/// * `TransferFromToProtocol`: Transfer the token from the sender to the protocol.
/// * `TransferPermit2ToProtocol`: Transfer the token from the sender to the protocol using Permit2.
/// * `TransferFromToRouter`: Transfer the token from the sender to the router.
/// * `TransferPermit2ToRouter`: Transfer the token from the sender to the router using Permit2.
/// * `None`: No transfer is needed. Tokens are already in the pool.
#[repr(u8)]
#[derive(Clone, Debug, PartialEq)]
pub enum TransferType {
TransferToProtocol = 0,
TransferFromToProtocol = 1,
TransferPermit2ToProtocol = 2,
TransferFromToRouter = 3,
TransferPermit2ToRouter = 4,
None = 5,
}
/// Represents necessary attributes for encoding an order.
///
/// # Fields
@@ -127,6 +106,7 @@ pub enum TransferType {
/// solution does not require router address.
/// * `group_token_in`: Token to be used as the input for the group swap.
/// * `group_token_out`: Token to be used as the output for the group swap.
/// * `transfer`: Type of transfer to be performed. See `TransferType` for more details.
#[derive(Clone, Debug)]
pub struct EncodingContext {
pub receiver: Bytes,
@@ -137,6 +117,21 @@ pub struct EncodingContext {
pub transfer_type: TransferType,
}
/// Represents the type of transfer to be performed into the pool.
///
/// # Fields
///
/// * `TransferFrom`: Transfer the token from the sender to the protocol/router.
/// * `Transfer`: Transfer the token from the router into the protocol.
/// * `None`: No transfer is needed. Tokens are already in the pool.
#[repr(u8)]
#[derive(Clone, Debug, PartialEq)]
pub enum TransferType {
TransferFrom = 0,
Transfer = 1,
None = 2,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Chain {
pub id: u64,