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]] [[package]]
name = "tycho-common" name = "tycho-common"
version = "0.66.4" version = "0.70.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5131fdb21cbd754822b0947fc6c763494531837ba8bb34123f6c7f4f89cb69f7" checksum = "5237d0e4ab6979a1ca9cdb749a2a97240ca6dc716c0da6f42543960d3141255a"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "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 = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true }
alloy-sol-types = { version = "0.8.14", optional = true } alloy-sol-types = { version = "0.8.14", optional = true }
alloy-primitives = { version = "0.8.9", 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" once_cell = "1.20.2"
[dev-dependencies] [dev-dependencies]

View File

@@ -10,7 +10,6 @@ error Dispatcher__InvalidDataLength();
/** /**
* @title Dispatcher - Dispatch execution to external contracts * @title Dispatcher - Dispatch execution to external contracts
* @author PropellerHeads Devs
* @dev Provides the ability to delegate execution of swaps to external * @dev Provides the ability to delegate execution of swaps to external
* contracts. This allows dynamically adding new supported protocols * contracts. This allows dynamically adding new supported protocols
* without needing to upgrade any contracts. External contracts will * 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 "./Dispatcher.sol";
import {LibSwap} from "../lib/LibSwap.sol"; import {LibSwap} from "../lib/LibSwap.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.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__InvalidDataLength();
error TychoRouter__UndefinedMinAmountOut(); error TychoRouter__UndefinedMinAmountOut();
contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { contract TychoRouter is
IAllowanceTransfer public immutable permit2; AccessControl,
Dispatcher,
Pausable,
ReentrancyGuard,
RestrictTransferFrom
{
IWETH private immutable _weth; IWETH private immutable _weth;
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
@@ -87,7 +93,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address indexed token, uint256 amount, address indexed receiver 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)) { if (_permit2 == address(0) || weth == address(0)) {
revert TychoRouter__AddressZero(); 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 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 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 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. * @param swaps Encoded swap graph data containing details of each swap.
* *
* @return amountOut The total amount of the output token received by the receiver. * @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, bool unwrapEth,
uint256 nTokens, uint256 nTokens,
address receiver, address receiver,
bool transferFromNeeded,
bytes calldata swaps bytes calldata swaps
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
_tstoreTransferFromInfo(tokenIn, amountIn, false, transferFromNeeded);
return _splitSwapChecked( return _splitSwapChecked(
amountIn, amountIn,
tokenIn, tokenIn,
tokenOut, tokenOut,
minAmountOut, minAmountOut,
initialBalanceTokenOut,
wrapEth, wrapEth,
unwrapEth, unwrapEth,
nTokens, nTokens,
@@ -182,16 +196,19 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bytes calldata signature, bytes calldata signature,
bytes calldata swaps bytes calldata swaps
) external payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) external payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
// For native ETH, assume funds already in our router. Else, handle approval. // For native ETH, assume funds already in our router. Else, handle approval.
if (tokenIn != address(0)) { if (tokenIn != address(0)) {
permit2.permit(msg.sender, permitSingle, signature); permit2.permit(msg.sender, permitSingle, signature);
} }
_tstoreTransferFromInfo(tokenIn, amountIn, true, true);
return _splitSwapChecked( return _splitSwapChecked(
amountIn, amountIn,
tokenIn, tokenIn,
tokenOut, tokenOut,
minAmountOut, minAmountOut,
initialBalanceTokenOut,
wrapEth, wrapEth,
unwrapEth, unwrapEth,
nTokens, 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 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 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 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. * @param swaps Encoded swap graph data containing details of each swap.
* *
* @return amountOut The total amount of the output token received by the receiver. * @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 wrapEth,
bool unwrapEth, bool unwrapEth,
address receiver, address receiver,
bool transferFromNeeded,
bytes calldata swaps bytes calldata swaps
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
_tstoreTransferFromInfo(tokenIn, amountIn, false, transferFromNeeded);
return _sequentialSwapChecked( return _sequentialSwapChecked(
amountIn, amountIn,
tokenIn, tokenIn,
tokenOut, tokenOut,
minAmountOut, minAmountOut,
initialBalanceTokenOut,
wrapEth, wrapEth,
unwrapEth, unwrapEth,
receiver, receiver,
@@ -280,16 +303,20 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bytes calldata signature, bytes calldata signature,
bytes calldata swaps bytes calldata swaps
) external payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) external payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
// For native ETH, assume funds already in our router. Else, handle approval. // For native ETH, assume funds already in our router. Else, handle approval.
if (tokenIn != address(0)) { if (tokenIn != address(0)) {
permit2.permit(msg.sender, permitSingle, signature); permit2.permit(msg.sender, permitSingle, signature);
} }
_tstoreTransferFromInfo(tokenIn, amountIn, true, true);
return _sequentialSwapChecked( return _sequentialSwapChecked(
amountIn, amountIn,
tokenIn, tokenIn,
tokenOut, tokenOut,
minAmountOut, minAmountOut,
initialBalanceTokenOut,
wrapEth, wrapEth,
unwrapEth, unwrapEth,
receiver, 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 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 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 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. * @param swapData Encoded swap details.
* *
* @return amountOut The total amount of the output token received by the receiver. * @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 wrapEth,
bool unwrapEth, bool unwrapEth,
address receiver, address receiver,
bool transferFromNeeded,
bytes calldata swapData bytes calldata swapData
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
_tstoreTransferFromInfo(tokenIn, amountIn, false, transferFromNeeded);
return _singleSwap( return _singleSwap(
amountIn, amountIn,
tokenIn, tokenIn,
tokenOut, tokenOut,
minAmountOut, minAmountOut,
initialBalanceTokenOut,
wrapEth, wrapEth,
unwrapEth, unwrapEth,
receiver, receiver,
@@ -375,16 +408,19 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
bytes calldata signature, bytes calldata signature,
bytes calldata swapData bytes calldata swapData
) external payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) external payable whenNotPaused nonReentrant returns (uint256 amountOut) {
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
// For native ETH, assume funds already in our router. Else, handle approval. // For native ETH, assume funds already in our router. Else, handle approval.
if (tokenIn != address(0)) { if (tokenIn != address(0)) {
permit2.permit(msg.sender, permitSingle, signature); permit2.permit(msg.sender, permitSingle, signature);
} }
_tstoreTransferFromInfo(tokenIn, amountIn, true, true);
return _singleSwap( return _singleSwap(
amountIn, amountIn,
tokenIn, tokenIn,
tokenOut, tokenOut,
minAmountOut, minAmountOut,
initialBalanceTokenOut,
wrapEth, wrapEth,
unwrapEth, unwrapEth,
receiver, receiver,
@@ -405,6 +441,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
uint256 minAmountOut, uint256 minAmountOut,
uint256 initialBalanceTokenOut,
bool wrapEth, bool wrapEth,
bool unwrapEth, bool unwrapEth,
uint256 nTokens, uint256 nTokens,
@@ -424,7 +461,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _splitSwap(amountIn, nTokens, swaps); amountOut = _splitSwap(amountIn, nTokens, swaps);
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
@@ -459,6 +495,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
uint256 minAmountOut, uint256 minAmountOut,
uint256 initialBalanceTokenOut,
bool wrapEth, bool wrapEth,
bool unwrapEth, bool unwrapEth,
address receiver, address receiver,
@@ -480,7 +517,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
(address executor, bytes calldata protocolData) = (address executor, bytes calldata protocolData) =
swap_.decodeSingleSwap(); swap_.decodeSingleSwap();
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _callSwapOnExecutor(executor, amountIn, protocolData); amountOut = _callSwapOnExecutor(executor, amountIn, protocolData);
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
@@ -515,6 +551,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
uint256 minAmountOut, uint256 minAmountOut,
uint256 initialBalanceTokenOut,
bool wrapEth, bool wrapEth,
bool unwrapEth, bool unwrapEth,
address receiver, address receiver,
@@ -533,7 +570,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _sequentialSwap(amountIn, swaps); amountOut = _sequentialSwap(amountIn, swaps);
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {

View File

@@ -10,16 +10,16 @@ import {
import {IAsset} from "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol"; import {IAsset} from "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol";
// slither-disable-next-line solc-version // slither-disable-next-line solc-version
import {IVault} from "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; import {IVault} from "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
import {TokenTransfer} from "./TokenTransfer.sol"; import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
error BalancerV2Executor__InvalidDataLength(); error BalancerV2Executor__InvalidDataLength();
contract BalancerV2Executor is IExecutor, TokenTransfer { contract BalancerV2Executor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
address private constant VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; address private constant VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
constructor(address _permit2) TokenTransfer(_permit2) {} constructor(address _permit2) RestrictTransferFrom(_permit2) {}
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
function swap(uint256 givenAmount, bytes calldata data) function swap(uint256 givenAmount, bytes calldata data)
@@ -32,21 +32,13 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
IERC20 tokenOut, IERC20 tokenOut,
bytes32 poolId, bytes32 poolId,
address receiver, address receiver,
bool needsApproval, bool approvalNeeded,
TransferType transferType TransferType transferType
) = _decodeData(data); ) = _decodeData(data);
_transfer( _transfer(address(this), transferType, address(tokenIn), givenAmount);
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
);
if (needsApproval) { if (approvalNeeded) {
// slither-disable-next-line unused-return // slither-disable-next-line unused-return
tokenIn.forceApprove(VAULT, type(uint256).max); tokenIn.forceApprove(VAULT, type(uint256).max);
} }
@@ -81,7 +73,7 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
IERC20 tokenOut, IERC20 tokenOut,
bytes32 poolId, bytes32 poolId,
address receiver, address receiver,
bool needsApproval, bool approvalNeeded,
TransferType transferType TransferType transferType
) )
{ {
@@ -93,7 +85,7 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
tokenOut = IERC20(address(bytes20(data[20:40]))); tokenOut = IERC20(address(bytes20(data[20:40])));
poolId = bytes32(data[40:72]); poolId = bytes32(data[40:72]);
receiver = address(bytes20(data[72:92])); receiver = address(bytes20(data[72:92]));
needsApproval = uint8(data[92]) > 0; approvalNeeded = data[92] != 0;
transferType = TransferType(uint8(data[93])); transferType = TransferType(uint8(data[93]));
} }
} }

View File

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

View File

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

View File

@@ -3,18 +3,21 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol"; import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.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__InvalidDataLength();
error MaverickV2Executor__InvalidTarget(); error MaverickV2Executor__InvalidTarget();
error MaverickV2Executor__InvalidFactory(); error MaverickV2Executor__InvalidFactory();
contract MaverickV2Executor is IExecutor, TokenTransfer { contract MaverickV2Executor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
address public immutable factory; address public immutable factory;
constructor(address _factory, address _permit2) TokenTransfer(_permit2) { constructor(address _factory, address _permit2)
RestrictTransferFrom(_permit2)
{
if (_factory == address(0)) { if (_factory == address(0)) {
revert MaverickV2Executor__InvalidFactory(); revert MaverickV2Executor__InvalidFactory();
} }
@@ -47,9 +50,8 @@ contract MaverickV2Executor is IExecutor, TokenTransfer {
tickLimit: tickLimit tickLimit: tickLimit
}); });
_transfer( _transfer(target, transferType, address(tokenIn), givenAmount);
address(tokenIn), msg.sender, target, givenAmount, transferType
);
// slither-disable-next-line unused-return // slither-disable-next-line unused-return
(, calculatedAmount) = pool.swap(receiver, swapParams, ""); (, 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 "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol"; import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol";
import "./TokenTransfer.sol"; import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
error UniswapV2Executor__InvalidDataLength(); error UniswapV2Executor__InvalidDataLength();
error UniswapV2Executor__InvalidTarget(); error UniswapV2Executor__InvalidTarget();
@@ -12,7 +12,7 @@ error UniswapV2Executor__InvalidFactory();
error UniswapV2Executor__InvalidInitCode(); error UniswapV2Executor__InvalidInitCode();
error UniswapV2Executor__InvalidFee(); error UniswapV2Executor__InvalidFee();
contract UniswapV2Executor is IExecutor, TokenTransfer { contract UniswapV2Executor is IExecutor, RestrictTransferFrom {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
address public immutable factory; address public immutable factory;
@@ -25,7 +25,7 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
bytes32 _initCode, bytes32 _initCode,
address _permit2, address _permit2,
uint256 _feeBps uint256 _feeBps
) TokenTransfer(_permit2) { ) RestrictTransferFrom(_permit2) {
if (_factory == address(0)) { if (_factory == address(0)) {
revert UniswapV2Executor__InvalidFactory(); revert UniswapV2Executor__InvalidFactory();
} }
@@ -59,9 +59,8 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
_verifyPairAddress(target); _verifyPairAddress(target);
calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne); calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne);
_transfer(
address(tokenIn), msg.sender, target, givenAmount, transferType _transfer(target, transferType, address(tokenIn), givenAmount);
);
IUniswapV2Pair pool = IUniswapV2Pair(target); IUniswapV2Pair pool = IUniswapV2Pair(target);
if (zeroForOne) { if (zeroForOne) {
@@ -88,7 +87,7 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
inToken = IERC20(address(bytes20(data[0:20]))); inToken = IERC20(address(bytes20(data[0:20])));
target = address(bytes20(data[20:40])); target = address(bytes20(data[20:40]));
receiver = address(bytes20(data[40:60])); receiver = address(bytes20(data[40:60]));
zeroForOne = uint8(data[60]) > 0; zeroForOne = data[60] != 0;
transferType = TransferType(uint8(data[61])); transferType = TransferType(uint8(data[61]));
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL, WETH_DAI_POOL,
ALICE, ALICE,
false, false,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL RestrictTransferFrom.TransferType.TransferFrom
); );
bytes memory swap = bytes memory swap =
@@ -67,7 +67,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL, WETH_DAI_POOL,
ALICE, ALICE,
false, false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL RestrictTransferFrom.TransferType.TransferFrom
); );
bytes memory swap = bytes memory swap =
@@ -82,6 +82,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
false, false,
false, false,
ALICE, ALICE,
true,
swap swap
); );
@@ -108,7 +109,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL, WETH_DAI_POOL,
ALICE, ALICE,
false, false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL RestrictTransferFrom.TransferType.None
); );
bytes memory swap = bytes memory swap =
@@ -116,7 +117,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector); vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
tychoRouter.singleSwap( 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, WETH_DAI_POOL,
ALICE, ALICE,
false, false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL RestrictTransferFrom.TransferType.TransferFrom
); );
bytes memory swap = bytes memory swap =
@@ -150,6 +151,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
false, false,
false, false,
ALICE, ALICE,
true,
swap swap
); );
} }
@@ -169,7 +171,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL, WETH_DAI_POOL,
ALICE, ALICE,
false, false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL RestrictTransferFrom.TransferType.TransferFrom
); );
bytes memory swap = bytes memory swap =
@@ -192,6 +194,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
false, false,
false, false,
ALICE, ALICE,
true,
swap swap
); );
} }
@@ -218,7 +221,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL, WETH_DAI_POOL,
ALICE, ALICE,
false, false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL RestrictTransferFrom.TransferType.Transfer // ETH has already been transferred to router
); );
bytes memory swap = bytes memory swap =
@@ -261,7 +264,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, tychoRouterAddr,
true, true,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL RestrictTransferFrom.TransferType.TransferFrom
); );
bytes memory swap = bytes memory swap =
@@ -287,6 +290,49 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
vm.stopPrank(); 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 { function testSingleSwapIntegration() public {
// Tests swapping WETH -> DAI on a USV2 pool with regular approvals // Tests swapping WETH -> DAI on a USV2 pool with regular approvals
deal(WETH_ADDR, ALICE, 1 ether); deal(WETH_ADDR, ALICE, 1 ether);

View File

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

View File

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

View File

@@ -1,28 +1,29 @@
test_uniswap_v3_uniswap_v2:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000bf00692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb8004375dff511095cc5a197a54140a24efef3a416cbcdf9626bc03e24f779434178a73a0b4bad62ed000100525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010500 test_uniswap_v3_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000bf00692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb8004375dff511095cc5a197a54140a24efef3a416cbcdf9626bc03e24f779434178a73a0b4bad62ed000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010200
test_single_encoding_strategy_ekubo:20144a070000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000071a0cb889707d426a7a386870a03bc70d1b069759805cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000000000000000 test_single_encoding_strategy_ekubo:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000071a0cb889707d426a7a386870a03bc70d1b069759802cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000000000000000
test_uniswap_v3_uniswap_v3:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000100692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010000000000000000000000 test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000
test_balancer_v2_uniswap_v2:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010300525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20105000000000000000000000000000000000000000000000000 test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_sequential_swap_strategy_encoder_no_permit2:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000100525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20105000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006852b03300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3b00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041558653365a24c992e17bbf18a802b91eb3cf936eeb1f214d3e5e6cad70ba57070c7e92db5c3f0399d7da505798950313ce1d0c399e54cca0049868ee0f0005501b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041b70914df20e4b2675adc5a61d26b959e0fd21e7cce6503b884d5ba9e1db2c82164112e4dc362b9a3ef794a03fcfebe0710700028b79abe8b7de525288ac904991c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006852b03300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c34556c17e3de4636f371033dc793b59f217facb4643ee3113acf72d3c2d7a3d22cf66c34d8d9c7bc119ac2b0fb2781a2097aa743fd77718f2a4e5ef5f92acbf1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c8000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c33e9da2d2a5bea1087e27f42b9255fcaea06efa76558843f6192d571b15c09c5818e1a4b3a111bb6accc4cb8b583c318d2386e0e94cc87b9fcc4065db0d65611c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000
test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041c55b2e21396823d694e6da247eee51ef542d57d3c5901911b85b3c195d89a56d03e85c23251af0d3981b0e07ca05caaaca029ae5efba9d58178be2f38d08cced1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000200525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20105000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_no_permit2:20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_swap_strategy_encoder_no_transfer_in:20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3b00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160a5490c6e80921cf7b1ad8e7a59a02febd09780fa88c17070663c5a78635116227c299592fac934afa013d95f8711a70fdeb54fc07a2c22352f9cf263924d931c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330105cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041f59744bfe3e154be7c72429eb93db7ef2ec00344feeb1376195a2e3f133437f7533c3df955de146e229e4a37fda22acbe11d9393215d14cf1e19334aebbf35e61b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000
test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ff87cdaae49fca40cccacadeab4710cdbd41d4901ba28cdabf36dbe07b198887506a6d4960e95f586b5d39011f74d4d57d61a7585ca7b8bdd86f550a9973e5571b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010200692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000000000 test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000
test_single_encoding_strategy_curve_st_eth:20144a070000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010005cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041c55b2e21396823d694e6da247eee51ef542d57d3c5901911b85b3c195d89a56d03e85c23251af0d3981b0e07ca05caaaca029ae5efba9d58178be2f38d08cced1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200020000000000000000000000000000 test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_encoding_strategy_curve:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000103cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410e67daacc0a64ee7e7a57211b347b4af6d4d8865acbb9f549395fe42cf5ea87e1ac51293305eef8aebba46d510052e6fdea42e2caf1c34624260108f32ea6a641b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501020000000000000000000000000000 test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e5679a7262a283109d2e312e4ffbec563f501f347efc00418f3a6701492635977583d4e2ff29904a33496b64f1bc2ac73796a546a495ab110cca86e6de1655b61c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000
test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417b597f22dfb861c6ac1ec78eecab17b5e8b8e389494a9d3c2f27efbc35dbd73846d4dc5fef1b9530c4d3b8d7065fbc4d190821c1b5efbfd41ce388163e3812911c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160aa554efc922689a7f426a984f9fc278aa948b9c78f4e7cc0a5f177e5fb6859632b92d3326f710603d43d3a8ea5753c58a61316fd1bff8e0b388f74c98993b91b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ff87cdaae49fca40cccacadeab4710cdbd41d4901ba28cdabf36dbe07b198887506a6d4960e95f586b5d39011f74d4d57d61a7585ca7b8bdd86f550a9973e5571b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950102006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000 test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000
test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ff87cdaae49fca40cccacadeab4710cdbd41d4901ba28cdabf36dbe07b198887506a6d4960e95f586b5d39011f74d4d57d61a7585ca7b8bdd86f550a9973e5571b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80102005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000 test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000
test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c55b2e21396823d694e6da247eee51ef542d57d3c5901911b85b3c195d89a56d03e85c23251af0d3981b0e07ca05caaaca029ae5efba9d58178be2f38d08cced1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950002005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950002005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010000000000000000000000000000000000000000000000000000000000 test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000
test_uniswap_v3_curve:e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000100691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000105cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000
test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006852b03400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b2a3c00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004153ce45df3a0b2c8061d920d172d594ee02cf37b1967001935d3a6055700a300f79f68cda34bd3c6f64bfd154a113a43aafd15e6443dcc402e5b6ac9263e6ff031c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501020072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010500691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001053ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598003ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a81d713c715dd501edf102a3c3a755a931e3c72e7322d66b620ece81d7c98bae3d46479041445649eeefdb6ccc09c2b933cd37eda3ae8c99034e84f5570eb2c31c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000
test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0105 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102
test_ekubo_encode_swap_multi:00ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001 test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001
test_single_encoding_strategy_maverick:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc201000000000000000000000000000000 test_single_encoding_strategy_maverick:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_encode_maverick_v2:40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00 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, WETH_BAL_POOL_ID,
address(2), address(2),
true, true,
TokenTransfer.TransferType.NONE RestrictTransferFrom.TransferType.None
); );
( (
@@ -55,7 +55,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
bytes32 poolId, bytes32 poolId,
address receiver, address receiver,
bool needsApproval, bool needsApproval,
TokenTransfer.TransferType transferType RestrictTransferFrom.TransferType transferType
) = balancerV2Exposed.decodeParams(params); ) = balancerV2Exposed.decodeParams(params);
assertEq(address(tokenIn), WETH_ADDR); assertEq(address(tokenIn), WETH_ADDR);
@@ -63,7 +63,9 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
assertEq(poolId, WETH_BAL_POOL_ID); assertEq(poolId, WETH_BAL_POOL_ID);
assertEq(receiver, address(2)); assertEq(receiver, address(2));
assertEq(needsApproval, true); assertEq(needsApproval, true);
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE)); assertEq(
uint8(transferType), uint8(RestrictTransferFrom.TransferType.None)
);
} }
function testDecodeParamsInvalidDataLength() public { function testDecodeParamsInvalidDataLength() public {
@@ -82,7 +84,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
WETH_BAL_POOL_ID, WETH_BAL_POOL_ID,
BOB, BOB,
true, true,
TokenTransfer.TransferType.NONE RestrictTransferFrom.TransferType.Transfer
); );
deal(WETH_ADDR, address(balancerV2Exposed), amountIn); deal(WETH_ADDR, address(balancerV2Exposed), amountIn);
@@ -104,7 +106,7 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
bytes32 poolId, bytes32 poolId,
address receiver, address receiver,
bool needsApproval, bool needsApproval,
TokenTransfer.TransferType transferType RestrictTransferFrom.TransferType transferType
) = balancerV2Exposed.decodeParams(protocolData); ) = balancerV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), WETH_ADDR); assertEq(address(tokenIn), WETH_ADDR);
@@ -112,7 +114,9 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
assertEq(poolId, WETH_BAL_POOL_ID); assertEq(poolId, WETH_BAL_POOL_ID);
assertEq(receiver, BOB); assertEq(receiver, BOB);
assertEq(needsApproval, true); assertEq(needsApproval, true);
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE)); assertEq(
uint8(transferType), uint8(RestrictTransferFrom.TransferType.None)
);
} }
function testSwapIntegration() public { function testSwapIntegration() public {

View File

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

View File

@@ -3,7 +3,7 @@ pragma solidity ^0.8.26;
import "../TestUtils.sol"; import "../TestUtils.sol";
import {Constants} from "../Constants.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 {ICore} from "@ekubo/interfaces/ICore.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.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)); uint256 usdcBalanceBeforeExecutor = USDC.balanceOf(address(executor));
bytes memory data = abi.encodePacked( 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 address(executor), // receiver
NATIVE_TOKEN_ADDRESS, // tokenIn NATIVE_TOKEN_ADDRESS, // tokenIn
USDC_ADDR, // tokenOut USDC_ADDR, // tokenOut
@@ -82,7 +82,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
uint256 ethBalanceBeforeExecutor = address(executor).balance; uint256 ethBalanceBeforeExecutor = address(executor).balance;
bytes memory data = abi.encodePacked( 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 address(executor), // receiver
USDC_ADDR, // tokenIn USDC_ADDR, // tokenIn
NATIVE_TOKEN_ADDRESS, // tokenOut 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 // Same test case as in swap_encoder::tests::ekubo::test_encode_swap_multi
function testMultiHopSwap() public { function testMultiHopSwap() public {
bytes memory data = abi.encodePacked( 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 address(executor), // receiver
NATIVE_TOKEN_ADDRESS, // tokenIn NATIVE_TOKEN_ADDRESS, // tokenIn
USDC_ADDR, // tokenOut of 1st swap USDC_ADDR, // tokenOut of 1st swap

View File

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

View File

@@ -1,11 +1,11 @@
// SPDX-License-Identifier: BUSL-1.1 // SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26; pragma solidity ^0.8.26;
import "../TestUtils.sol";
import "@src/executors/UniswapV2Executor.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 {Constants} from "../Constants.sol";
import {Permit2TestHelper} from "../Permit2TestHelper.sol"; import {Permit2TestHelper} from "../Permit2TestHelper.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
contract UniswapV2ExecutorExposed is UniswapV2Executor { contract UniswapV2ExecutorExposed is UniswapV2Executor {
constructor( constructor(
@@ -23,7 +23,7 @@ contract UniswapV2ExecutorExposed is UniswapV2Executor {
address target, address target,
address receiver, address receiver,
bool zeroForOne, bool zeroForOne,
TransferType transferType RestrictTransferFrom.TransferType transferType
) )
{ {
return _decodeData(data); 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; using SafeERC20 for IERC20;
UniswapV2ExecutorExposed uniswapV2Exposed; UniswapV2ExecutorExposed uniswapV2Exposed;
@@ -60,7 +60,6 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
UniswapV2ExecutorExposed pancakeswapV2Exposed; UniswapV2ExecutorExposed pancakeswapV2Exposed;
IERC20 WETH = IERC20(WETH_ADDR); IERC20 WETH = IERC20(WETH_ADDR);
IERC20 DAI = IERC20(DAI_ADDR); IERC20 DAI = IERC20(DAI_ADDR);
IAllowanceTransfer permit2;
function setUp() public { function setUp() public {
uint256 forkBlock = 17323404; uint256 forkBlock = 17323404;
@@ -80,7 +79,6 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
PERMIT2_ADDRESS, PERMIT2_ADDRESS,
25 25
); );
permit2 = IAllowanceTransfer(PERMIT2_ADDRESS);
} }
function testDecodeParams() public view { function testDecodeParams() public view {
@@ -89,7 +87,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
address(2), address(2),
address(3), address(3),
false, false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL RestrictTransferFrom.TransferType.Transfer
); );
( (
@@ -97,7 +95,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
address target, address target,
address receiver, address receiver,
bool zeroForOne, bool zeroForOne,
TokenTransfer.TransferType transferType RestrictTransferFrom.TransferType transferType
) = uniswapV2Exposed.decodeParams(params); ) = uniswapV2Exposed.decodeParams(params);
assertEq(address(tokenIn), WETH_ADDR); assertEq(address(tokenIn), WETH_ADDR);
@@ -105,8 +103,8 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
assertEq(receiver, address(3)); assertEq(receiver, address(3));
assertEq(zeroForOne, false); assertEq(zeroForOne, false);
assertEq( 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, WETH_DAI_POOL,
BOB, BOB,
zeroForOne, zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) RestrictTransferFrom.TransferType.Transfer
); );
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn); deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -173,59 +171,6 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
assertGe(finalBalance, amountOut); 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 { function testSwapNoTransfer() public {
uint256 amountIn = 10 ** 18; uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891; uint256 amountOut = 1847751195973566072891;
@@ -235,7 +180,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
WETH_DAI_POOL, WETH_DAI_POOL,
BOB, BOB,
zeroForOne, zeroForOne,
uint8(TokenTransfer.TransferType.NONE) RestrictTransferFrom.TransferType.None
); );
deal(WETH_ADDR, address(this), amountIn); deal(WETH_ADDR, address(this), amountIn);
@@ -248,27 +193,29 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
function testDecodeIntegration() public view { function testDecodeIntegration() public view {
bytes memory protocolData = bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f564000000000000000000000000000000000000000010000"; hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f564000000000000000000000000000000000000000010001";
( (
IERC20 tokenIn, IERC20 tokenIn,
address target, address target,
address receiver, address receiver,
bool zeroForOne, bool zeroForOne,
TokenTransfer.TransferType transferType RestrictTransferFrom.TransferType transferType
) = uniswapV2Exposed.decodeParams(protocolData); ) = uniswapV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), WETH_ADDR); assertEq(address(tokenIn), WETH_ADDR);
assertEq(target, 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640); assertEq(target, 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
assertEq(receiver, 0x0000000000000000000000000000000000000001); assertEq(receiver, 0x0000000000000000000000000000000000000001);
assertEq(zeroForOne, false); assertEq(zeroForOne, false);
// TRANSFER = 0 assertEq(
assertEq(0, uint8(transferType)); uint8(transferType),
uint8(RestrictTransferFrom.TransferType.Transfer)
);
} }
function testSwapIntegration() public { function testSwapIntegration() public {
bytes memory protocolData = bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000"; loadCallDataFromFile("test_encode_uniswap_v2");
uint256 amountIn = 10 ** 18; uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891; uint256 amountOut = 1847751195973566072891;
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn); deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -287,7 +234,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
fakePool, fakePool,
BOB, BOB,
zeroForOne, zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) RestrictTransferFrom.TransferType.Transfer
); );
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn); deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -307,7 +254,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
USDC_MAG7_POOL, USDC_MAG7_POOL,
BOB, BOB,
zeroForOne, zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) RestrictTransferFrom.TransferType.Transfer
); );
deal(BASE_USDC, address(uniswapV2Exposed), amountIn); deal(BASE_USDC, address(uniswapV2Exposed), amountIn);

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,6 @@ use crate::encoding::{
pub struct TransferOptimization { pub struct TransferOptimization {
native_token: Bytes, native_token: Bytes,
wrapped_token: Bytes, wrapped_token: Bytes,
permit2: bool,
token_in_already_in_router: bool, token_in_already_in_router: bool,
router_address: Bytes, router_address: Bytes,
} }
@@ -25,69 +24,58 @@ impl TransferOptimization {
pub fn new( pub fn new(
native_token: Bytes, native_token: Bytes,
wrapped_token: Bytes, wrapped_token: Bytes,
permit2: bool,
token_in_already_in_router: bool, token_in_already_in_router: bool,
router_address: Bytes, router_address: Bytes,
) -> Self { ) -> Self {
TransferOptimization { TransferOptimization {
native_token, native_token,
wrapped_token, wrapped_token,
permit2,
token_in_already_in_router, token_in_already_in_router,
router_address, router_address,
} }
} }
/// Returns the transfer method that should be used for the given swap and solution. /// Returns the transfer type that should be used for the current transfer.
pub fn get_transfer_type( pub fn get_transfers(
&self, &self,
swap: SwapGroup, swap: SwapGroup,
given_token: Bytes, given_token: Bytes,
wrap: bool, wrap: bool,
in_between_swap_optimization: bool, in_between_swap_optimization: bool,
) -> TransferType { ) -> TransferType {
let is_first_swap = swap.token_in == given_token;
let in_transfer_required: bool = let in_transfer_required: bool =
IN_TRANSFER_REQUIRED_PROTOCOLS.contains(&swap.protocol_system.as_str()); 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 { if swap.token_in == self.native_token {
// Funds are already in router. All protocols currently take care of native transfers. // Funds are already in router. All protocols currently take care of native transfers.
TransferType::None TransferType::None
} else if (swap.token_in == self.wrapped_token) && wrap { } else if (swap.token_in == self.wrapped_token) && wrap {
// Wrapping already happened in the router so we can just use a normal transfer. // Wrapping already happened in the router so, we just do a normal transfer.
TransferType::TransferToProtocol TransferType::Transfer
} else if is_first_swap { } else if is_first_swap {
if in_transfer_required { if in_transfer_required {
if self.token_in_already_in_router { if self.token_in_already_in_router {
// Transfer from router to pool. // Transfer from router to pool.
TransferType::TransferToProtocol TransferType::Transfer
} else if self.permit2 {
// Transfer from swapper to pool using permit2.
TransferType::TransferPermit2ToProtocol
} else { } else {
// Transfer from swapper to pool. // Transfer from swapper to pool
TransferType::TransferFromToProtocol TransferType::TransferFrom
} }
// in transfer is not necessary for these protocols. Only make a transfer if the // in transfer is not necessary for these protocols. Only make a transfer from the
// tokens are not already in the router // swapper to the router if the tokens are not already in the router
} else if !self.token_in_already_in_router { } else if !self.token_in_already_in_router {
if self.permit2 { // Transfer from swapper to router using.
// Transfer from swapper to router using permit2. TransferType::TransferFrom
TransferType::TransferPermit2ToRouter
} else {
// Transfer from swapper to router.
TransferType::TransferFromToRouter
}
} else { } else {
TransferType::None TransferType::None
} }
// all other swaps // all other swaps that not the first one
} else if !in_transfer_required || in_between_swap_optimization { } else if !in_transfer_required || in_between_swap_optimization {
// funds should already be in the router or in the next pool // funds should already be in the router or in the next pool
TransferType::None TransferType::None
} else { } else {
TransferType::TransferToProtocol TransferType::Transfer
} }
} }
@@ -155,146 +143,63 @@ mod tests {
Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f") Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f")
} }
#[test] #[rstest]
fn test_first_swap_transfer_from_permit2() { // 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 // The swap token is the same as the given token, which is not the native token
let swap = SwapGroup { let swaps = vec![Swap {
protocol_system: "uniswap_v2".to_string(), component: ProtocolComponent {
token_in: weth(), protocol_system: "uniswap_v2".to_string(),
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
..Default::default()
},
token_in: swap_token_in.clone(),
token_out: dai(), token_out: dai(),
split: 0f64, 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 { let swap = SwapGroup {
protocol_system: "uniswap_v2".to_string(), protocol_system: protocol,
token_in: weth(), token_in: swap_token_in,
token_out: dai(), token_out: dai(),
split: 0f64, split: 0f64,
swaps: vec![], swaps,
}; };
let optimization = TransferOptimization::new(eth(), weth(), false, false, router_address()); let optimization =
let transfer_method = optimization.get_transfer_type(swap.clone(), weth(), false, false); TransferOptimization::new(eth(), weth(), token_in_already_in_router, router_address());
assert_eq!(transfer_method, TransferType::TransferFromToProtocol); let transfer = optimization.get_transfers(
} swap.clone(),
given_token,
#[test] wrap,
fn test_first_swap_native() { in_between_swap_optimization,
// The swap token is the same as the given token, and it's the native token. );
// No transfer action is needed. assert_eq!(transfer, expected_transfer);
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);
} }
fn receiver() -> Bytes { fn receiver() -> Bytes {
@@ -319,7 +224,7 @@ mod tests {
#[case] expected_receiver: Bytes, #[case] expected_receiver: Bytes,
#[case] expected_optimization: bool, #[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() { let next_swap = if protocol.is_none() {
None None

View File

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

View File

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

View File

@@ -96,27 +96,6 @@ pub struct Transaction {
pub data: Vec<u8>, 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. /// Represents necessary attributes for encoding an order.
/// ///
/// # Fields /// # Fields
@@ -127,6 +106,7 @@ pub enum TransferType {
/// solution does not require router address. /// solution does not require router address.
/// * `group_token_in`: Token to be used as the input for the group swap. /// * `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. /// * `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)] #[derive(Clone, Debug)]
pub struct EncodingContext { pub struct EncodingContext {
pub receiver: Bytes, pub receiver: Bytes,
@@ -137,6 +117,21 @@ pub struct EncodingContext {
pub transfer_type: TransferType, 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)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct Chain { pub struct Chain {
pub id: u64, pub id: u64,