feat: (WIP) Support selection of transfer into router

- For protocols like Balancer and Curve, which expect funds to be in the router at the time of swap, we must support also transferring funds from the user into the router. Doing this in the router would mean we are dealing with transfers in two different places: in the router main methods and in the executors. To avoid this, we are now performing transfers just in the executors, and two transfer types have been added to support transfers into the router.

TODO:
- Add this for Balancer and Curve (only added for USV4 atm).
- TODO consider renaming TRANSFER_FROM and TRANSFER_PERMIT2 to include "pool" in the name
This commit is contained in:
TAMARA LIPOWSKI
2025-04-11 23:25:45 -04:00
committed by Diana Carvalho
parent 59a80dc392
commit a301a1cef3
19 changed files with 426 additions and 240 deletions

View File

@@ -99,7 +99,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
/** /**
* @notice Executes a swap operation based on a predefined swap graph, supporting internal token amount splits. * @notice Executes a swap operation based on a predefined swap graph, supporting internal token amount splits.
* This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount * This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount
* against a user-specified minimum. This function performs a transferFrom to retrieve tokens from the caller. * against a user-specified minimum.
* *
* @dev * @dev
* - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token.
@@ -130,11 +130,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address receiver, address receiver,
bytes calldata swaps bytes calldata swaps
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
if (address(tokenIn) != address(0)) {
IERC20(tokenIn).safeTransferFrom(
msg.sender, address(this), amountIn
);
}
return _splitSwapChecked( return _splitSwapChecked(
amountIn, amountIn,
tokenIn, tokenIn,
@@ -187,15 +182,9 @@ 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) {
// For native ETH, assume funds already in our router. Else, transfer and 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);
permit2.transferFrom(
msg.sender,
address(this),
uint160(amountIn),
permitSingle.details.token
);
} }
return _splitSwapChecked( return _splitSwapChecked(
@@ -214,7 +203,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
/** /**
* @notice Executes a swap operation based on a predefined swap graph with no split routes. * @notice Executes a swap operation based on a predefined swap graph with no split routes.
* This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount * This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount
* against a user-specified minimum. This function performs a transferFrom to retrieve tokens from the caller. * against a user-specified minimum.
* *
* @dev * @dev
* - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token.
@@ -243,7 +232,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address receiver, address receiver,
bytes calldata swaps bytes calldata swaps
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
return _sequentialSwapChecked( return _sequentialSwapChecked(
amountIn, amountIn,
tokenIn, tokenIn,
@@ -292,15 +280,9 @@ 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) {
// For native ETH, assume funds already in our router. Else, transfer and 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);
permit2.transferFrom(
msg.sender,
address(this),
uint160(amountIn),
permitSingle.details.token
);
} }
return _sequentialSwapChecked( return _sequentialSwapChecked(
@@ -318,7 +300,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
/** /**
* @notice Executes a single swap operation. * @notice Executes a single swap operation.
* This function enables optional ETH wrapping/unwrapping, and validates the output amount against a user-specified minimum. * This function enables optional ETH wrapping/unwrapping, and validates the output amount against a user-specified minimum.
* This function performs a transferFrom to retrieve tokens from the caller.
* *
* @dev * @dev
* - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token.
@@ -346,7 +327,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address receiver, address receiver,
bytes calldata swapData bytes calldata swapData
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
return _singleSwap( return _singleSwap(
amountIn, amountIn,
tokenIn, tokenIn,
@@ -395,15 +375,9 @@ 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) {
// For native ETH, assume funds already in our router. Else, transfer and 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);
permit2.transferFrom(
msg.sender,
address(this),
uint160(amountIn),
permitSingle.details.token
);
} }
return _singleSwap( return _singleSwap(
@@ -449,23 +423,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
_wrapETH(amountIn); _wrapETH(amountIn);
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalance = tokenIn == address(0)
? address(this).balance
: IERC20(tokenIn).balanceOf(address(this));
amountOut = _splitSwap(amountIn, nTokens, swaps); amountOut = _splitSwap(amountIn, nTokens, swaps);
uint256 currentBalance = tokenIn == address(0)
? address(this).balance
: IERC20(tokenIn).balanceOf(address(this));
uint256 amountConsumed = initialBalance - currentBalance;
if (tokenIn != tokenOut && amountConsumed != amountIn) {
revert TychoRouter__AmountInDiffersFromConsumed(
amountIn, amountConsumed
);
}
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
@@ -512,25 +470,10 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalance = tokenIn == address(0)
? address(this).balance
: IERC20(tokenIn).balanceOf(address(this));
(address executor, bytes calldata protocolData) = (address executor, bytes calldata protocolData) =
swap_.decodeSingleSwap(); swap_.decodeSingleSwap();
amountOut = _callExecutor(executor, amountIn, protocolData); amountOut = _callExecutor(executor, amountIn, protocolData);
uint256 currentBalance = tokenIn == address(0)
? address(this).balance
: IERC20(tokenIn).balanceOf(address(this));
uint256 amountConsumed = initialBalance - currentBalance;
if (amountConsumed != amountIn) {
revert TychoRouter__AmountInDiffersFromConsumed(
amountIn, amountConsumed
);
}
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
@@ -577,10 +520,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalance = tokenIn == address(0)
? address(this).balance
: IERC20(tokenIn).balanceOf(address(this));
amountOut = _sequentialSwap(amountIn, swaps); amountOut = _sequentialSwap(amountIn, swaps);
uint256 currentBalance = tokenIn == address(0) uint256 currentBalance = tokenIn == address(0)

View File

@@ -16,9 +16,17 @@ contract TokenTransfer {
// Assume funds are in the TychoRouter - transfer into the pool // Assume funds are in the TychoRouter - transfer into the pool
TRANSFER, TRANSFER,
// Assume funds are in msg.sender's wallet - transferFrom into the pool // Assume funds are in msg.sender's wallet - transferFrom into the pool
TRANSFERFROM, TRANSFER_FROM,
// Assume funds are in msg.sender's wallet - permit2TransferFrom into the pool // Assume funds are in msg.sender's wallet - permit2TransferFrom into the pool
TRANSFERPERMIT2, TRANSFER_PERMIT2,
// 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_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. // Assume funds have already been transferred into the pool. Do nothing.
NONE NONE
} }
@@ -31,21 +39,31 @@ contract TokenTransfer {
} }
function _transfer( function _transfer(
IERC20 tokenIn, address tokenIn,
address sender, address sender,
address receiver, address receiver,
uint256 amount, uint256 amount,
TransferType transferType TransferType transferType
) internal { ) internal {
if (transferType == TransferType.TRANSFER) { if (transferType == TransferType.TRANSFER) {
tokenIn.safeTransfer(receiver, amount); if (tokenIn == address(0)) {
} else if (transferType == TransferType.TRANSFERFROM) { payable(receiver).transfer(amount);
} else {
IERC20(tokenIn).safeTransfer(receiver, amount);
}
} else if (transferType == TransferType.TRANSFER_FROM) {
// slither-disable-next-line arbitrary-send-erc20 // slither-disable-next-line arbitrary-send-erc20
tokenIn.safeTransferFrom(sender, receiver, amount); IERC20(tokenIn).safeTransferFrom(sender, receiver, amount);
} else if (transferType == TransferType.TRANSFERPERMIT2) { } else if (transferType == TransferType.TRANSFER_PERMIT2) {
// Permit2.permit is already called from the TychoRouter
permit2.transferFrom(sender, receiver, uint160(amount), tokenIn);
} else if (transferType == TransferType.TRANSFER_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.permit is already called from the TychoRouter
permit2.transferFrom( permit2.transferFrom(
sender, receiver, uint160(amount), address(tokenIn) sender, address(this), uint160(amount), tokenIn
); );
} }
} }

View File

@@ -50,7 +50,9 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
_verifyPairAddress(target); _verifyPairAddress(target);
calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne); calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne);
_transfer(tokenIn, msg.sender, target, givenAmount, transferType); _transfer(
address(tokenIn), msg.sender, target, givenAmount, transferType
);
IUniswapV2Pair pool = IUniswapV2Pair(target); IUniswapV2Pair pool = IUniswapV2Pair(target);
if (zeroForOne) { if (zeroForOne) {

View File

@@ -111,7 +111,7 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
uint256 amountOwed = uint256 amountOwed =
amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta); amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
_transfer(IERC20(tokenIn), sender, msg.sender, amountOwed, transferType); _transfer(tokenIn, sender, msg.sender, amountOwed, transferType);
return abi.encode(amountOwed, tokenIn); return abi.encode(amountOwed, tokenIn);
} }

View File

@@ -17,10 +17,11 @@ import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol"; import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol";
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol"; import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
import {ICallback} from "@interfaces/ICallback.sol"; import {ICallback} from "@interfaces/ICallback.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
error UniswapV4Executor__InvalidDataLength(); error UniswapV4Executor__InvalidDataLength();
contract UniswapV4Executor is IExecutor, V4Router, ICallback { contract UniswapV4Executor is IExecutor, V4Router, ICallback, TokenTransfer {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
using CurrencyLibrary for Currency; using CurrencyLibrary for Currency;
@@ -30,7 +31,10 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
int24 tickSpacing; int24 tickSpacing;
} }
constructor(IPoolManager _poolManager) V4Router(_poolManager) {} constructor(IPoolManager _poolManager, address _permit2)
V4Router(_poolManager)
TokenTransfer(_permit2)
{}
function swap(uint256 amountIn, bytes calldata data) function swap(uint256 amountIn, bytes calldata data)
external external
@@ -41,9 +45,19 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
bool zeroForOne, bool zeroForOne,
TransferType transferType,
UniswapV4Executor.UniswapV4Pool[] memory pools UniswapV4Executor.UniswapV4Pool[] memory pools
) = _decodeData(data); ) = _decodeData(data);
// TODO move this into callback when we implement callback transfer type support
_transfer(
tokenIn,
msg.sender,
address(this), // irrelevant attribute
amountIn,
transferType
);
bytes memory swapData; bytes memory swapData;
if (pools.length == 1) { if (pools.length == 1) {
PoolKey memory key = PoolKey({ PoolKey memory key = PoolKey({
@@ -138,6 +152,7 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
bool zeroForOne, bool zeroForOne,
TransferType transferType,
UniswapV4Pool[] memory pools UniswapV4Pool[] memory pools
) )
{ {
@@ -148,10 +163,11 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
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]));
uint256 poolsLength = (data.length - 41) / 26; // 26 bytes per pool object uint256 poolsLength = (data.length - 42) / 26; // 26 bytes per pool object
pools = new UniswapV4Pool[](poolsLength); pools = new UniswapV4Pool[](poolsLength);
bytes memory poolsData = data[41:]; bytes memory poolsData = data[42:];
uint256 offset = 0; uint256 offset = 0;
for (uint256 i = 0; i < poolsLength; i++) { for (uint256 i = 0; i < poolsLength; i++) {
address intermediaryToken; address intermediaryToken;

View File

@@ -12,7 +12,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE); uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE);
// Encoded solution generated using `test_split_swap_strategy_encoder_no_permit2` // Encoded solution generated using `test_split_swap_strategy_encoder_no_permit2`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000100000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -37,7 +37,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_encoding_strategy_usv4` // Encoded solution generated using `test_split_encoding_strategy_usv4`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c553846000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000681f1d6200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f7976a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416af8ab519cbe8f466aed6188c8966ca4910d72d40d2f4360f62dbe75864b77ce5ec168870bb1540673b3f720c4f2bfaa5de2a79813c857ad5cc49b20217c65041c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007800760001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000" hex"7c553846000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000682163b600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9ddbe000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e9e58e3facf99cd2c64b834d3b646b8cf9377c47540d65b5e180a06bca6f42851cf320a205cf466c7943abe45c2998afa6fd3d870043a108578e71256831ca1c1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d008b0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a040000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f400000000000000000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -60,7 +60,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
// Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in` // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in`
(bool success,) = tychoRouterAddr.call{value: 1 ether}( (bool success,) = tychoRouterAddr.call{value: 1 ether}(
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681f1d7100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f79779000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c9ad125cc7c7188d1f0cd61dd8ee0d752d45c89ade1df275c5e0ce17525c6ff137bd976ebd0d17223c007e1f0a2806d086b4c7015460ebd21fef8a6caf8368d51c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933016982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000" hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821689800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9e2a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412da8d5aab101bbdf256d785a42db176328e8298ee6d0906e0ef1998cfcaa332460f8409d9b298dff73c947796a22c8de21caa17405ea157ced090da2b6cb27431c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a056982508145454ce325ddbe47a25d4ec3d23119330061a80001f400000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -87,7 +87,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
// Encoded solution generated using `test_split_encoding_strategy_usv4_eth_out` // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_out`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000681f1d7e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f797860000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412beedebe0200a3d4ca03f34697af8ec027fe6c164e3d3b78e17d1c327dcfc8241ce8bda513f092e54b35856637e5e485a06cc8c1c6287f15f469d47e84fd91341b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c0000" hex"7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000682163ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9ddf60000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416056d925e7906c11b865992ac5c853532f5058bb57b67cd000a53b899503dd8a6fd4c0e5ea44c1ca4137753589bf89f66824796e719e807adee7567a707ee6681b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a040000000000000000000000000000000000000000000bb800003c00000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -109,7 +109,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
vm.startPrank(ALICE); vm.startPrank(ALICE);
// Encoded solution generated using `test_split_swap_strategy_encoder_wrap` // Encoded solution generated using `test_split_swap_strategy_encoder_wrap`
(bool success,) = tychoRouterAddr.call{value: 1 ether}( (bool success,) = tychoRouterAddr.call{value: 1 ether}(
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e435000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6bd580000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415563c90eb0f8a79e234e300520b50879242b42622cabd5d01c19e67aba4854a723e0bd8333774062ae03a22b8c5de4a0dfd70bffd9197f56b2063f390610e5891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821640400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de0c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041b34e1f3d4e78942b2429b776073a5dfab1420f763de7d7e2a2296ca8abf684f923f7ae7945e824d8a084b9610d33ed49246a36e8e0efbce8ae210b0474f9fe3a1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -131,7 +131,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(DAI_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(DAI_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_swap_strategy_encoder_unwrap` // Encoded solution generated using `test_split_swap_strategy_encoder_unwrap`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000681e436b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6bd730000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000411150ac5d21c61fcd35b486034a433e44d045950a52e991a7ad4035f3ad52beee5d9ecbc5ed9c370730cd2631a050fbe1219dd606e39c8aca40d588aa3eab03411c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000" hex"7c5538460000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006821641200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de1a00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004181d23336d0cacd47a4d228590a825a1d92a48378cd481ff308b6d235e14b925c584f31e420682879bea58363ca4aa44a3b79557b15a9f73078a4696e00f55f911b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010200000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -183,7 +183,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_swap_strategy_encoder_complex` // Encoded solution generated using `test_split_swap_strategy_encoder_complex`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e2d8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a793000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e9868ce97a4c09488710ce748362ca38418bda44148bb5faf7820445d47efaff66f8f3667a0b3091c9d67d240db4cc2c95db27bdf019e4d5ff76b7b6620514691b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d013950100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000000000" hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821643700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de3f00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004112f41a590796702b322fa5a9ee1602daef9b22d732e4fd8f122f072b65dda325271f630759db500db8a42bd4f41ddc18ddda63650deaf36228dca702e28eefd31b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950002005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950002005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d013950100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -210,7 +210,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_sequential_swap_strategy_encoder_complex_route` // Encoded solution generated using `test_sequential_swap_strategy_encoder_complex_route`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e493d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6c34500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004189757b528c9874627ca7ecd64bd662db5fd7855837ec98cca5cff2cbd599f9af15baff1d389b5de4ca0dc1106b9d3795a1695934cd5fa1158fffcc8d6495dfaa1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000" hex"51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821644a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de5200000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041530bdcde0c687eacb51a339f30c7b3eff7c078a3bbd4bc852519568dcdf271bb4c6ac05583f32c4a8d1a99be3a2817fe86c15ad2a06c5cf938bde9c22bc80f301c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000200525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -234,7 +234,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max); IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max);
// Encoded solution generated using `test_sequential_swap_strategy_encoder_no_permit2` // Encoded solution generated using `test_sequential_swap_strategy_encoder_no_permit2`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000" hex"e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000100525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -254,7 +254,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_cyclic_sequential_swap` // Encoded solution generated using `test_cyclic_sequential_swap`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e4a0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6c40a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416a11f27f0546e9cc9d27e4383a3f860d9822f0d7d0117e73abbf03007b3e235b36c2288ff083a04f51285092ff23422b3e00a5a292d5627172dfd031308fc3a11b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000" hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de780000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000413c8b048dc7b7614106a5aa1fa13e48c02a6a9714dfa07d2c424f68b81a5f828c39ace62f2dd57d7bfad10910ae44f77d68aec5c079fce456028b1bd7f72053151c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000"
); );
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294);
@@ -270,7 +270,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_input_cyclic_swap` // Encoded solution generated using `test_split_input_cyclic_swap`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e2d1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a71e00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004129478d2bffac3e378054d024ca3461f61e55552e2e8b0c7e0869f38ee834462f157f761e1a67c713f5749a6f6210dc4ac998a0521dda8dcb780b35d774250a141c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821659d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfa5000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041dd84c5cdc51719e377598eccd8eac0aae036e7e0745a7c65b5d44cc817071a7460ccc73934363f33cc7af71dc07545aeff1d92f8c2f0b2973e1fc37e7b2de3551c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80102005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
); );
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171);
@@ -286,7 +286,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_output_cyclic_swap` // Encoded solution generated using `test_split_output_cyclic_swap`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e2d3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a738000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041196745237299bfab79f038c0c12decec9c46b264621e1f00bbef32b15a22f15543defe95694dd1616aa8785bbb6f91a1557eebc97c0f5ca968c99f250da1b2ce1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000" hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000682165ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfb400000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004107f2b0f9c2e4e308ab43b288d69de30d84b10c8075e4dd9a2cf66594f97a52fb34de2534b89bf1887da74c92fd03464f45baff700dd32e213e3add1a3f351e891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950102006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000"
); );
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908);

View File

@@ -8,23 +8,38 @@ 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() internal view returns (bytes[] memory) { function _getSequentialSwaps(bool permit2)
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
: TokenTransfer.TransferType.TRANSFER_FROM;
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 WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, transferType
) )
); );
// DAI -> USDC // DAI -> USDC
swaps[1] = encodeSequentialSwap( swaps[1] = encodeSequentialSwap(
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap(DAI_ADDR, DAI_USDC_POOL, tychoRouterAddr, true) encodeUniswapV2Swap(
DAI_ADDR,
DAI_USDC_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER
)
); );
return swaps; return swaps;
} }
@@ -32,10 +47,13 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
function testSequentialSwapInternalMethod() public { function testSequentialSwapInternalMethod() public {
// Trade 1 WETH for USDC through DAI - see _getSequentialSwaps for more info // Trade 1 WETH for USDC through DAI - see _getSequentialSwaps for more info
uint256 amountIn = 1 ether; uint256 amountIn = 1 ether;
deal(WETH_ADDR, tychoRouterAddr, amountIn); deal(WETH_ADDR, ALICE, amountIn);
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps(); bytes[] memory swaps = _getSequentialSwaps(false);
tychoRouter.exposedSequentialSwap(amountIn, pleEncode(swaps)); tychoRouter.exposedSequentialSwap(amountIn, pleEncode(swaps));
vm.stopPrank();
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
assertEq(usdcBalance, 2644659787); assertEq(usdcBalance, 2644659787);
@@ -53,7 +71,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(); bytes[] memory swaps = _getSequentialSwaps(true);
tychoRouter.sequentialSwapPermit2( tychoRouter.sequentialSwapPermit2(
amountIn, amountIn,
WETH_ADDR, WETH_ADDR,
@@ -80,7 +98,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(); bytes[] memory swaps = _getSequentialSwaps(false);
tychoRouter.sequentialSwap( tychoRouter.sequentialSwap(
amountIn, amountIn,
WETH_ADDR, WETH_ADDR,
@@ -105,7 +123,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(); bytes[] memory swaps = _getSequentialSwaps(false);
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector); vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
tychoRouter.sequentialSwap( tychoRouter.sequentialSwap(
amountIn, amountIn,
@@ -127,7 +145,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(); bytes[] memory swaps = _getSequentialSwaps(false);
vm.expectRevert(); vm.expectRevert();
tychoRouter.sequentialSwap( tychoRouter.sequentialSwap(
amountIn, amountIn,
@@ -152,7 +170,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(); bytes[] memory swaps = _getSequentialSwaps(true);
uint256 minAmountOut = 3000 * 1e18; uint256 minAmountOut = 3000 * 1e18;
@@ -195,7 +213,30 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
sigDeadline: 0 sigDeadline: 0
}); });
bytes[] memory swaps = _getSequentialSwaps(); bytes[] memory swaps = new bytes[](2);
// WETH -> DAI
swaps[0] = encodeSequentialSwap(
address(usv2Executor),
encodeUniswapV2Swap(
WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER
)
);
// DAI -> USDC
swaps[1] = encodeSequentialSwap(
address(usv2Executor),
encodeUniswapV2Swap(
DAI_ADDR,
DAI_USDC_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER
)
);
uint256 amountOut = tychoRouter.sequentialSwapPermit2{value: amountIn}( uint256 amountOut = tychoRouter.sequentialSwapPermit2{value: amountIn}(
amountIn, amountIn,
@@ -237,14 +278,24 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
swaps[0] = encodeSequentialSwap( swaps[0] = encodeSequentialSwap(
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap( encodeUniswapV2Swap(
USDC_ADDR, DAI_USDC_POOL, tychoRouterAddr, false USDC_ADDR,
DAI_USDC_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_PERMIT2
) )
); );
// DAI -> WETH // DAI -> WETH
swaps[1] = encodeSequentialSwap( swaps[1] = encodeSequentialSwap(
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true) encodeUniswapV2Swap(
DAI_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER
)
); );
uint256 amountOut = tychoRouter.sequentialSwapPermit2( uint256 amountOut = tychoRouter.sequentialSwapPermit2(
@@ -275,11 +326,21 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
deal(USDC_ADDR, tychoRouterAddr, amountIn); deal(USDC_ADDR, tychoRouterAddr, amountIn);
bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap( bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap(
USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3, true USDC_ADDR,
WETH_ADDR,
tychoRouterAddr,
USDC_WETH_USV3,
true,
TokenTransfer.TransferType.TRANSFER
); );
bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap(
WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false WETH_ADDR,
USDC_ADDR,
tychoRouterAddr,
USDC_WETH_USV3_2,
false,
TokenTransfer.TransferType.TRANSFER
); );
bytes[] memory swaps = new bytes[](2); bytes[] memory swaps = new bytes[](2);

View File

@@ -22,7 +22,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_PERMIT2
); );
bytes memory swap = bytes memory swap =
@@ -59,7 +63,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory swap = bytes memory swap =
@@ -96,7 +104,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory swap = bytes memory swap =
@@ -118,7 +130,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory swap = bytes memory swap =
@@ -149,7 +165,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory swap = bytes memory swap =
@@ -194,7 +214,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
}); });
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER
); );
bytes memory swap = bytes memory swap =
@@ -232,8 +256,13 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
bytes memory signature bytes memory signature
) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn); ) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = bytes memory protocolData = encodeUniswapV2Swap(
encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true); DAI_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_PERMIT2
);
bytes memory swap = bytes memory swap =
encodeSingleSwap(address(usv2Executor), protocolData); encodeSingleSwap(address(usv2Executor), protocolData);
@@ -267,7 +296,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max); IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max);
// Encoded solution generated using `test_single_swap_strategy_encoder_no_permit2` // Encoded solution generated using `test_single_swap_strategy_encoder_no_permit2`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000" hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500010000000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();
@@ -286,7 +315,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_single_swap_strategy_encoder` // Encoded solution generated using `test_single_swap_strategy_encoder`
(bool success,) = tychoRouterAddr.call( (bool success,) = tychoRouterAddr.call(
hex"30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006817833200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067effd3a00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417efea09004d5d40d8d072e1ce0a425507717ea485c765eb90c170859197d362b502fb039b4f5cdce57318ecfe3ab276d1ac87771eb5d017b253a8f4107e6a20b1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000" hex"30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000682169c100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9e3c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000412d3f0fee3fc61987512f024f20b1448eb934f82105a91653dd169179c693aaf95d09ef666ce1d38be70b8156fa6e4ea3e8717204e02fe7ba99a1fc4e5a26e6e11b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500020000000000000000000000000000"
); );
vm.stopPrank(); vm.stopPrank();

View File

@@ -8,13 +8,22 @@ 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() private view returns (bytes[] memory) { function _getSplitSwaps(bool permit2)
private
view
returns (bytes[] memory)
{
// Trade 1 WETH for USDC through DAI and WBTC with 4 swaps on Uniswap V2 // Trade 1 WETH for USDC through DAI and WBTC with 4 swaps on Uniswap V2
// -> DAI -> // -> DAI ->
// 1 WETH USDC // 1 WETH USDC
// -> WBTC -> // -> WBTC ->
// (univ2) (univ2) // (univ2) (univ2)
bytes[] memory swaps = new bytes[](4); bytes[] memory swaps = new bytes[](4);
TokenTransfer.TransferType inTransferType = permit2
? TokenTransfer.TransferType.TRANSFER_PERMIT2
: TokenTransfer.TransferType.TRANSFER_FROM;
// WETH -> WBTC (60%) // WETH -> WBTC (60%)
swaps[0] = encodeSplitSwap( swaps[0] = encodeSplitSwap(
uint8(0), uint8(0),
@@ -22,7 +31,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(0xffffff * 60) / 100, // 60% (0xffffff * 60) / 100, // 60%
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap( encodeUniswapV2Swap(
WETH_ADDR, WETH_WBTC_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_WBTC_POOL,
tychoRouterAddr,
false,
inTransferType
) )
); );
// WBTC -> USDC // WBTC -> USDC
@@ -32,7 +45,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint24(0), uint24(0),
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap( encodeUniswapV2Swap(
WBTC_ADDR, USDC_WBTC_POOL, tychoRouterAddr, true WBTC_ADDR,
USDC_WBTC_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER
) )
); );
// WETH -> DAI // WETH -> DAI
@@ -42,7 +59,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint24(0), uint24(0),
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap( encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, inTransferType
) )
); );
@@ -52,7 +69,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint8(2), uint8(2),
uint24(0), uint24(0),
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap(DAI_ADDR, DAI_USDC_POOL, tychoRouterAddr, true) encodeUniswapV2Swap(
DAI_ADDR,
DAI_USDC_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER
)
); );
return swaps; return swaps;
@@ -62,9 +85,12 @@ 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, tychoRouterAddr, amountIn); deal(WETH_ADDR, ALICE, amountIn);
bytes[] memory swaps = _getSplitSwaps(); vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes[] memory swaps = _getSplitSwaps(false);
tychoRouter.exposedSplitSwap(amountIn, 4, pleEncode(swaps)); tychoRouter.exposedSplitSwap(amountIn, 4, pleEncode(swaps));
vm.stopPrank();
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
assertEq(usdcBalance, 2615491639); assertEq(usdcBalance, 2615491639);
@@ -83,7 +109,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
bytes memory signature bytes memory signature
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSplitSwaps(); bytes[] memory swaps = _getSplitSwaps(true);
tychoRouter.splitSwapPermit2( tychoRouter.splitSwapPermit2(
amountIn, amountIn,
@@ -112,7 +138,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(); bytes[] memory swaps = _getSplitSwaps(false);
tychoRouter.splitSwap( tychoRouter.splitSwap(
amountIn, amountIn,
@@ -139,7 +165,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(); bytes[] memory swaps = _getSplitSwaps(false);
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector); vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
tychoRouter.splitSwap( tychoRouter.splitSwap(
@@ -164,7 +190,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(); bytes[] memory swaps = _getSplitSwaps(false);
vm.expectRevert(); vm.expectRevert();
tychoRouter.splitSwap( tychoRouter.splitSwap(
@@ -193,7 +219,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
bytes memory signature bytes memory signature
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSplitSwaps(); bytes[] memory swaps = _getSplitSwaps(true);
uint256 minAmountOut = 3000 * 1e18; uint256 minAmountOut = 3000 * 1e18;
@@ -240,7 +266,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
sigDeadline: 0 sigDeadline: 0
}); });
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false WETH_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER
); );
bytes memory swap = encodeSplitSwap( bytes memory swap = encodeSplitSwap(
@@ -284,8 +314,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
bytes memory signature bytes memory signature
) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn); ) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = bytes memory protocolData = encodeUniswapV2Swap(
encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true); DAI_ADDR,
WETH_DAI_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_PERMIT2
);
bytes memory swap = encodeSplitSwap( bytes memory swap = encodeSplitSwap(
uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData
@@ -330,7 +365,12 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI
bool zeroForOne = false; bool zeroForOne = false;
bytes memory protocolData = encodeUniswapV3Swap( bytes memory protocolData = encodeUniswapV3Swap(
WETH_ADDR, DAI_ADDR, tychoRouterAddr, DAI_WETH_USV3, zeroForOne WETH_ADDR,
DAI_ADDR,
tychoRouterAddr,
DAI_WETH_USV3,
zeroForOne,
TokenTransfer.TransferType.TRANSFER_PERMIT2
); );
bytes memory swap = encodeSplitSwap( bytes memory swap = encodeSplitSwap(
uint8(0), uint8(1), uint24(0), address(usv3Executor), protocolData uint8(0), uint8(1), uint24(0), address(usv3Executor), protocolData
@@ -366,59 +406,6 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tychoRouter.exposedSplitSwap(amountIn, 2, swaps); tychoRouter.exposedSplitSwap(amountIn, 2, swaps);
} }
function testSplitSwapAmountInNotFullySpent() public {
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
// Has invalid data as input! There is only one swap with 60% of the input amount
uint256 amountIn = 1 ether;
deal(WETH_ADDR, ALICE, amountIn);
vm.startPrank(ALICE);
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
);
bytes memory swap = encodeSplitSwap(
uint8(0),
uint8(1),
(0xffffff * 60) / 100, // 60%
address(usv2Executor),
protocolData
);
bytes[] memory swaps = new bytes[](1);
swaps[0] = swap;
vm.expectRevert(
abi.encodeWithSelector(
TychoRouter__AmountInDiffersFromConsumed.selector,
1000000000000000000,
600000000000000000
)
);
tychoRouter.splitSwapPermit2(
amountIn,
WETH_ADDR,
DAI_ADDR,
1,
false,
false,
2,
ALICE,
permitSingle,
signature,
pleEncode(swaps)
);
vm.stopPrank();
}
function testSplitSwapSingleUSV4CallbackPermit2() public { function testSplitSwapSingleUSV4CallbackPermit2() public {
vm.startPrank(ALICE); vm.startPrank(ALICE);
uint256 amountIn = 100 ether; uint256 amountIn = 100 ether;
@@ -436,8 +423,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tickSpacing: int24(1) tickSpacing: int24(1)
}); });
bytes memory protocolData = bytes memory protocolData = UniswapV4Utils.encodeExactInput(
UniswapV4Utils.encodeExactInput(USDE_ADDR, USDT_ADDR, true, pools); USDE_ADDR,
USDT_ADDR,
true,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_ROUTER,
pools
);
bytes memory swap = encodeSplitSwap( bytes memory swap = encodeSplitSwap(
uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData
@@ -483,8 +475,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tickSpacing: int24(60) tickSpacing: int24(60)
}); });
bytes memory protocolData = bytes memory protocolData = UniswapV4Utils.encodeExactInput(
UniswapV4Utils.encodeExactInput(USDE_ADDR, WBTC_ADDR, true, pools); USDE_ADDR,
WBTC_ADDR,
true,
TokenTransfer.TransferType.NONE,
pools
);
bytes memory swap = encodeSplitSwap( bytes memory swap = encodeSplitSwap(
uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData
@@ -507,18 +504,35 @@ 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, tychoRouterAddr, amountIn); deal(USDC_ADDR, ALICE, amountIn);
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, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3, true USDC_ADDR,
WETH_ADDR,
tychoRouterAddr,
USDC_WETH_USV3,
true,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory usdcWethV3Pool2ZeroOneData = encodeUniswapV3Swap( bytes memory usdcWethV3Pool2ZeroOneData = encodeUniswapV3Swap(
USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, true USDC_ADDR,
WETH_ADDR,
tychoRouterAddr,
USDC_WETH_USV3_2,
true,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory wethUsdcV2OneZeroData = encodeUniswapV2Swap( bytes memory wethUsdcV2OneZeroData = encodeUniswapV2Swap(
WETH_ADDR, USDC_WETH_USV2, tychoRouterAddr, false WETH_ADDR,
USDC_WETH_USV2,
tychoRouterAddr,
false,
TokenTransfer.TransferType.TRANSFER
); );
bytes[] memory swaps = new bytes[](3); bytes[] memory swaps = new bytes[](3);
@@ -547,6 +561,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
wethUsdcV2OneZeroData wethUsdcV2OneZeroData
); );
tychoRouter.exposedSplitSwap(amountIn, 2, pleEncode(swaps)); tychoRouter.exposedSplitSwap(amountIn, 2, pleEncode(swaps));
vm.stopPrank();
assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99574171); assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99574171);
} }
@@ -563,15 +578,29 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
deal(USDC_ADDR, tychoRouterAddr, amountIn); deal(USDC_ADDR, tychoRouterAddr, amountIn);
bytes memory usdcWethV2Data = encodeUniswapV2Swap( bytes memory usdcWethV2Data = encodeUniswapV2Swap(
USDC_ADDR, USDC_WETH_USV2, tychoRouterAddr, true USDC_ADDR,
USDC_WETH_USV2,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER
); );
bytes memory usdcWethV3Pool1OneZeroData = encodeUniswapV3Swap( bytes memory usdcWethV3Pool1OneZeroData = encodeUniswapV3Swap(
WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3, false WETH_ADDR,
USDC_ADDR,
tychoRouterAddr,
USDC_WETH_USV3,
false,
TokenTransfer.TransferType.TRANSFER
); );
bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap(
WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false WETH_ADDR,
USDC_ADDR,
tychoRouterAddr,
USDC_WETH_USV3_2,
false,
TokenTransfer.TransferType.TRANSFER
); );
bytes[] memory swaps = new bytes[](3); bytes[] memory swaps = new bytes[](3);
@@ -610,7 +639,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
deal(BASE_USDC, tychoRouterAddr, amountIn); deal(BASE_USDC, tychoRouterAddr, amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData = encodeUniswapV2Swap(
BASE_USDC, USDC_MAG7_POOL, tychoRouterAddr, true BASE_USDC,
USDC_MAG7_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_FROM
); );
bytes memory swap = encodeSplitSwap( bytes memory swap = encodeSplitSwap(

View File

@@ -102,7 +102,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
new UniswapV2Executor(factoryV2, initCodeV2, PERMIT2_ADDRESS); new UniswapV2Executor(factoryV2, initCodeV2, PERMIT2_ADDRESS);
usv3Executor = usv3Executor =
new UniswapV3Executor(factoryV3, initCodeV3, PERMIT2_ADDRESS); new UniswapV3Executor(factoryV3, initCodeV3, PERMIT2_ADDRESS);
usv4Executor = new UniswapV4Executor(poolManager); usv4Executor = new UniswapV4Executor(poolManager, PERMIT2_ADDRESS);
pancakev3Executor = new UniswapV3Executor( pancakev3Executor = new UniswapV3Executor(
factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS
); );
@@ -178,15 +178,11 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
address tokenIn, address tokenIn,
address target, address target,
address receiver, address receiver,
bool zero2one bool zero2one,
TokenTransfer.TransferType transferType
) internal pure returns (bytes memory) { ) internal pure returns (bytes memory) {
return abi.encodePacked( return
tokenIn, abi.encodePacked(tokenIn, target, receiver, zero2one, transferType);
target,
receiver,
zero2one,
TokenTransfer.TransferType.TRANSFER
);
} }
function encodeUniswapV3Swap( function encodeUniswapV3Swap(
@@ -194,7 +190,8 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
address tokenOut, address tokenOut,
address receiver, address receiver,
address target, address target,
bool zero2one bool zero2one,
TokenTransfer.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(
@@ -204,7 +201,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
receiver, receiver,
target, target,
zero2one, zero2one,
TokenTransfer.TransferType.TRANSFER transferType
); );
} }
} }

View File

@@ -176,7 +176,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
WETH_DAI_POOL, WETH_DAI_POOL,
BOB, BOB,
zeroForOne, zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFERFROM) uint8(TokenTransfer.TransferType.TRANSFER_FROM)
); );
deal(WETH_ADDR, address(this), amountIn); deal(WETH_ADDR, address(this), amountIn);
@@ -197,7 +197,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
WETH_DAI_POOL, WETH_DAI_POOL,
ALICE, ALICE,
zeroForOne, zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFERPERMIT2) uint8(TokenTransfer.TransferType.TRANSFER_PERMIT2)
); );
deal(WETH_ADDR, ALICE, amountIn); deal(WETH_ADDR, ALICE, amountIn);

View File

@@ -7,9 +7,12 @@ import "@src/executors/UniswapV4Executor.sol";
import {Constants} from "../Constants.sol"; import {Constants} from "../Constants.sol";
import {Test} from "../../lib/forge-std/src/Test.sol"; import {Test} from "../../lib/forge-std/src/Test.sol";
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol"; import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
import "@src/executors/TokenTransfer.sol";
contract UniswapV4ExecutorExposed is UniswapV4Executor { contract UniswapV4ExecutorExposed is UniswapV4Executor {
constructor(IPoolManager _poolManager) UniswapV4Executor(_poolManager) {} constructor(IPoolManager _poolManager, address _permit2)
UniswapV4Executor(_poolManager, _permit2)
{}
function decodeData(bytes calldata data) function decodeData(bytes calldata data)
external external
@@ -18,6 +21,7 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
bool zeroForOne, bool zeroForOne,
TokenTransfer.TransferType transferType,
UniswapV4Pool[] memory pools UniswapV4Pool[] memory pools
) )
{ {
@@ -36,8 +40,9 @@ contract UniswapV4ExecutorTest is Test, Constants {
function setUp() public { function setUp() public {
uint256 forkBlock = 21817316; uint256 forkBlock = 21817316;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV4Exposed = uniswapV4Exposed = new UniswapV4ExecutorExposed(
new UniswapV4ExecutorExposed(IPoolManager(poolManager)); IPoolManager(poolManager), PERMIT2_ADDRESS
);
} }
function testDecodeParams() public view { function testDecodeParams() public view {
@@ -46,6 +51,8 @@ contract UniswapV4ExecutorTest is Test, Constants {
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;
UniswapV4Executor.UniswapV4Pool[] memory pools = UniswapV4Executor.UniswapV4Pool[] memory pools =
new UniswapV4Executor.UniswapV4Pool[](2); new UniswapV4Executor.UniswapV4Pool[](2);
@@ -61,19 +68,21 @@ contract UniswapV4ExecutorTest is Test, Constants {
}); });
bytes memory data = UniswapV4Utils.encodeExactInput( bytes memory data = UniswapV4Utils.encodeExactInput(
USDE_ADDR, USDT_ADDR, zeroForOne, pools USDE_ADDR, USDT_ADDR, zeroForOne, transferType, pools
); );
( (
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
bool zeroForOneDecoded, bool zeroForOneDecoded,
TokenTransfer.TransferType transferTypeDecoded,
UniswapV4Executor.UniswapV4Pool[] memory decodedPools UniswapV4Executor.UniswapV4Pool[] memory decodedPools
) = uniswapV4Exposed.decodeData(data); ) = uniswapV4Exposed.decodeData(data);
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(decodedPools.length, 2); assertEq(decodedPools.length, 2);
assertEq(decodedPools[0].intermediaryToken, USDT_ADDR); assertEq(decodedPools[0].intermediaryToken, USDT_ADDR);
assertEq(decodedPools[0].fee, pool1Fee); assertEq(decodedPools[0].fee, pool1Fee);
@@ -98,8 +107,13 @@ contract UniswapV4ExecutorTest is Test, Constants {
tickSpacing: int24(1) tickSpacing: int24(1)
}); });
bytes memory data = bytes memory data = UniswapV4Utils.encodeExactInput(
UniswapV4Utils.encodeExactInput(USDE_ADDR, USDT_ADDR, true, pools); USDE_ADDR,
USDT_ADDR,
true,
TokenTransfer.TransferType.NONE,
pools
);
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data); uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn); assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
@@ -114,7 +128,7 @@ contract UniswapV4ExecutorTest is Test, Constants {
// USDE -> USDT // USDE -> USDT
// Generated by the Tycho swap encoder - test_encode_uniswap_v4_simple_swap // Generated by the Tycho swap encoder - test_encode_uniswap_v4_simple_swap
bytes memory protocolData = bytes memory protocolData =
hex"4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec701dac17f958d2ee523a2206206994597c13d831ec7000064000001"; hex"4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec701f62849f9a0b5bf2913b396098f7c7019b51a820a00dac17f958d2ee523a2206206994597c13d831ec7000064000001";
uint256 amountIn = 100 ether; uint256 amountIn = 100 ether;
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn); deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager); uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
@@ -151,8 +165,13 @@ contract UniswapV4ExecutorTest is Test, Constants {
tickSpacing: int24(60) tickSpacing: int24(60)
}); });
bytes memory data = bytes memory data = UniswapV4Utils.encodeExactInput(
UniswapV4Utils.encodeExactInput(USDE_ADDR, WBTC_ADDR, true, pools); USDE_ADDR,
WBTC_ADDR,
true,
TokenTransfer.TransferType.NONE,
pools
);
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data); uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn); assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
@@ -170,7 +189,7 @@ contract UniswapV4ExecutorTest is Test, Constants {
// Generated by the Tycho swap encoder - test_encode_uniswap_v4_sequential_swap // Generated by the Tycho swap encoder - test_encode_uniswap_v4_sequential_swap
bytes memory protocolData = bytes memory protocolData =
hex"4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c59901dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c"; hex"4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c59901f62849f9a0b5bf2913b396098f7c7019b51a820a00dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c";
uint256 amountIn = 100 ether; uint256 amountIn = 100 ether;
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn); deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);

View File

@@ -8,6 +8,7 @@ library UniswapV4Utils {
address tokenIn, address tokenIn,
address tokenOut, address tokenOut,
bool zeroForOne, bool zeroForOne,
UniswapV4Executor.TransferType transferType,
UniswapV4Executor.UniswapV4Pool[] memory pools UniswapV4Executor.UniswapV4Pool[] memory pools
) public pure returns (bytes memory) { ) public pure returns (bytes memory) {
bytes memory encodedPools; bytes memory encodedPools;
@@ -21,6 +22,12 @@ library UniswapV4Utils {
); );
} }
return abi.encodePacked(tokenIn, tokenOut, zeroForOne, encodedPools); return abi.encodePacked(
tokenIn,
tokenOut,
zeroForOne,
transferType,
encodedPools
);
} }
} }

View File

@@ -26,3 +26,14 @@ pub static IN_TRANSFER_OPTIMIZABLE_PROTOCOLS: LazyLock<HashSet<&'static str>> =
set.insert("uniswap_v3"); set.insert("uniswap_v3");
set set
}); });
/// These protocols expect funds to be in the router at the time of swap.
pub static PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER: LazyLock<HashSet<&'static str>> =
LazyLock::new(|| {
let mut set = HashSet::new();
set.insert("curve");
set.insert("balancer_v2");
// TODO remove uniswap_v4 when we add callback support for transfer optimizations
set.insert("uniswap_v4");
set
});

View File

@@ -39,6 +39,7 @@ pub struct SingleSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry, swap_encoder_registry: SwapEncoderRegistry,
permit2: Option<Permit2>, permit2: Option<Permit2>,
selector: String, selector: String,
native_address: Bytes,
router_address: Bytes, router_address: Bytes,
} }
@@ -57,7 +58,13 @@ impl SingleSwapStrategyEncoder {
"singleSwap(uint256,address,address,uint256,bool,bool,address,bytes)".to_string(), "singleSwap(uint256,address,address,uint256,bool,bool,address,bytes)".to_string(),
) )
}; };
Ok(Self { permit2, selector, swap_encoder_registry, router_address }) Ok(Self {
permit2,
selector,
swap_encoder_registry,
native_address: chain.native_token()?,
router_address,
})
} }
/// Encodes information necessary for performing a single hop against a given executor for /// Encodes information necessary for performing a single hop against a given executor for
@@ -117,6 +124,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
let transfer_type = self.get_transfer_method( let transfer_type = self.get_transfer_method(
swap.clone(), swap.clone(),
solution.given_token.clone(), solution.given_token.clone(),
self.native_address.clone(),
self.permit2.clone().is_some(), self.permit2.clone().is_some(),
); );
@@ -289,6 +297,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
let transfer_type = self.get_transfer_method( let transfer_type = self.get_transfer_method(
swap.clone(), swap.clone(),
solution.given_token.clone(), solution.given_token.clone(),
self.native_address.clone(),
self.permit2.clone().is_some(), self.permit2.clone().is_some(),
); );
@@ -515,6 +524,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
let transfer_type = self.get_transfer_method( let transfer_type = self.get_transfer_method(
swap.clone(), swap.clone(),
solution.given_token.clone(), solution.given_token.clone(),
self.native_address.clone(),
self.permit2.clone().is_some(), self.permit2.clone().is_some(),
); );
@@ -857,6 +867,7 @@ mod tests {
"0000000000000000000000000000", // padding "0000000000000000000000000000", // padding
)); ));
let hex_calldata = encode(&calldata); let hex_calldata = encode(&calldata);
println!("{}", hex_calldata);
assert_eq!(hex_calldata[..456], expected_input); assert_eq!(hex_calldata[..456], expected_input);
assert_eq!(hex_calldata[1224..], expected_swap); assert_eq!(hex_calldata[1224..], expected_swap);
@@ -1354,9 +1365,9 @@ mod tests {
let expected_swaps = String::from(concat!( let expected_swaps = String::from(concat!(
// length of ple encoded swaps without padding // length of ple encoded swaps without padding
"0000000000000000000000000000000000000000000000000000000000000078", "0000000000000000000000000000000000000000000000000000000000000079",
// ple encoded swaps // ple encoded swaps
"0076", // Swap length "0077", // Swap length
"00", // token in index "00", // token in index
"01", // token out index "01", // token out index
"000000", // split "000000", // split
@@ -1366,6 +1377,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in
"6982508145454ce325ddbe47a25d4ec3d2311933", // group token in "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in
"00", // zero2one "00", // zero2one
"04", // transfer type (transfer to router)
// First pool params // First pool params
"0000000000000000000000000000000000000000", // intermediary token (ETH) "0000000000000000000000000000000000000000", // intermediary token (ETH)
"000bb8", // fee "000bb8", // fee
@@ -1374,7 +1386,7 @@ mod tests {
"6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE) "6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE)
"0061a8", // fee "0061a8", // fee
"0001f4", // tick spacing "0001f4", // tick spacing
"0000000000000000" // padding "00000000000000" // padding
)); ));
let hex_calldata = encode(&calldata); let hex_calldata = encode(&calldata);

View File

@@ -1,26 +1,47 @@
use tycho_common::Bytes; use tycho_common::Bytes;
use crate::encoding::{ use crate::encoding::{
evm::constants::IN_TRANSFER_OPTIMIZABLE_PROTOCOLS, evm::constants::{IN_TRANSFER_OPTIMIZABLE_PROTOCOLS, PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER},
models::{Swap, TransferType}, models::{Swap, TransferType},
}; };
/// A trait that defines how the tokens will be transferred into the given pool given the solution. /// A trait that defines how the tokens will be transferred into the given pool given the solution.
pub trait TransferOptimization { pub trait TransferOptimization {
/// Returns the transfer method that should be used for the given swap and solution. /// Returns the transfer method that should be used for the given swap and solution.
/// fn get_transfer_method(
/// If the swap is for the in token of the solution and the protocol supports transferring &self,
/// straight from the user, it will return `TransferType::Permit2Transfer` or swap: Swap,
/// `TransferType::TransferFrom`. given_token: Bytes,
fn get_transfer_method(&self, swap: Swap, given_token: Bytes, permit2: bool) -> TransferType { native_token: Bytes,
let optimize_in_transfer = permit2: bool,
) -> TransferType {
let send_funds_to_pool: bool =
IN_TRANSFER_OPTIMIZABLE_PROTOCOLS.contains(&swap.component.protocol_system.as_str()); IN_TRANSFER_OPTIMIZABLE_PROTOCOLS.contains(&swap.component.protocol_system.as_str());
if (swap.token_in == given_token) && optimize_in_transfer { let funds_expected_in_router: bool =
if permit2 { PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER.contains(&swap.component.protocol_system.as_str());
if (swap.token_in == given_token) && send_funds_to_pool {
if swap.token_in == native_token {
// Funds are already in router. Transfer from router to pool.
TransferType::Transfer
} else if permit2 {
// Transfer from swapper to pool using permit2.
TransferType::Permit2Transfer TransferType::Permit2Transfer
} else { } else {
// Transfer from swapper to pool.
TransferType::TransferFrom TransferType::TransferFrom
} }
} else if (swap.token_in == given_token) && funds_expected_in_router {
if swap.token_in == native_token {
// Funds already in router. Do nothing.
TransferType::None
} else if permit2 {
// Transfer from swapper to router using permit2.
TransferType::Permit2TransferToRouter
} else {
// Transfer from swapper to router.
TransferType::TransferToRouter
}
} else { } else {
TransferType::Transfer TransferType::Transfer
} }
@@ -41,6 +62,10 @@ mod tests {
Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec()) Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
} }
fn eth() -> Bytes {
Bytes::from(hex!("0000000000000000000000000000000000000000").to_vec())
}
fn dai() -> Bytes { fn dai() -> Bytes {
Bytes::from(hex!("6b175474e89094c44da98b954eedeac495271d0f").to_vec()) Bytes::from(hex!("6b175474e89094c44da98b954eedeac495271d0f").to_vec())
} }
@@ -57,7 +82,7 @@ mod tests {
split: 0f64, split: 0f64,
}; };
let strategy = MockStrategy {}; let strategy = MockStrategy {};
let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), true); let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), eth(), true);
assert_eq!(transfer_method, TransferType::Permit2Transfer); assert_eq!(transfer_method, TransferType::Permit2Transfer);
} }
@@ -73,7 +98,7 @@ mod tests {
split: 0f64, split: 0f64,
}; };
let strategy = MockStrategy {}; let strategy = MockStrategy {};
let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), false); let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), eth(), false);
assert_eq!(transfer_method, TransferType::TransferFrom); assert_eq!(transfer_method, TransferType::TransferFrom);
} }
@@ -89,7 +114,7 @@ mod tests {
split: 0f64, split: 0f64,
}; };
let strategy = MockStrategy {}; let strategy = MockStrategy {};
let transfer_method = strategy.get_transfer_method(swap.clone(), dai(), false); let transfer_method = strategy.get_transfer_method(swap.clone(), dai(), eth(), false);
assert_eq!(transfer_method, TransferType::Transfer); assert_eq!(transfer_method, TransferType::Transfer);
} }
} }

View File

@@ -202,7 +202,13 @@ impl SwapEncoder for UniswapV4SwapEncoder {
let pool_params = let pool_params =
(token_out_address, pool_fee_u24, pool_tick_spacing_u24).abi_encode_packed(); (token_out_address, pool_fee_u24, pool_tick_spacing_u24).abi_encode_packed();
let args = (group_token_in_address, group_token_out_address, zero_to_one, pool_params); let args = (
group_token_in_address,
group_token_out_address,
zero_to_one,
(encoding_context.transfer_type as u8).to_be_bytes(),
pool_params,
);
Ok(args.abi_encode_packed()) Ok(args.abi_encode_packed())
} }
@@ -786,6 +792,8 @@ mod tests {
"dac17f958d2ee523a2206206994597c13d831ec7", "dac17f958d2ee523a2206206994597c13d831ec7",
// zero for one // zero for one
"01", "01",
// transfer type
"00",
// pool params: // pool params:
// - intermediary token // - intermediary token
"dac17f958d2ee523a2206206994597c13d831ec7", "dac17f958d2ee523a2206206994597c13d831ec7",
@@ -942,6 +950,7 @@ mod tests {
let combined_hex = let combined_hex =
format!("{}{}", encode(&initial_encoded_swap), encode(&second_encoded_swap)); format!("{}{}", encode(&initial_encoded_swap), encode(&second_encoded_swap));
println!("{}", combined_hex);
assert_eq!( assert_eq!(
combined_hex, combined_hex,
String::from(concat!( String::from(concat!(
@@ -951,6 +960,8 @@ mod tests {
"2260fac5e5542a773aa44fbcfedf7c193bc2c599", "2260fac5e5542a773aa44fbcfedf7c193bc2c599",
// zero for one // zero for one
"01", "01",
// transfer type
"00",
// pool params: // pool params:
// - intermediary token USDT // - intermediary token USDT
"dac17f958d2ee523a2206206994597c13d831ec7", "dac17f958d2ee523a2206206994597c13d831ec7",

View File

@@ -1048,6 +1048,8 @@ mod tests {
"6982508145454ce325ddbe47a25d4ec3d2311933", "6982508145454ce325ddbe47a25d4ec3d2311933",
// zero for one // zero for one
"00", "00",
// transfer type
"00",
// first pool intermediary token (ETH) // first pool intermediary token (ETH)
"0000000000000000000000000000000000000000", "0000000000000000000000000000000000000000",
// fee // fee

View File

@@ -103,6 +103,8 @@ pub struct Transaction {
/// * `Transfer`: Transfer the token from the router into the pool. /// * `Transfer`: Transfer the token from the router into the pool.
/// * `TransferFrom`: Transfer the token from the swapper to the pool. /// * `TransferFrom`: Transfer the token from the swapper to the pool.
/// * `Permit2Transfer`: Transfer the token from the sender to the pool using Permit2. /// * `Permit2Transfer`: Transfer the token from the sender to the pool using Permit2.
/// * `TransferToRouter`: Transfer the token from the swapper to the router.
/// * `Permit2TransferToRouter`: Transfer the token from the swapper to the router using Permit2.
/// * `None`: No transfer is needed. Tokens are already in the pool. /// * `None`: No transfer is needed. Tokens are already in the pool.
#[repr(u8)] #[repr(u8)]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@@ -110,7 +112,9 @@ pub enum TransferType {
Transfer = 0, Transfer = 0,
TransferFrom = 1, TransferFrom = 1,
Permit2Transfer = 2, Permit2Transfer = 2,
None = 3, TransferToRouter = 3,
Permit2TransferToRouter = 4,
None = 5,
} }
/// Represents necessary attributes for encoding an order. /// Represents necessary attributes for encoding an order.