Merge pull request #111 from propeller-heads/router/tnl/executor-transfer-sketch

feat: TokenTransfer helper contract
This commit is contained in:
Tamara
2025-04-14 11:30:19 -04:00
committed by Diana Carvalho
14 changed files with 467 additions and 212 deletions

View File

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

View File

@@ -4,20 +4,23 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol";
import "./TokenTransfer.sol";
error UniswapV2Executor__InvalidDataLength();
error UniswapV2Executor__InvalidTarget();
error UniswapV2Executor__InvalidFactory();
error UniswapV2Executor__InvalidInitCode();
contract UniswapV2Executor is IExecutor {
contract UniswapV2Executor is IExecutor, TokenTransfer {
using SafeERC20 for IERC20;
address public immutable factory;
bytes32 public immutable initCode;
address private immutable self;
constructor(address _factory, bytes32 _initCode) {
constructor(address _factory, bytes32 _initCode, address _permit2)
TokenTransfer(_permit2)
{
if (_factory == address(0)) {
revert UniswapV2Executor__InvalidFactory();
}
@@ -35,17 +38,19 @@ contract UniswapV2Executor is IExecutor {
payable
returns (uint256 calculatedAmount)
{
IERC20 tokenIn;
address target;
address receiver;
bool zeroForOne;
IERC20 tokenIn;
TransferType transferType;
(tokenIn, target, receiver, zeroForOne) = _decodeData(data);
(tokenIn, target, receiver, zeroForOne, transferType) =
_decodeData(data);
_verifyPairAddress(target);
calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne);
tokenIn.safeTransfer(target, givenAmount);
_transfer(tokenIn, msg.sender, target, givenAmount, transferType);
IUniswapV2Pair pool = IUniswapV2Pair(target);
if (zeroForOne) {
@@ -62,16 +67,18 @@ contract UniswapV2Executor is IExecutor {
IERC20 inToken,
address target,
address receiver,
bool zeroForOne
bool zeroForOne,
TransferType transferType
)
{
if (data.length != 61) {
if (data.length != 62) {
revert UniswapV2Executor__InvalidDataLength();
}
inToken = IERC20(address(bytes20(data[0:20])));
target = address(bytes20(data[20:40]));
receiver = address(bytes20(data[40:60]));
zeroForOne = uint8(data[60]) > 0;
transferType = TransferType(uint8(data[61]));
}
function _getAmountOut(address target, uint256 amountIn, bool zeroForOne)

View File

@@ -5,13 +5,15 @@ import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@interfaces/ICallback.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
error UniswapV3Executor__InvalidDataLength();
error UniswapV3Executor__InvalidFactory();
error UniswapV3Executor__InvalidTarget();
error UniswapV3Executor__InvalidInitCode();
error UniswapV3Executor__InvalidTransferType(uint8 transferType);
contract UniswapV3Executor is IExecutor, ICallback {
contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
using SafeERC20 for IERC20;
uint160 private constant MIN_SQRT_RATIO = 4295128739;
@@ -22,7 +24,9 @@ contract UniswapV3Executor is IExecutor, ICallback {
bytes32 public immutable initCode;
address private immutable self;
constructor(address _factory, bytes32 _initCode) {
constructor(address _factory, bytes32 _initCode, address _permit2)
TokenTransfer(_permit2)
{
if (_factory == address(0)) {
revert UniswapV3Executor__InvalidFactory();
}
@@ -46,7 +50,8 @@ contract UniswapV3Executor is IExecutor, ICallback {
uint24 fee,
address receiver,
address target,
bool zeroForOne
bool zeroForOne,
TransferType transferType
) = _decodeData(data);
_verifyPairAddress(tokenIn, tokenOut, fee, target);
@@ -55,7 +60,8 @@ contract UniswapV3Executor is IExecutor, ICallback {
int256 amount1;
IUniswapV3Pool pool = IUniswapV3Pool(target);
bytes memory callbackData = _makeV3CallbackData(tokenIn, tokenOut, fee);
bytes memory callbackData =
_makeV3CallbackData(tokenIn, tokenOut, fee, transferType);
{
(amount0, amount1) = pool.swap(
@@ -92,12 +98,21 @@ contract UniswapV3Executor is IExecutor, ICallback {
address tokenIn = address(bytes20(msgData[132:152]));
// Transfer type does not exist
if (uint8(msgData[175]) > uint8(TransferType.NONE)) {
revert UniswapV3Executor__InvalidTransferType(uint8(msgData[175]));
}
TransferType transferType = TransferType(uint8(msgData[175]));
address sender = address(bytes20(msgData[176:196]));
verifyCallback(msgData[132:]);
uint256 amountOwed =
amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
IERC20(tokenIn).safeTransfer(msg.sender, amountOwed);
_transfer(IERC20(tokenIn), sender, msg.sender, amountOwed, transferType);
return abi.encode(amountOwed, tokenIn);
}
@@ -132,10 +147,11 @@ contract UniswapV3Executor is IExecutor, ICallback {
uint24 fee,
address receiver,
address target,
bool zeroForOne
bool zeroForOne,
TransferType transferType
)
{
if (data.length != 84) {
if (data.length != 85) {
revert UniswapV3Executor__InvalidDataLength();
}
tokenIn = address(bytes20(data[0:20]));
@@ -144,14 +160,18 @@ contract UniswapV3Executor is IExecutor, ICallback {
receiver = address(bytes20(data[43:63]));
target = address(bytes20(data[63:83]));
zeroForOne = uint8(data[83]) > 0;
transferType = TransferType(uint8(data[84]));
}
function _makeV3CallbackData(address tokenIn, address tokenOut, uint24 fee)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(tokenIn, tokenOut, fee);
function _makeV3CallbackData(
address tokenIn,
address tokenOut,
uint24 fee,
TransferType transferType
) internal view returns (bytes memory) {
return abi.encodePacked(
tokenIn, tokenOut, fee, uint8(transferType), msg.sender
);
}
function _verifyPairAddress(

View File

@@ -0,0 +1,87 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
import "./Constants.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Permit2TestHelper is Constants {
/**
* @dev Handles the Permit2 approval process for Alice, allowing the TychoRouter contract
* to spend `amount_in` of `tokenIn` on her behalf.
*
* This function approves the Permit2 contract to transfer the specified token amount
* and constructs a `PermitSingle` struct for the approval. It also generates a valid
* EIP-712 signature for the approval using Alice's private key.
*
* @param tokenIn The address of the token being approved.
* @param amount_in The amount of tokens to approve for transfer.
* @return permitSingle The `PermitSingle` struct containing the approval details.
* @return signature The EIP-712 signature for the approval.
*/
function handlePermit2Approval(
address tokenIn,
address spender,
uint256 amount_in
) internal returns (IAllowanceTransfer.PermitSingle memory, bytes memory) {
IERC20(tokenIn).approve(PERMIT2_ADDRESS, amount_in);
IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer
.PermitSingle({
details: IAllowanceTransfer.PermitDetails({
token: tokenIn,
amount: uint160(amount_in),
expiration: uint48(block.timestamp + 1 days),
nonce: 0
}),
spender: spender,
sigDeadline: block.timestamp + 1 days
});
bytes memory signature = signPermit2(permitSingle, ALICE_PK);
return (permitSingle, signature);
}
/**
* @dev Signs a Permit2 `PermitSingle` struct with the given private key.
* @param permit The `PermitSingle` struct to sign.
* @param privateKey The private key of the signer.
* @return The signature as a `bytes` array.
*/
function signPermit2(
IAllowanceTransfer.PermitSingle memory permit,
uint256 privateKey
) internal view returns (bytes memory) {
bytes32 _PERMIT_DETAILS_TYPEHASH = keccak256(
"PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
);
bytes32 _PERMIT_SINGLE_TYPEHASH = keccak256(
"PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
);
bytes32 domainSeparator = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,uint256 chainId,address verifyingContract)"
),
keccak256("Permit2"),
block.chainid,
PERMIT2_ADDRESS
)
);
bytes32 detailsHash =
keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permit.details));
bytes32 permitHash = keccak256(
abi.encode(
_PERMIT_SINGLE_TYPEHASH,
detailsHash,
permit.spender,
permit.sigDeadline
)
);
bytes32 digest =
keccak256(abi.encodePacked("\x19\x01", domainSeparator, permitHash));
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
return abi.encodePacked(r, s, v);
}
}

View File

@@ -4,36 +4,15 @@ pragma solidity ^0.8.26;
import "./TychoRouterTestSetup.sol";
contract TychoRouterTestIntegration is TychoRouterTestSetup {
function testSplitSwapSingleIntegration() public {
// Tests swapping WETH -> DAI on a USV2 pool
deal(WETH_ADDR, ALICE, 1 ether);
uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE);
// Approve permit2
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_swap_strategy_encoder_simple`
(bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681362ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdcf2000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a82e9bdde90314de4b1bf918cc2e8b27da98adcab46e8e99d4e77472a572d6381837e9453095f4cc5e9b25691b678288174e547e040a67d12b36ddfdd1e672d21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
);
vm.stopPrank();
uint256 balancerAfter = IERC20(DAI_ADDR).balanceOf(ALICE);
assertTrue(success, "Call Failed");
assertEq(balancerAfter - balancerBefore, 2659881924818443699787);
}
function testSplitSwapSingleWithoutPermit2Integration() public {
// Tests swapping WETH -> DAI on a USV2 pool without permit2
deal(WETH_ADDR, ALICE, 1 ether);
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), 1 ether);
uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE);
// Encoded solution generated using `test_split_swap_strategy_encoder_simple_route_no_permit2`
// Encoded solution generated using `test_split_swap_strategy_encoder_no_permit2`
(bool success,) = tychoRouterAddr.call(
hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
);
vm.stopPrank();
@@ -128,9 +107,9 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
// Approve permit2
vm.startPrank(ALICE);
// Encoded solution generated using `test_split_swap_strategy_encoder_simple_route_wrap`
// Encoded solution generated using `test_split_swap_strategy_encoder_wrap`
(bool success,) = tychoRouterAddr.call{value: 1 ether}(
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006813638900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd91000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f6ff7411a7ec76cb7dcafecf5e7f11121b1aa88af505635dc7faae6057e4f44e2859712f58331a14a1624f1e5edf2af80ddd2d90b5453d74df1b1fea10b9a2f91c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e435000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6bd580000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415563c90eb0f8a79e234e300520b50879242b42622cabd5d01c19e67aba4854a723e0bd8333774062ae03a22b8c5de4a0dfd70bffd9197f56b2063f390610e5891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
);
vm.stopPrank();
@@ -150,9 +129,9 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
// Approve permit2
vm.startPrank(ALICE);
IERC20(DAI_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_swap_strategy_encoder_simple_route_unwrap`
// Encoded solution generated using `test_split_swap_strategy_encoder_unwrap`
(bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006813615200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdb5a000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a7da748b04674485a5da185055affefc85b6d8fe412accce55b6f67842116f0f7f7130de5d74c68c20e1cedcdf93b8741b9171de2e6a3f2567887382a0712e3f1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000"
hex"7c5538460000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000681e436b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6bd730000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000411150ac5d21c61fcd35b486034a433e44d045950a52e991a7ad4035f3ad52beee5d9ecbc5ed9c370730cd2631a050fbe1219dd606e39c8aca40d588aa3eab03411c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000"
);
vm.stopPrank();
@@ -204,7 +183,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_swap_strategy_encoder_complex`
(bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681363a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebddab0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415b7ff43991de10c4c3a0372653891d27eb305ce04228bfe46a7d84a0978063fc4cb05183f19b83511bcb689b002d4f8e170f1d3cd77cf18c638229ccb67e0cac1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160005600028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005602030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005601030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d0139501"
hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e2d8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a793000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e9868ce97a4c09488710ce748362ca38418bda44148bb5faf7820445d47efaff66f8f3667a0b3091c9d67d240db4cc2c95db27bdf019e4d5ff76b7b6620514691b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d013950100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000000000"
);
vm.stopPrank();
@@ -231,7 +210,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_sequential_swap_strategy_encoder_complex_route`
(bool success,) = tychoRouterAddr.call(
hex"51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068168aea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ef04f200000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004154956683effd126a9182e2d82ebd3d778e5283b93d571b13cdbc9dfbf3d9f655057a2332ed566f79bed7514a22ef1c52969132bc71a5a2ef125d78e39ec264511c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a600515615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000515615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000"
hex"51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e493d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6c34500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004189757b528c9874627ca7ecd64bd662db5fd7855837ec98cca5cff2cbd599f9af15baff1d389b5de4ca0dc1106b9d3795a1695934cd5fa1158fffcc8d6495dfaa1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000"
);
vm.stopPrank();
@@ -255,7 +234,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max);
// Encoded solution generated using `test_sequential_swap_strategy_encoder_no_permit2`
(bool success,) = tychoRouterAddr.call(
hex"e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a600515615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000515615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000"
hex"e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000"
);
vm.stopPrank();
@@ -275,7 +254,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_cyclic_sequential_swap`
(bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681363d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebddda0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000418d58a54a3b8afc5d2e228ce6c5a1ab6b342cb5bfd9a00d57b869a4703ca2bb084d10d21f6842be9652a9ff2392673fbdcb961439ccc962de09f6bc64e5e665fe1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000"
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e4a0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6c40a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416a11f27f0546e9cc9d27e4383a3f860d9822f0d7d0117e73abbf03007b3e235b36c2288ff083a04f51285092ff23422b3e00a5a292d5627172dfd031308fc3a11b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000"
);
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294);
@@ -291,7 +270,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_input_cyclic_swap`
(bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006816408300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067eeba8b0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416de253b927fdcf110d157372e620e70c7220d3c01f04e01cdffb076edbb8b42052d281dd6c55a2349502742a0a8de58d2d1dbdc452f6c9d695b1c732c023d0561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801005601000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000"
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e2d1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a71e00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004129478d2bffac3e378054d024ca3461f61e55552e2e8b0c7e0869f38ee834462f157f761e1a67c713f5749a6f6210dc4ac998a0521dda8dcb780b35d774250a141c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
);
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171);
@@ -307,7 +286,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_split_output_cyclic_swap`
(bool success,) = tychoRouterAddr.call(
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006816418400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067eebb8c0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412c44c7de8f7eaaea61e49dbdefdc5606925db6f93db0789e632899ac88d3c7677cc8b69719603ab1b5ecef07d659b7254881d0667a49ebccbf43949b760b041a1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000"
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e2d3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a738000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041196745237299bfab79f038c0c12decec9c46b264621e1f00bbef32b15a22f15543defe95694dd1616aa8785bbb6f91a1557eebc97c0f5ca968c99f250da1b2ce1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000"
);
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908);

View File

@@ -51,7 +51,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps();
tychoRouter.sequentialSwapPermit2(
@@ -150,7 +150,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps();
@@ -229,7 +229,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(USDC_ADDR, amountIn);
) = handlePermit2Approval(USDC_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = new bytes[](2);

View File

@@ -19,7 +19,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
@@ -230,7 +230,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(DAI_ADDR, amountIn);
) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData =
encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true);
@@ -267,7 +267,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max);
// Encoded solution generated using `test_single_swap_strategy_encoder_no_permit2`
(bool success,) = tychoRouterAddr.call(
hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000515615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000"
hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000"
);
vm.stopPrank();
@@ -286,7 +286,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
// Encoded solution generated using `test_single_swap_strategy_encoder`
(bool success,) = tychoRouterAddr.call(
hex"30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006817833200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067effd3a00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417efea09004d5d40d8d072e1ce0a425507717ea485c765eb90c170859197d362b502fb039b4f5cdce57318ecfe3ab276d1ac87771eb5d017b253a8f4107e6a20b1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000515615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000"
hex"30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006817833200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067effd3a00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417efea09004d5d40d8d072e1ce0a425507717ea485c765eb90c170859197d362b502fb039b4f5cdce57318ecfe3ab276d1ac87771eb5d017b253a8f4107e6a20b1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000"
);
vm.stopPrank();

View File

@@ -81,7 +81,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSplitSwaps();
@@ -191,7 +191,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSplitSwaps();
@@ -282,7 +282,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(DAI_ADDR, amountIn);
) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData =
encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true);
@@ -325,7 +325,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI
bool zeroForOne = false;
@@ -377,7 +377,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(WETH_ADDR, amountIn);
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = encodeUniswapV2Swap(
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
@@ -426,7 +426,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(USDE_ADDR, amountIn);
) = handlePermit2Approval(USDE_ADDR, tychoRouterAddr, amountIn);
UniswapV4Executor.UniswapV4Pool[] memory pools =
new UniswapV4Executor.UniswapV4Pool[](1);

View File

@@ -13,6 +13,7 @@ import "@src/TychoRouter.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol";
import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol";
import {Permit2TestHelper} from "./Permit2TestHelper.sol";
contract TychoRouterExposed is TychoRouter {
constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {}
@@ -41,7 +42,7 @@ contract TychoRouterExposed is TychoRouter {
}
}
contract TychoRouterTestSetup is Constants {
contract TychoRouterTestSetup is Constants, Permit2TestHelper {
TychoRouterExposed tychoRouter;
address tychoRouterAddr;
UniswapV2Executor public usv2Executor;
@@ -97,11 +98,14 @@ contract TychoRouterTestSetup is Constants {
address ekuboCore = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444;
IPoolManager poolManager = IPoolManager(poolManagerAddress);
usv2Executor = new UniswapV2Executor(factoryV2, initCodeV2);
usv3Executor = new UniswapV3Executor(factoryV3, initCodeV3);
usv2Executor =
new UniswapV2Executor(factoryV2, initCodeV2, PERMIT2_ADDRESS);
usv3Executor =
new UniswapV3Executor(factoryV3, initCodeV3, PERMIT2_ADDRESS);
usv4Executor = new UniswapV4Executor(poolManager);
pancakev3Executor =
new UniswapV3Executor(factoryPancakeV3, initCodePancakeV3);
pancakev3Executor = new UniswapV3Executor(
factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS
);
balancerv2Executor = new BalancerV2Executor();
ekuboExecutor = new EkuboExecutor(ekuboCore);
curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE);
@@ -129,84 +133,6 @@ contract TychoRouterTestSetup is Constants {
}
}
/**
* @dev Handles the Permit2 approval process for Alice, allowing the TychoRouter contract
* to spend `amount_in` of `tokenIn` on her behalf.
*
* This function approves the Permit2 contract to transfer the specified token amount
* and constructs a `PermitSingle` struct for the approval. It also generates a valid
* EIP-712 signature for the approval using Alice's private key.
*
* @param tokenIn The address of the token being approved.
* @param amount_in The amount of tokens to approve for transfer.
* @return permitSingle The `PermitSingle` struct containing the approval details.
* @return signature The EIP-712 signature for the approval.
*/
function handlePermit2Approval(address tokenIn, uint256 amount_in)
internal
returns (IAllowanceTransfer.PermitSingle memory, bytes memory)
{
IERC20(tokenIn).approve(PERMIT2_ADDRESS, amount_in);
IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer
.PermitSingle({
details: IAllowanceTransfer.PermitDetails({
token: tokenIn,
amount: uint160(amount_in),
expiration: uint48(block.timestamp + 1 days),
nonce: 0
}),
spender: tychoRouterAddr,
sigDeadline: block.timestamp + 1 days
});
bytes memory signature = signPermit2(permitSingle, ALICE_PK);
return (permitSingle, signature);
}
/**
* @dev Signs a Permit2 `PermitSingle` struct with the given private key.
* @param permit The `PermitSingle` struct to sign.
* @param privateKey The private key of the signer.
* @return The signature as a `bytes` array.
*/
function signPermit2(
IAllowanceTransfer.PermitSingle memory permit,
uint256 privateKey
) internal view returns (bytes memory) {
bytes32 _PERMIT_DETAILS_TYPEHASH = keccak256(
"PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
);
bytes32 _PERMIT_SINGLE_TYPEHASH = keccak256(
"PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
);
bytes32 domainSeparator = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,uint256 chainId,address verifyingContract)"
),
keccak256("Permit2"),
block.chainid,
PERMIT2_ADDRESS
)
);
bytes32 detailsHash =
keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permit.details));
bytes32 permitHash = keccak256(
abi.encode(
_PERMIT_SINGLE_TYPEHASH,
detailsHash,
permit.spender,
permit.sigDeadline
)
);
bytes32 digest =
keccak256(abi.encodePacked("\x19\x01", domainSeparator, permitHash));
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
return abi.encodePacked(r, s, v);
}
function pleEncode(bytes[] memory data)
public
pure
@@ -254,7 +180,13 @@ contract TychoRouterTestSetup is Constants {
address receiver,
bool zero2one
) internal pure returns (bytes memory) {
return abi.encodePacked(tokenIn, target, receiver, zero2one);
return abi.encodePacked(
tokenIn,
target,
receiver,
zero2one,
TokenTransfer.TransferType.TRANSFER
);
}
function encodeUniswapV3Swap(
@@ -266,7 +198,13 @@ contract TychoRouterTestSetup is Constants {
) internal view returns (bytes memory) {
IUniswapV3Pool pool = IUniswapV3Pool(target);
return abi.encodePacked(
tokenIn, tokenOut, pool.fee(), receiver, target, zero2one
tokenIn,
tokenOut,
pool.fee(),
receiver,
target,
zero2one,
TokenTransfer.TransferType.TRANSFER
);
}
}

View File

@@ -2,12 +2,14 @@
pragma solidity ^0.8.26;
import "@src/executors/UniswapV2Executor.sol";
import "@src/executors/TokenTransfer.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol";
import {Permit2TestHelper} from "../Permit2TestHelper.sol";
contract UniswapV2ExecutorExposed is UniswapV2Executor {
constructor(address _factory, bytes32 _initCode)
UniswapV2Executor(_factory, _initCode)
constructor(address _factory, bytes32 _initCode, address _permit2)
UniswapV2Executor(_factory, _initCode, _permit2)
{}
function decodeParams(bytes calldata data)
@@ -17,7 +19,8 @@ contract UniswapV2ExecutorExposed is UniswapV2Executor {
IERC20 inToken,
address target,
address receiver,
bool zeroForOne
bool zeroForOne,
TransferType transferType
)
{
return _decodeData(data);
@@ -46,7 +49,7 @@ contract FakeUniswapV2Pool {
}
}
contract UniswapV2ExecutorTest is Test, Constants {
contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
using SafeERC20 for IERC20;
UniswapV2ExecutorExposed uniswapV2Exposed;
@@ -54,32 +57,51 @@ contract UniswapV2ExecutorTest is Test, Constants {
UniswapV2ExecutorExposed pancakeswapV2Exposed;
IERC20 WETH = IERC20(WETH_ADDR);
IERC20 DAI = IERC20(DAI_ADDR);
IAllowanceTransfer permit2;
function setUp() public {
uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV2Exposed = new UniswapV2ExecutorExposed(
USV2_FACTORY_ETHEREUM, USV2_POOL_CODE_INIT_HASH
USV2_FACTORY_ETHEREUM, USV2_POOL_CODE_INIT_HASH, PERMIT2_ADDRESS
);
sushiswapV2Exposed = new UniswapV2ExecutorExposed(
SUSHISWAPV2_FACTORY_ETHEREUM, SUSHIV2_POOL_CODE_INIT_HASH
SUSHISWAPV2_FACTORY_ETHEREUM,
SUSHIV2_POOL_CODE_INIT_HASH,
PERMIT2_ADDRESS
);
pancakeswapV2Exposed = new UniswapV2ExecutorExposed(
PANCAKESWAPV2_FACTORY_ETHEREUM, PANCAKEV2_POOL_CODE_INIT_HASH
PANCAKESWAPV2_FACTORY_ETHEREUM,
PANCAKEV2_POOL_CODE_INIT_HASH,
PERMIT2_ADDRESS
);
permit2 = IAllowanceTransfer(PERMIT2_ADDRESS);
}
function testDecodeParams() public view {
bytes memory params =
abi.encodePacked(WETH_ADDR, address(2), address(3), false);
bytes memory params = abi.encodePacked(
WETH_ADDR,
address(2),
address(3),
false,
TokenTransfer.TransferType.TRANSFER
);
(IERC20 tokenIn, address target, address receiver, bool zeroForOne) =
uniswapV2Exposed.decodeParams(params);
(
IERC20 tokenIn,
address target,
address receiver,
bool zeroForOne,
TokenTransfer.TransferType transferType
) = uniswapV2Exposed.decodeParams(params);
assertEq(address(tokenIn), WETH_ADDR);
assertEq(target, address(2));
assertEq(receiver, address(3));
assertEq(zeroForOne, false);
assertEq(
uint8(TokenTransfer.TransferType.TRANSFER), uint8(transferType)
);
}
function testDecodeParamsInvalidDataLength() public {
@@ -126,12 +148,17 @@ contract UniswapV2ExecutorTest is Test, Constants {
assertGe(amountOut, 0);
}
function testSwap() public {
function testSwapWithTransfer() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData =
abi.encodePacked(WETH_ADDR, WETH_DAI_POOL, BOB, zeroForOne);
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER)
);
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
uniswapV2Exposed.swap(amountIn, protocolData);
@@ -140,24 +167,104 @@ contract UniswapV2ExecutorTest is Test, Constants {
assertGe(finalBalance, amountOut);
}
function testSwapWithTransferFrom() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFERFROM)
);
deal(WETH_ADDR, address(this), amountIn);
IERC20(WETH_ADDR).approve(address(uniswapV2Exposed), amountIn);
uniswapV2Exposed.swap(amountIn, protocolData);
uint256 finalBalance = DAI.balanceOf(BOB);
assertGe(finalBalance, amountOut);
}
function testSwapWithPermit2TransferFrom() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
ALICE,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFERPERMIT2)
);
deal(WETH_ADDR, ALICE, amountIn);
vm.startPrank(ALICE);
(
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) = handlePermit2Approval(
WETH_ADDR, address(uniswapV2Exposed), amountIn
);
// Assume the permit2.approve method will be called from the TychoRouter
// Replicate this scenario in this test.
permit2.permit(ALICE, permitSingle, signature);
uniswapV2Exposed.swap(amountIn, protocolData);
vm.stopPrank();
uint256 finalBalance = DAI.balanceOf(ALICE);
assertGe(finalBalance, amountOut);
}
function testSwapNoTransfer() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.NONE)
);
deal(WETH_ADDR, address(this), amountIn);
IERC20(WETH_ADDR).transfer(address(WETH_DAI_POOL), amountIn);
uniswapV2Exposed.swap(amountIn, protocolData);
uint256 finalBalance = DAI.balanceOf(BOB);
assertGe(finalBalance, amountOut);
}
function testDecodeIntegration() public view {
// Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode
bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000100";
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f564000000000000000000000000000000000000000010000";
(IERC20 tokenIn, address target, address receiver, bool zeroForOne) =
uniswapV2Exposed.decodeParams(protocolData);
(
IERC20 tokenIn,
address target,
address receiver,
bool zeroForOne,
TokenTransfer.TransferType transferType
) = uniswapV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), WETH_ADDR);
assertEq(target, 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
assertEq(receiver, 0x0000000000000000000000000000000000000001);
assertEq(zeroForOne, false);
// TRANSFER = 0
assertEq(0, uint8(transferType));
}
function testSwapIntegration() public {
// Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode
bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00";
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000";
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
@@ -171,8 +278,13 @@ contract UniswapV2ExecutorTest is Test, Constants {
uint256 amountIn = 10 ** 18;
bool zeroForOne = false;
address fakePool = address(new FakeUniswapV2Pool(WETH_ADDR, DAI_ADDR));
bytes memory protocolData =
abi.encodePacked(WETH_ADDR, fakePool, BOB, zeroForOne);
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
fakePool,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER)
);
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
vm.expectRevert(UniswapV2Executor__InvalidTarget.selector);
@@ -186,8 +298,13 @@ contract UniswapV2ExecutorTest is Test, Constants {
vm.rollFork(26857267);
uint256 amountIn = 10 * 10 ** 6;
bool zeroForOne = true;
bytes memory protocolData =
abi.encodePacked(BASE_USDC, USDC_MAG7_POOL, BOB, zeroForOne);
bytes memory protocolData = abi.encodePacked(
BASE_USDC,
USDC_MAG7_POOL,
BOB,
zeroForOne,
uint8(TokenTransfer.TransferType.TRANSFER)
);
deal(BASE_USDC, address(uniswapV2Exposed), amountIn);

View File

@@ -2,12 +2,14 @@
pragma solidity ^0.8.26;
import "@src/executors/UniswapV3Executor.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol";
import {Permit2TestHelper} from "../Permit2TestHelper.sol";
contract UniswapV3ExecutorExposed is UniswapV3Executor {
constructor(address _factory, bytes32 _initCode)
UniswapV3Executor(_factory, _initCode)
constructor(address _factory, bytes32 _initCode, address _permit2)
UniswapV3Executor(_factory, _initCode, _permit2)
{}
function decodeData(bytes calldata data)
@@ -19,7 +21,8 @@ contract UniswapV3ExecutorExposed is UniswapV3Executor {
uint24 fee,
address receiver,
address target,
bool zeroForOne
bool zeroForOne,
TransferType transferType
)
{
return _decodeData(data);
@@ -35,30 +38,40 @@ contract UniswapV3ExecutorExposed is UniswapV3Executor {
}
}
contract UniswapV3ExecutorTest is Test, Constants {
contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
using SafeERC20 for IERC20;
UniswapV3ExecutorExposed uniswapV3Exposed;
UniswapV3ExecutorExposed pancakeV3Exposed;
IERC20 WETH = IERC20(WETH_ADDR);
IERC20 DAI = IERC20(DAI_ADDR);
IAllowanceTransfer permit2;
function setUp() public {
uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV3Exposed = new UniswapV3ExecutorExposed(
USV3_FACTORY_ETHEREUM, USV3_POOL_CODE_INIT_HASH
USV3_FACTORY_ETHEREUM, USV3_POOL_CODE_INIT_HASH, PERMIT2_ADDRESS
);
pancakeV3Exposed = new UniswapV3ExecutorExposed(
PANCAKESWAPV3_DEPLOYER_ETHEREUM, PANCAKEV3_POOL_CODE_INIT_HASH
PANCAKESWAPV3_DEPLOYER_ETHEREUM,
PANCAKEV3_POOL_CODE_INIT_HASH,
PERMIT2_ADDRESS
);
permit2 = IAllowanceTransfer(PERMIT2_ADDRESS);
}
function testDecodeParams() public view {
uint24 expectedPoolFee = 500;
bytes memory data = abi.encodePacked(
WETH_ADDR, DAI_ADDR, expectedPoolFee, address(2), address(3), false
WETH_ADDR,
DAI_ADDR,
expectedPoolFee,
address(2),
address(3),
false,
TokenTransfer.TransferType.TRANSFER
);
(
@@ -67,7 +80,8 @@ contract UniswapV3ExecutorTest is Test, Constants {
uint24 fee,
address receiver,
address target,
bool zeroForOne
bool zeroForOne,
TokenTransfer.TransferType transferType
) = uniswapV3Exposed.decodeData(data);
assertEq(tokenIn, WETH_ADDR);
@@ -76,6 +90,9 @@ contract UniswapV3ExecutorTest is Test, Constants {
assertEq(receiver, address(2));
assertEq(target, address(3));
assertEq(zeroForOne, false);
assertEq(
uint8(transferType), uint8(TokenTransfer.TransferType.TRANSFER)
);
}
function testDecodeParamsInvalidDataLength() public {
@@ -105,8 +122,9 @@ contract UniswapV3ExecutorTest is Test, Constants {
uint256 initialPoolReserve = IERC20(WETH_ADDR).balanceOf(DAI_WETH_USV3);
vm.startPrank(DAI_WETH_USV3);
bytes memory protocolData =
abi.encodePacked(WETH_ADDR, DAI_ADDR, poolFee);
bytes memory protocolData = abi.encodePacked(
WETH_ADDR, DAI_ADDR, poolFee, TokenTransfer.TransferType.TRANSFER
);
uint256 dataOffset = 3; // some offset
uint256 dataLength = protocolData.length;
@@ -116,7 +134,8 @@ contract UniswapV3ExecutorTest is Test, Constants {
int256(0), // amount1Delta
dataOffset,
dataLength,
protocolData
protocolData,
address(uniswapV3Exposed) // transferFrom sender (irrelevant in this case)
);
uniswapV3Exposed.handleCallback(callbackData);
vm.stopPrank();
@@ -137,7 +156,8 @@ contract UniswapV3ExecutorTest is Test, Constants {
uint24(3000),
address(this),
fakePool,
zeroForOne
zeroForOne,
TokenTransfer.TransferType.TRANSFER
);
vm.expectRevert(UniswapV3Executor__InvalidTarget.selector);
@@ -149,11 +169,18 @@ contract UniswapV3ExecutorTest is Test, Constants {
address tokenOut,
address receiver,
address target,
bool zero2one
bool zero2one,
TokenTransfer.TransferType transferType
) internal view returns (bytes memory) {
IUniswapV3Pool pool = IUniswapV3Pool(target);
return abi.encodePacked(
tokenIn, tokenOut, pool.fee(), receiver, target, zero2one
tokenIn,
tokenOut,
pool.fee(),
receiver,
target,
zero2one,
transferType
);
}
}

View File

@@ -709,9 +709,9 @@ mod tests {
let expected_swaps = String::from(concat!(
// length of ple encoded swaps without padding
"0000000000000000000000000000000000000000000000000000000000000058",
"0000000000000000000000000000000000000000000000000000000000000059",
// ple encoded swaps
"0056",
"0057",
// Swap header
"00", // token in index
"01", // token out index
@@ -722,7 +722,7 @@ mod tests {
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"00", // zero2one
"00", // exact out
"00", // transfer type
"00000000000000", // padding
));
let hex_calldata = encode(&calldata);
@@ -817,8 +817,8 @@ mod tests {
// it's hard to assert
let expected_swap = String::from(concat!(
// length of ple encoded swaps without padding
"0000000000000000000000000000000000000000000000000000000000000051",
// length of encoded swap without padding
"0000000000000000000000000000000000000000000000000000000000000052",
// Swap data
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
@@ -826,7 +826,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"00", // zero2one
"00", // exact out
"0000000000000000000000000000", // padding
"00", // transfer type
"00000000000000000000000000", // padding
));
let hex_calldata = encode(&calldata);
@@ -1475,7 +1476,7 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000051", // length of swap bytes without padding
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
// Swap data
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
@@ -1484,7 +1485,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"00", // zero2one
"00", // exact out
"0000000000000000000000000000", // padding
"00", // transfer type
"00000000000000000000000000", // padding
]
.join("");
@@ -1554,8 +1556,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000002", // tokens length
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000120", // offset of ple encoded swaps
"0000000000000000000000000000000000000000000000000000000000000058", // length of ple encoded swaps without padding
"0056", // ple encoded swaps
"0000000000000000000000000000000000000000000000000000000000000059", // length of ple encoded swaps without padding
"0057", // ple encoded swaps
// Swap header
"00", // token in index
"01", // token out index
@@ -1567,7 +1569,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"00", // zero2one
"00", // exact out
"00000000000000", // padding
"00", // transfer type
"000000000000", // padding
]
.join("");
@@ -1806,8 +1809,8 @@ mod tests {
.join("");
let expected_swaps = [
"00000000000000000000000000000000000000000000000000000000000000de", // length of ple encoded swaps without padding
"006d", // ple encoded swaps
"00000000000000000000000000000000000000000000000000000000000000e0", // length of ple encoded swaps without padding
"006e", // ple encoded swaps
"00", // token in index
"01", // token out index
"000000", // split
@@ -1818,7 +1821,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one
"006d", // ple encoded swaps
"00", // transfer type
"006e", // ple encoded swaps
"01", // token in index
"00000000", // split
"2e234dae75c793f67a35089c9d99245e1c58470b", // executor address
@@ -1827,7 +1831,8 @@ mod tests {
"000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"000000", // zero2one
"00", // zero2one
"00", // transfer type
]
.join("");
@@ -1958,8 +1963,8 @@ mod tests {
]
.join("");
let expected_swaps = [
"0000000000000000000000000000000000000000000000000000000000000136", // length of ple encoded swaps without padding
"006d", // ple encoded swaps
"0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding
"006e", // ple encoded swaps
"00", // token in index
"01", // token out index
"999999", // split
@@ -1970,7 +1975,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one
"006d", // ple encoded swaps
"00", // transfer type
"006e", // ple encoded swaps
"00", // token in index
"01", // token out index
"000000", // split
@@ -1981,7 +1987,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"01", // zero2one
"0056", // ple encoded swaps
"00", // transfer type
"0057", // ple encoded swaps
"01", // token in index
"00", // token out index
"000000", // split
@@ -1990,7 +1997,8 @@ mod tests {
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id,
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"00", // zero2one
"00000000000000000000" // padding
"00", // transfer type
"00000000000000" // padding
]
.join("");
assert_eq!(hex_calldata[..520], expected_input);
@@ -2117,8 +2125,8 @@ mod tests {
.join("");
let expected_swaps = [
"0000000000000000000000000000000000000000000000000000000000000136", // length of ple encoded swaps without padding
"0056", // ple encoded swaps
"0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding
"0057", // ple encoded swaps
"00", // token in index
"01", // token out index
"000000", // split
@@ -2127,7 +2135,8 @@ mod tests {
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"01", // zero2one
"006d", // ple encoded swaps
"00", // transfer type
"006e", // ple encoded swaps
"01", // token in index
"00", // token out index
"999999", // split
@@ -2138,7 +2147,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"00", // zero2one
"006d", // ple encoded swaps
"00", // transfer type
"006e", // ple encoded swaps
"01", // token in index
"00", // token out index
"000000", // split
@@ -2149,7 +2159,8 @@ mod tests {
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one
"00000000000000000000" // padding
"00", // transfer type
"00000000000000" // padding
]
.join("");

View File

@@ -22,6 +22,15 @@ use crate::encoding::{
swap_encoder::SwapEncoder,
};
#[allow(dead_code)]
#[repr(u8)]
pub enum TransferType {
Transfer = 0,
TransferFrom = 1,
Permit2Transfer = 2,
None = 3,
}
/// Encodes a swap on a Uniswap V2 pool through the given executor address.
///
/// # Fields
@@ -66,6 +75,7 @@ impl SwapEncoder for UniswapV2SwapEncoder {
component_id,
bytes_to_address(&encoding_context.receiver)?,
zero_to_one,
(TransferType::Transfer as u8).to_be_bytes(),
);
Ok(args.abi_encode_packed())
@@ -128,6 +138,7 @@ impl SwapEncoder for UniswapV3SwapEncoder {
bytes_to_address(&encoding_context.receiver)?,
component_id,
zero_to_one,
(TransferType::Transfer as u8).to_be_bytes(),
);
Ok(args.abi_encode_packed())
@@ -606,6 +617,8 @@ mod tests {
"0000000000000000000000000000000000000001",
// zero for one
"00",
// transfer type (transfer)
"00",
))
);
}
@@ -661,6 +674,8 @@ mod tests {
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
// zero for one
"00",
// transfer type (transfer)
"00",
))
);
}

View File

@@ -916,6 +916,8 @@ mod tests {
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
// zero for one
"00",
// transfer type
"00",
))
);
}