Merge branch 'refs/heads/main' into feat/bebop-rfq-encoder-and-executor
# Conflicts: # foundry/test/TychoRouterProtocolIntegration.t.sol # foundry/test/TychoRouterTestSetup.sol # foundry/test/assets/calldata.txt # foundry/test/protocols/BebopExecutor.t.sol # src/encoding/evm/tycho_encoders.rs Took 4 minutes
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {TychoRouter} from "@src/TychoRouter.sol";
|
||||
import "./TychoRouterTestSetup.sol";
|
||||
import "./executors/UniswapV4Utils.sol";
|
||||
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||
|
||||
contract TychoRouterTest is TychoRouterTestSetup {
|
||||
bytes32 public constant EXECUTOR_SETTER_ROLE =
|
||||
@@ -91,16 +88,16 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
||||
}
|
||||
|
||||
function testWithdrawERC20Tokens() public {
|
||||
vm.startPrank(BOB);
|
||||
mintTokens(100 ether, tychoRouterAddr);
|
||||
vm.stopPrank();
|
||||
IERC20[] memory tokens = new IERC20[](2);
|
||||
tokens[0] = IERC20(WETH_ADDR);
|
||||
tokens[1] = IERC20(USDC_ADDR);
|
||||
for (uint256 i = 0; i < tokens.length; i++) {
|
||||
deal(address(tokens[i]), tychoRouterAddr, 100 ether);
|
||||
}
|
||||
|
||||
vm.startPrank(FUND_RESCUER);
|
||||
IERC20[] memory tokensArray = new IERC20[](3);
|
||||
tokensArray[0] = IERC20(address(tokens[0]));
|
||||
tokensArray[1] = IERC20(address(tokens[1]));
|
||||
tokensArray[2] = IERC20(address(tokens[2]));
|
||||
tychoRouter.withdraw(tokensArray, FUND_RESCUER);
|
||||
|
||||
tychoRouter.withdraw(tokens, FUND_RESCUER);
|
||||
|
||||
// Check balances after withdrawing
|
||||
for (uint256 i = 0; i < tokens.length; i++) {
|
||||
@@ -113,21 +110,22 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
||||
}
|
||||
|
||||
function testWithdrawERC20TokensFailures() public {
|
||||
mintTokens(100 ether, tychoRouterAddr);
|
||||
IERC20[] memory tokensArray = new IERC20[](3);
|
||||
tokensArray[0] = IERC20(address(tokens[0]));
|
||||
tokensArray[1] = IERC20(address(tokens[1]));
|
||||
tokensArray[2] = IERC20(address(tokens[2]));
|
||||
IERC20[] memory tokens = new IERC20[](2);
|
||||
tokens[0] = IERC20(WETH_ADDR);
|
||||
tokens[1] = IERC20(USDC_ADDR);
|
||||
for (uint256 i = 0; i < tokens.length; i++) {
|
||||
deal(address(tokens[i]), tychoRouterAddr, 100 ether);
|
||||
}
|
||||
|
||||
vm.startPrank(FUND_RESCUER);
|
||||
vm.expectRevert(TychoRouter__AddressZero.selector);
|
||||
tychoRouter.withdraw(tokensArray, address(0));
|
||||
tychoRouter.withdraw(tokens, address(0));
|
||||
vm.stopPrank();
|
||||
|
||||
// Not role FUND_RESCUER
|
||||
vm.startPrank(BOB);
|
||||
vm.expectRevert();
|
||||
tychoRouter.withdraw(tokensArray, FUND_RESCUER);
|
||||
tychoRouter.withdraw(tokens, FUND_RESCUER);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,131 +2,10 @@
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "./TychoRouterTestSetup.sol";
|
||||
import "./executors/UniswapV4Utils.sol";
|
||||
import "./protocols/UniswapV4Utils.sol";
|
||||
import "@src/executors/BebopExecutor.sol";
|
||||
|
||||
contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
||||
function testSingleSwapUSV4CallbackPermit2() public {
|
||||
vm.startPrank(ALICE);
|
||||
uint256 amountIn = 100 ether;
|
||||
deal(USDE_ADDR, ALICE, amountIn);
|
||||
(
|
||||
IAllowanceTransfer.PermitSingle memory permitSingle,
|
||||
bytes memory signature
|
||||
) = handlePermit2Approval(USDE_ADDR, tychoRouterAddr, amountIn);
|
||||
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: uint24(100),
|
||||
tickSpacing: int24(1)
|
||||
});
|
||||
|
||||
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
USDT_ADDR,
|
||||
true,
|
||||
RestrictTransferFrom.TransferType.TransferFrom,
|
||||
ALICE,
|
||||
pools
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv4Executor), protocolData);
|
||||
|
||||
tychoRouter.singleSwapPermit2(
|
||||
amountIn,
|
||||
USDE_ADDR,
|
||||
USDT_ADDR,
|
||||
99943850,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
permitSingle,
|
||||
signature,
|
||||
swap
|
||||
);
|
||||
|
||||
assertEq(IERC20(USDT_ADDR).balanceOf(ALICE), 99963618);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSplitSwapMultipleUSV4Callback() public {
|
||||
// This test has two uniswap v4 hops that will be executed inside of the V4 pool manager
|
||||
// USDE -> USDT -> WBTC
|
||||
uint256 amountIn = 100 ether;
|
||||
deal(USDE_ADDR, ALICE, amountIn);
|
||||
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: uint24(100),
|
||||
tickSpacing: int24(1)
|
||||
});
|
||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: WBTC_ADDR,
|
||||
fee: uint24(3000),
|
||||
tickSpacing: int24(60)
|
||||
});
|
||||
|
||||
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
WBTC_ADDR,
|
||||
true,
|
||||
RestrictTransferFrom.TransferType.TransferFrom,
|
||||
ALICE,
|
||||
pools
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv4Executor), protocolData);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(USDE_ADDR).approve(tychoRouterAddr, amountIn);
|
||||
tychoRouter.singleSwap(
|
||||
amountIn,
|
||||
USDE_ADDR,
|
||||
WBTC_ADDR,
|
||||
118280,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
true,
|
||||
swap
|
||||
);
|
||||
|
||||
assertEq(IERC20(WBTC_ADDR).balanceOf(ALICE), 118281);
|
||||
}
|
||||
|
||||
function testSingleUSV4IntegrationGroupedSwap() public {
|
||||
// Test created with calldata from our router encoder.
|
||||
|
||||
// Performs a single swap from USDC to PEPE though ETH using two
|
||||
// consecutive USV4 pools. It's a single swap because it is a consecutive grouped swaps
|
||||
//
|
||||
// USDC ──(USV4)──> ETH ───(USV4)──> PEPE
|
||||
//
|
||||
deal(USDC_ADDR, ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
|
||||
bytes memory callData = loadCallDataFromFile(
|
||||
"test_single_encoding_strategy_usv4_grouped_swap"
|
||||
);
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balanceAfter = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertEq(balanceAfter - balanceBefore, 123172000092711286554274694);
|
||||
}
|
||||
|
||||
function testMultiProtocolIntegration() public {
|
||||
// Test created with calldata from our router encoder.
|
||||
//
|
||||
@@ -171,130 +50,6 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
||||
assertEq(balanceAfter - balanceBefore, 235610487387677804636755778);
|
||||
}
|
||||
|
||||
function testSingleUSV4IntegrationOutputETH() public {
|
||||
// Test created with calldata from our router encoder.
|
||||
|
||||
// Performs a single swap from USDC to ETH without wrapping or unwrapping
|
||||
//
|
||||
// USDC ───(USV4)──> ETH
|
||||
//
|
||||
deal(USDC_ADDR, ALICE, 3000_000000);
|
||||
uint256 balanceBefore = ALICE.balance;
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
|
||||
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_usv4_eth_out");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balanceAfter = ALICE.balance;
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
console.logUint(balanceAfter - balanceBefore);
|
||||
assertEq(balanceAfter - balanceBefore, 1474406268748155809);
|
||||
}
|
||||
|
||||
function testSingleMaverickIntegration() public {
|
||||
deal(GHO_ADDR, ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(GHO_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
||||
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_maverick");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGe(balanceAfter - balanceBefore, 999725);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||
}
|
||||
|
||||
function testSingleEkuboIntegration() public {
|
||||
vm.stopPrank();
|
||||
|
||||
deal(ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_ekubo");
|
||||
(bool success,) = tychoRouterAddr.call{value: 1 ether}(callData);
|
||||
|
||||
uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGe(balanceAfter - balanceBefore, 26173932);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||
}
|
||||
|
||||
function testSingleCurveIntegration() public {
|
||||
deal(UWU_ADDR, ALICE, 1 ether);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(UWU_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_curve");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 2877855391767);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSingleSwapUSV3Permit2() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V3 using Permit2
|
||||
// Tests entire USV3 flow including callback
|
||||
// 1 WETH -> DAI
|
||||
// (USV3)
|
||||
vm.startPrank(ALICE);
|
||||
uint256 amountIn = 10 ** 18;
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
(
|
||||
IAllowanceTransfer.PermitSingle memory permitSingle,
|
||||
bytes memory signature
|
||||
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
|
||||
|
||||
uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI
|
||||
bool zeroForOne = false;
|
||||
bytes memory protocolData = encodeUniswapV3Swap(
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
ALICE,
|
||||
DAI_WETH_USV3,
|
||||
zeroForOne,
|
||||
RestrictTransferFrom.TransferType.TransferFrom
|
||||
);
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv3Executor), protocolData);
|
||||
|
||||
tychoRouter.singleSwapPermit2(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
expAmountOut - 1,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
permitSingle,
|
||||
signature,
|
||||
swap
|
||||
);
|
||||
|
||||
uint256 finalBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSingleBebopIntegration() public {
|
||||
// The calldata swaps 200 USDC for ONDO
|
||||
// The receiver in the order is 0xc5564C13A157E6240659fb81882A28091add8670
|
||||
|
||||
@@ -4,8 +4,6 @@ pragma solidity ^0.8.26;
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {TychoRouter} from "@src/TychoRouter.sol";
|
||||
import "./TychoRouterTestSetup.sol";
|
||||
import "./executors/UniswapV4Utils.sol";
|
||||
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||
|
||||
contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
|
||||
function _getSequentialSwaps() internal view returns (bytes[] memory) {
|
||||
|
||||
@@ -4,8 +4,6 @@ pragma solidity ^0.8.26;
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {TychoRouter} from "@src/TychoRouter.sol";
|
||||
import "./TychoRouterTestSetup.sol";
|
||||
import "./executors/UniswapV4Utils.sol";
|
||||
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||
|
||||
contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
|
||||
function testSingleSwapPermit2() public {
|
||||
|
||||
@@ -4,8 +4,6 @@ pragma solidity ^0.8.26;
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {TychoRouter, RestrictTransferFrom} from "@src/TychoRouter.sol";
|
||||
import "./TychoRouterTestSetup.sol";
|
||||
import "./executors/UniswapV4Utils.sol";
|
||||
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||
|
||||
contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
|
||||
function _getSplitSwaps(bool transferFrom)
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "../src/executors/BalancerV2Executor.sol";
|
||||
import "../src/executors/CurveExecutor.sol";
|
||||
import "../src/executors/EkuboExecutor.sol";
|
||||
import "../src/executors/UniswapV2Executor.sol";
|
||||
import "../src/executors/UniswapV3Executor.sol";
|
||||
import "../src/executors/UniswapV4Executor.sol";
|
||||
// Executors
|
||||
import {BalancerV2Executor} from "../src/executors/BalancerV2Executor.sol";
|
||||
import {BalancerV3Executor} from "../src/executors/BalancerV3Executor.sol";
|
||||
import {CurveExecutor} from "../src/executors/CurveExecutor.sol";
|
||||
import {EkuboExecutor} from "../src/executors/EkuboExecutor.sol";
|
||||
import {MaverickV2Executor} from "../src/executors/MaverickV2Executor.sol";
|
||||
import {UniswapV2Executor} from "../src/executors/UniswapV2Executor.sol";
|
||||
import {
|
||||
UniswapV3Executor,
|
||||
IUniswapV3Pool
|
||||
} from "../src/executors/UniswapV3Executor.sol";
|
||||
import {UniswapV4Executor} from "../src/executors/UniswapV4Executor.sol";
|
||||
import {BebopExecutorHarness} from "./executors/BebopExecutor.t.sol";
|
||||
|
||||
// Test utilities and mocks
|
||||
import "./Constants.sol";
|
||||
import "./mock/MockERC20.sol";
|
||||
import "./TestUtils.sol";
|
||||
import {Permit2TestHelper} from "./Permit2TestHelper.sol";
|
||||
|
||||
// Core contracts and interfaces
|
||||
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";
|
||||
import "./TestUtils.sol";
|
||||
import {MaverickV2Executor} from "../src/executors/MaverickV2Executor.sol";
|
||||
import {BalancerV3Executor} from "../src/executors/BalancerV3Executor.sol";
|
||||
import {BebopExecutorHarness} from "./executors/BebopExecutor.t.sol";
|
||||
|
||||
contract TychoRouterExposed is TychoRouter {
|
||||
constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {}
|
||||
@@ -70,7 +75,6 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
MaverickV2Executor public maverickv2Executor;
|
||||
BalancerV3Executor public balancerV3Executor;
|
||||
BebopExecutorHarness public bebopExecutor;
|
||||
MockERC20[] tokens;
|
||||
|
||||
function getForkBlock() public view virtual returns (uint256) {
|
||||
return 22082754;
|
||||
@@ -89,12 +93,6 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
vm.startPrank(EXECUTOR_SETTER);
|
||||
tychoRouter.setExecutors(executors);
|
||||
vm.stopPrank();
|
||||
|
||||
vm.startPrank(BOB);
|
||||
tokens.push(new MockERC20("Token A", "A"));
|
||||
tokens.push(new MockERC20("Token B", "B"));
|
||||
tokens.push(new MockERC20("Token C", "C"));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function deployRouter() public returns (TychoRouterExposed) {
|
||||
@@ -152,18 +150,6 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
return executors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Mints tokens to the given address
|
||||
* @param amount The amount of tokens to mint
|
||||
* @param to The address to mint tokens to
|
||||
*/
|
||||
function mintTokens(uint256 amount, address to) internal {
|
||||
for (uint256 i = 0; i < tokens.length; i++) {
|
||||
// slither-disable-next-line calls-loop
|
||||
tokens[i].mint(to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
function pleEncode(bytes[] memory data)
|
||||
public
|
||||
pure
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
contract MockERC20 is ERC20 {
|
||||
constructor(string memory name_, string memory symbol_)
|
||||
ERC20(name_, symbol_)
|
||||
{}
|
||||
|
||||
function mint(address to, uint256 amount) external {
|
||||
_mint(to, amount);
|
||||
}
|
||||
|
||||
function decimals() public view virtual override returns (uint8) {
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "../TychoRouterTestSetup.sol";
|
||||
import "@src/executors/CurveExecutor.sol";
|
||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||
|
||||
interface ICurvePool {
|
||||
function coins(uint256 i) external view returns (address);
|
||||
@@ -393,3 +394,20 @@ contract CurveExecutorTest is Test, Constants {
|
||||
return (coinInIndex, coinOutIndex);
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
function testSingleCurveIntegration() public {
|
||||
deal(UWU_ADDR, ALICE, 1 ether);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(UWU_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_curve");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 2877855391767);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "../TestUtils.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
import "../TychoRouterTestSetup.sol";
|
||||
import "@src/executors/EkuboExecutor.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
import {ICore} from "@ekubo/interfaces/ICore.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol";
|
||||
@@ -158,3 +159,24 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
multiHopSwap(loadCallDataFromFile("test_ekubo_encode_swap_multi"));
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
function testSingleEkuboIntegration() public {
|
||||
vm.stopPrank();
|
||||
|
||||
deal(ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_ekubo");
|
||||
(bool success,) = tychoRouterAddr.call{value: 1 ether}(callData);
|
||||
|
||||
uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGe(balanceAfter - balanceBefore, 26173932);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "../TestUtils.sol";
|
||||
import "../TychoRouterTestSetup.sol";
|
||||
import "@src/executors/MaverickV2Executor.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
import "../TestUtils.sol";
|
||||
|
||||
contract MaverickV2ExecutorExposed is MaverickV2Executor {
|
||||
constructor(address _factory, address _permit2)
|
||||
@@ -126,3 +127,23 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
|
||||
assertEq(balanceAfter - balanceBefore, amountOut);
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
function testSingleMaverickIntegration() public {
|
||||
deal(GHO_ADDR, ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(GHO_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
||||
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_maverick");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGe(balanceAfter - balanceBefore, 999725);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "@src/executors/UniswapV3Executor.sol";
|
||||
import "../TychoRouterTestSetup.sol";
|
||||
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||
import "@src/executors/UniswapV3Executor.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
import {Permit2TestHelper} from "../Permit2TestHelper.sol";
|
||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||
|
||||
contract UniswapV3ExecutorExposed is UniswapV3Executor {
|
||||
constructor(address _factory, bytes32 _initCode, address _permit2)
|
||||
@@ -43,7 +44,6 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
|
||||
|
||||
UniswapV3ExecutorExposed uniswapV3Exposed;
|
||||
UniswapV3ExecutorExposed pancakeV3Exposed;
|
||||
IERC20 WETH = IERC20(WETH_ADDR);
|
||||
IERC20 DAI = IERC20(DAI_ADDR);
|
||||
IAllowanceTransfer permit2;
|
||||
|
||||
@@ -211,3 +211,50 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
function testSingleSwapUSV3Permit2() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V3 using Permit2
|
||||
// Tests entire USV3 flow including callback
|
||||
// 1 WETH -> DAI
|
||||
// (USV3)
|
||||
vm.startPrank(ALICE);
|
||||
uint256 amountIn = 10 ** 18;
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
(
|
||||
IAllowanceTransfer.PermitSingle memory permitSingle,
|
||||
bytes memory signature
|
||||
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
|
||||
|
||||
uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI
|
||||
bool zeroForOne = false;
|
||||
bytes memory protocolData = encodeUniswapV3Swap(
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
ALICE,
|
||||
DAI_WETH_USV3,
|
||||
zeroForOne,
|
||||
RestrictTransferFrom.TransferType.TransferFrom
|
||||
);
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv3Executor), protocolData);
|
||||
|
||||
tychoRouter.singleSwapPermit2(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
expAmountOut - 1,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
permitSingle,
|
||||
signature,
|
||||
swap
|
||||
);
|
||||
|
||||
uint256 finalBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ pragma solidity ^0.8.26;
|
||||
|
||||
import "../../src/executors/UniswapV4Executor.sol";
|
||||
import "../TestUtils.sol";
|
||||
import "../TychoRouterTestSetup.sol";
|
||||
import "./UniswapV4Utils.sol";
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
@@ -211,3 +212,175 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
assertTrue(IERC20(WBTC_ADDR).balanceOf(ALICE) == amountOut);
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
function testSingleSwapUSV4CallbackPermit2() public {
|
||||
vm.startPrank(ALICE);
|
||||
uint256 amountIn = 100 ether;
|
||||
deal(USDE_ADDR, ALICE, amountIn);
|
||||
(
|
||||
IAllowanceTransfer.PermitSingle memory permitSingle,
|
||||
bytes memory signature
|
||||
) = handlePermit2Approval(USDE_ADDR, tychoRouterAddr, amountIn);
|
||||
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: uint24(100),
|
||||
tickSpacing: int24(1)
|
||||
});
|
||||
|
||||
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
USDT_ADDR,
|
||||
true,
|
||||
RestrictTransferFrom.TransferType.TransferFrom,
|
||||
ALICE,
|
||||
pools
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv4Executor), protocolData);
|
||||
|
||||
tychoRouter.singleSwapPermit2(
|
||||
amountIn,
|
||||
USDE_ADDR,
|
||||
USDT_ADDR,
|
||||
99943850,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
permitSingle,
|
||||
signature,
|
||||
swap
|
||||
);
|
||||
|
||||
assertEq(IERC20(USDT_ADDR).balanceOf(ALICE), 99963618);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSplitSwapMultipleUSV4Callback() public {
|
||||
// This test has two uniswap v4 hops that will be executed inside of the V4 pool manager
|
||||
// USDE -> USDT -> WBTC
|
||||
uint256 amountIn = 100 ether;
|
||||
deal(USDE_ADDR, ALICE, amountIn);
|
||||
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: uint24(100),
|
||||
tickSpacing: int24(1)
|
||||
});
|
||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: WBTC_ADDR,
|
||||
fee: uint24(3000),
|
||||
tickSpacing: int24(60)
|
||||
});
|
||||
|
||||
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
WBTC_ADDR,
|
||||
true,
|
||||
RestrictTransferFrom.TransferType.TransferFrom,
|
||||
ALICE,
|
||||
pools
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv4Executor), protocolData);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(USDE_ADDR).approve(tychoRouterAddr, amountIn);
|
||||
tychoRouter.singleSwap(
|
||||
amountIn,
|
||||
USDE_ADDR,
|
||||
WBTC_ADDR,
|
||||
118280,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
true,
|
||||
swap
|
||||
);
|
||||
|
||||
assertEq(IERC20(WBTC_ADDR).balanceOf(ALICE), 118281);
|
||||
}
|
||||
|
||||
function testSingleUSV4IntegrationGroupedSwap() public {
|
||||
// Test created with calldata from our router encoder.
|
||||
|
||||
// Performs a single swap from USDC to PEPE though ETH using two
|
||||
// consecutive USV4 pools. It's a single swap because it is a consecutive grouped swaps
|
||||
//
|
||||
// USDC ──(USV4)──> ETH ───(USV4)──> PEPE
|
||||
//
|
||||
deal(USDC_ADDR, ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
|
||||
bytes memory callData = loadCallDataFromFile(
|
||||
"test_single_encoding_strategy_usv4_grouped_swap"
|
||||
);
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balanceAfter = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertEq(balanceAfter - balanceBefore, 123172000092711286554274694);
|
||||
}
|
||||
|
||||
function testSingleUSV4IntegrationInputETH() public {
|
||||
// Test created with calldata from our router encoder.
|
||||
|
||||
// Performs a single swap from ETH to PEPE without wrapping or unwrapping
|
||||
//
|
||||
// ETH ───(USV4)──> PEPE
|
||||
//
|
||||
deal(ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_usv4_eth_in");
|
||||
(bool success,) = tychoRouterAddr.call{value: 1 ether}(callData);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balanceAfter = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertEq(balanceAfter - balanceBefore, 235610487387677804636755778);
|
||||
}
|
||||
|
||||
function testSingleUSV4IntegrationOutputETH() public {
|
||||
// Test created with calldata from our router encoder.
|
||||
|
||||
// Performs a single swap from USDC to ETH without wrapping or unwrapping
|
||||
//
|
||||
// USDC ───(USV4)──> ETH
|
||||
//
|
||||
deal(USDC_ADDR, ALICE, 3000_000000);
|
||||
uint256 balanceBefore = ALICE.balance;
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max);
|
||||
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_usv4_eth_out");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balanceAfter = ALICE.balance;
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
console.logUint(balanceAfter - balanceBefore);
|
||||
assertEq(balanceAfter - balanceBefore, 1474406268748155809);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user