From a75dfd02abb2fde70805f5bff8388f3f8bd164a6 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Sun, 16 Mar 2025 00:18:37 -0400 Subject: [PATCH 01/79] docs: Fix swap without permit2 doc - We changed this to use a transferFrom --- foundry/src/TychoRouter.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 4b01e5b..47d6d14 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -111,8 +111,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { /** * @notice Executes a swap operation based on a predefined swap graph, supporting internal token amount splits. * This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount - * against a user-specified minimum. This function expects the input tokens to already be in the router at - * the time of calling. + * against a user-specified minimum. This function performs a transferFrom to retrieve tokens from the caller. * * @dev * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. From d644b63851a63babadfb909af97c5bf80dd03376 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 18 Mar 2025 21:28:48 -0400 Subject: [PATCH 02/79] fix: Slippage precision calculation - The resolution wasn't high enough before, so 0.1% slippage would round to 0% slippage. We should instead use basis point resolution to overcome this. --- src/encoding/evm/utils.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/encoding/evm/utils.rs b/src/encoding/evm/utils.rs index 96e04ea..f284ee4 100644 --- a/src/encoding/evm/utils.rs +++ b/src/encoding/evm/utils.rs @@ -74,10 +74,10 @@ pub fn get_min_amount_for_solution(solution: Solution) -> BigUint { if let (Some(expected_amount), Some(slippage)) = (solution.expected_amount.as_ref(), solution.slippage) { - let one_hundred = BigUint::from(100u32); - let slippage_percent = BigUint::from((slippage * 100.0) as u32); - let multiplier = &one_hundred - slippage_percent; - let expected_amount_with_slippage = (expected_amount * &multiplier) / &one_hundred; + let bps = BigUint::from(10_000u32); + let slippage_percent = BigUint::from((slippage * 10000.0) as u32); + let multiplier = &bps - slippage_percent; + let expected_amount_with_slippage = (expected_amount * &multiplier) / &bps; min_amount_out = max(min_amount_out, expected_amount_with_slippage); } min_amount_out @@ -133,3 +133,27 @@ pub fn get_runtime() -> Result<(Handle, Option>), EncodingError> { } } } +#[cfg(test)] +mod tests { + use num_bigint::BigUint; + + use super::*; + use crate::encoding::models::Solution; + + #[test] + fn test_min_amount_out_small_slippage() { + // Tests that the calculation's precision is high enough to support a slippage of 0.1%. + + let solution = Solution { + exact_out: false, + given_amount: BigUint::from(1000000000000000000u64), + checked_amount: None, + slippage: Some(0.001f64), + expected_amount: Some(BigUint::from(1000000000000000000u64)), + ..Default::default() + }; + + let min_amount_out = get_min_amount_for_solution(solution); + assert_eq!(min_amount_out, BigUint::from(999000000000000000u64)); + } +} From 5a6433caf1e968ed972bd510c0b87698b65bce14 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 19 Mar 2025 14:15:07 +0000 Subject: [PATCH 03/79] chore(release): 0.66.1 [skip ci] ## [0.66.1](https://github.com/propeller-heads/tycho-execution/compare/0.66.0...0.66.1) (2025-03-19) ### Bug Fixes * Slippage precision calculation ([d644b63](https://github.com/propeller-heads/tycho-execution/commit/d644b63851a63babadfb909af97c5bf80dd03376)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3258cde..b46ceb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.66.1](https://github.com/propeller-heads/tycho-execution/compare/0.66.0...0.66.1) (2025-03-19) + + +### Bug Fixes + +* Slippage precision calculation ([d644b63](https://github.com/propeller-heads/tycho-execution/commit/d644b63851a63babadfb909af97c5bf80dd03376)) + ## [0.66.0](https://github.com/propeller-heads/tycho-execution/compare/0.65.1...0.66.0) (2025-03-14) diff --git a/Cargo.lock b/Cargo.lock index 6bf10b6..a326a08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.66.0" +version = "0.66.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 6d58787..cc569cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.66.0" +version = "0.66.1" edition = "2021" [[bin]] From 0a8a34be035588d45e6b72a42f4dd691e3c98d2f Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 19 Mar 2025 14:27:34 -0400 Subject: [PATCH 04/79] feat: Support sushiswap v2 and pancakeswap v2 on ethereum - Needed to take pool code init hash as input param for executors - Added tests for ethereum. Will test base on-chain. --- foundry/scripts/deploy-executors.js | 33 +++++++++++++++++-- foundry/src/executors/UniswapV2Executor.sol | 15 +++++---- foundry/test/Constants.sol | 24 +++++++++++++- foundry/test/TychoRouterTestSetup.sol | 3 +- .../test/executors/UniswapV2Executor.t.sol | 24 ++++++++++++-- 5 files changed, 86 insertions(+), 13 deletions(-) diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index 00b28b7..a7ac3d6 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -5,13 +5,42 @@ const hre = require("hardhat"); // Comment out the executors you don't want to deploy const executors_to_deploy = { "ethereum":[ - {exchange: "UniswapV2Executor", args: ["0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"]}, + // Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ + "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", + "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" + ]}, + // Factory, Pool Init Code Hash + {exchange: "SushiSwapV2Executor", args: [ + "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", + "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" + ]}, + // Factory, Pool Init Code Hash + {exchange: "PancakeSwapV2Executor", args: [ + "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362", + "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" + ]}, {exchange: "UniswapV3Executor", args: ["0x1F98431c8aD98523631AE4a59f267346ea31F984"]}, {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "BalancerV2Executor", args: []}, ], "base":[ - {exchange: "UniswapV2Executor", args: ["0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6"]}, + // Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ + "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6", + "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" + ]}, + // Factory, Pool Init Code Hash + {exchange: "SushiSwapV2Executor", args: [ + "0x71524B4f93c58fcbF659783284E38825f0622859", + "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" + ]}, + // Factory, Pool Init Code Hash + {exchange: "PancakeSwapV2Executor", args: [ + "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", + "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" + ]}, + // Factory, Pool Init Code Hash {exchange: "UniswapV3Executor", args: ["0x33128a8fC17869897dcE68Ed026d694621f6FDfD"]}, {exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]}, {exchange: "BalancerV2Executor", args: []}, diff --git a/foundry/src/executors/UniswapV2Executor.sol b/foundry/src/executors/UniswapV2Executor.sol index 6707ce8..51a33e6 100644 --- a/foundry/src/executors/UniswapV2Executor.sol +++ b/foundry/src/executors/UniswapV2Executor.sol @@ -8,21 +8,24 @@ import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol"; error UniswapV2Executor__InvalidDataLength(); error UniswapV2Executor__InvalidTarget(); error UniswapV2Executor__InvalidFactory(); +error UniswapV2Executor__InvalidInitCode(); contract UniswapV2Executor is IExecutor { using SafeERC20 for IERC20; - bytes32 internal constant POOL_INIT_CODE_HASH = - 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f; - address public immutable factory; + bytes32 public immutable init_code; address private immutable self; - constructor(address _factory) { + constructor(address _factory, bytes32 _init_code) { if (_factory == address(0)) { revert UniswapV2Executor__InvalidFactory(); } + if (_init_code == bytes32(0)) { + revert UniswapV2Executor__InvalidInitCode(); + } factory = _factory; + init_code = _init_code; self = address(this); } @@ -102,9 +105,7 @@ contract UniswapV2Executor is IExecutor { uint160( uint256( keccak256( - abi.encodePacked( - hex"ff", factory, salt, POOL_INIT_CODE_HASH - ) + abi.encodePacked(hex"ff", factory, salt, init_code) ) ) ) diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index 9149dcf..d366db9 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -47,9 +47,23 @@ contract Constants is Test, BaseConstants { address USDC_WBTC_POOL = 0x004375Dff511095CC5A197A54140a24eFEF3A416; address USDC_WETH_USV2 = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc; - // Uniswap v3 + // Sushiswap v2 + address SUSHISWAP_WBTC_WETH_POOL = + 0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58; + + // Pancakeswap v2 + address PANCAKESWAP_WBTC_WETH_POOL = + 0x4AB6702B3Ed3877e9b1f203f90cbEF13d663B0e8; + + // Factories address USV3_FACTORY_ETHEREUM = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address USV2_FACTORY_ETHEREUM = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; + address SUSHISWAPV2_FACTORY_ETHEREUM = + 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac; + address PANCAKESWAPV2_FACTORY_ETHEREUM = + 0x1097053Fd2ea711dad45caCcc45EfF7548fCB362; + + // Uniswap v3 address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; address USDC_WETH_USV3 = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640; // 0.05% fee address USDC_WETH_USV3_2 = 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8; // 0.3% fee @@ -60,6 +74,14 @@ contract Constants is Test, BaseConstants { // Permit2 address PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; + // Pool Code Init Hashes + bytes32 USV2_POOL_CODE_INIT_HASH = + 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f; + bytes32 SUSHIV2_POOL_CODE_INIT_HASH = + 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303; + bytes32 PANCAKEV2_POOL_CODE_INIT_HASH = + 0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d; + /** * @dev Deploys a dummy contract with non-empty bytecode */ diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 634f814..dd4bc57 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -46,6 +46,7 @@ contract TychoRouterTestSetup is Test, Constants { vm.startPrank(ADMIN); address factoryV3 = USV3_FACTORY_ETHEREUM; address factoryV2 = USV2_FACTORY_ETHEREUM; + bytes32 initCodeV2 = USV2_POOL_CODE_INIT_HASH; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; IPoolManager poolManager = IPoolManager(poolManagerAddress); tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); @@ -60,7 +61,7 @@ contract TychoRouterTestSetup is Test, Constants { deployDummyContract(); vm.stopPrank(); - usv2Executor = new UniswapV2Executor(factoryV2); + usv2Executor = new UniswapV2Executor(factoryV2, initCodeV2); usv3Executor = new UniswapV3Executor(factoryV3); usv4Executor = new UniswapV4Executor(poolManager); vm.startPrank(EXECUTOR_SETTER); diff --git a/foundry/test/executors/UniswapV2Executor.t.sol b/foundry/test/executors/UniswapV2Executor.t.sol index f9b15a4..99110ef 100644 --- a/foundry/test/executors/UniswapV2Executor.t.sol +++ b/foundry/test/executors/UniswapV2Executor.t.sol @@ -6,7 +6,9 @@ import {Test} from "../../lib/forge-std/src/Test.sol"; import {Constants} from "../Constants.sol"; contract UniswapV2ExecutorExposed is UniswapV2Executor { - constructor(address _factory) UniswapV2Executor(_factory) {} + constructor(address _factory, bytes32 _init_code) + UniswapV2Executor(_factory, _init_code) + {} function decodeParams(bytes calldata data) external @@ -48,13 +50,23 @@ contract UniswapV2ExecutorTest is Test, Constants { using SafeERC20 for IERC20; UniswapV2ExecutorExposed uniswapV2Exposed; + UniswapV2ExecutorExposed sushiswapV2Exposed; + UniswapV2ExecutorExposed pancakeswapV2Exposed; IERC20 WETH = IERC20(WETH_ADDR); IERC20 DAI = IERC20(DAI_ADDR); function setUp() public { uint256 forkBlock = 17323404; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - uniswapV2Exposed = new UniswapV2ExecutorExposed(USV2_FACTORY_ETHEREUM); + uniswapV2Exposed = new UniswapV2ExecutorExposed( + USV2_FACTORY_ETHEREUM, USV2_POOL_CODE_INIT_HASH + ); + sushiswapV2Exposed = new UniswapV2ExecutorExposed( + SUSHISWAPV2_FACTORY_ETHEREUM, SUSHIV2_POOL_CODE_INIT_HASH + ); + pancakeswapV2Exposed = new UniswapV2ExecutorExposed( + PANCAKESWAPV2_FACTORY_ETHEREUM, PANCAKEV2_POOL_CODE_INIT_HASH + ); } function testDecodeParams() public view { @@ -82,6 +94,14 @@ contract UniswapV2ExecutorTest is Test, Constants { uniswapV2Exposed.verifyPairAddress(WETH_DAI_POOL); } + function testVerifyPairAddressSushi() public view { + sushiswapV2Exposed.verifyPairAddress(SUSHISWAP_WBTC_WETH_POOL); + } + + function testVerifyPairAddressPancake() public view { + pancakeswapV2Exposed.verifyPairAddress(PANCAKESWAP_WBTC_WETH_POOL); + } + function testInvalidTarget() public { address fakePool = address(new FakeUniswapV2Pool(WETH_ADDR, DAI_ADDR)); vm.expectRevert(UniswapV2Executor__InvalidTarget.selector); From 3c982c5824602c3d2ddcd1990b4da380a723520b Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:58:40 +0100 Subject: [PATCH 05/79] Ekubo integration --- .gitmodules | 3 + foundry/lib/ekubo/interfaces/ICore.sol | 16 ++ .../lib/ekubo/interfaces/IFlashAccountant.sol | 16 ++ foundry/lib/ekubo/math/constants.sol | 5 + foundry/lib/ekubo/types/poolKey.sol | 12 + foundry/lib/ekubo/types/sqrtRatio.sol | 9 + foundry/lib/solady | 1 + foundry/remappings.txt | 4 +- foundry/src/executors/EkuboExecutor.sol | 210 ++++++++++++++++++ foundry/test/executors/EkuboExecutor.t.sol | 121 ++++++++++ src/encoding/evm/constants.rs | 1 + .../evm/swap_encoder/swap_encoders.rs | 60 ++++- 12 files changed, 456 insertions(+), 2 deletions(-) create mode 100644 foundry/lib/ekubo/interfaces/ICore.sol create mode 100644 foundry/lib/ekubo/interfaces/IFlashAccountant.sol create mode 100644 foundry/lib/ekubo/math/constants.sol create mode 100644 foundry/lib/ekubo/types/poolKey.sol create mode 100644 foundry/lib/ekubo/types/sqrtRatio.sol create mode 160000 foundry/lib/solady create mode 100644 foundry/src/executors/EkuboExecutor.sol create mode 100644 foundry/test/executors/EkuboExecutor.t.sol diff --git a/.gitmodules b/.gitmodules index eacb977..d9919b8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "foundry/lib/v4-periphery"] path = foundry/lib/v4-periphery url = https://github.com/Uniswap/v4-periphery +[submodule "foundry/lib/solady"] + path = foundry/lib/solady + url = https://github.com/vectorized/solady diff --git a/foundry/lib/ekubo/interfaces/ICore.sol b/foundry/lib/ekubo/interfaces/ICore.sol new file mode 100644 index 0000000..2bc6b3d --- /dev/null +++ b/foundry/lib/ekubo/interfaces/ICore.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import {IFlashAccountant} from "./IFlashAccountant.sol"; +import {PoolKey} from "../types/poolKey.sol"; +import {SqrtRatio} from "../types/sqrtRatio.sol"; + +interface ICore is IFlashAccountant { + function swap_611415377( + PoolKey memory poolKey, + int128 amount, + bool isToken1, + SqrtRatio sqrtRatioLimit, + uint256 skipAhead + ) external payable returns (int128 delta0, int128 delta1); +} \ No newline at end of file diff --git a/foundry/lib/ekubo/interfaces/IFlashAccountant.sol b/foundry/lib/ekubo/interfaces/IFlashAccountant.sol new file mode 100644 index 0000000..2524078 --- /dev/null +++ b/foundry/lib/ekubo/interfaces/IFlashAccountant.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +interface ILocker { + function locked(uint256 id) external; +} + +interface IPayer { + function payCallback(uint256 id, address token) external; +} + +interface IFlashAccountant { + // Withdraws a token amount from the accountant to the given recipient. + // The contract must be locked, as it tracks the withdrawn amount against the current locker's delta. + function withdraw(address token, address recipient, uint128 amount) external; +} diff --git a/foundry/lib/ekubo/math/constants.sol b/foundry/lib/ekubo/math/constants.sol new file mode 100644 index 0000000..cd08e3b --- /dev/null +++ b/foundry/lib/ekubo/math/constants.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +// We use this address to represent the native token within the protocol +address constant NATIVE_TOKEN_ADDRESS = address(0); diff --git a/foundry/lib/ekubo/types/poolKey.sol b/foundry/lib/ekubo/types/poolKey.sol new file mode 100644 index 0000000..b0301e2 --- /dev/null +++ b/foundry/lib/ekubo/types/poolKey.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +// address (20 bytes) | fee (8 bytes) | tickSpacing (4 bytes) +type Config is bytes32; + +// Each pool has its own state associated with this key +struct PoolKey { + address token0; + address token1; + Config config; +} diff --git a/foundry/lib/ekubo/types/sqrtRatio.sol b/foundry/lib/ekubo/types/sqrtRatio.sol new file mode 100644 index 0000000..27e5856 --- /dev/null +++ b/foundry/lib/ekubo/types/sqrtRatio.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +type SqrtRatio is uint96; + +uint96 constant MIN_SQRT_RATIO_RAW = 4611797791050542631; +SqrtRatio constant MIN_SQRT_RATIO = SqrtRatio.wrap(MIN_SQRT_RATIO_RAW); +uint96 constant MAX_SQRT_RATIO_RAW = 79227682466138141934206691491; +SqrtRatio constant MAX_SQRT_RATIO = SqrtRatio.wrap(MAX_SQRT_RATIO_RAW); diff --git a/foundry/lib/solady b/foundry/lib/solady new file mode 160000 index 0000000..c9e079c --- /dev/null +++ b/foundry/lib/solady @@ -0,0 +1 @@ +Subproject commit c9e079c0ca836dcc52777a1fa7227ef28e3537b3 diff --git a/foundry/remappings.txt b/foundry/remappings.txt index 04240ad..d10f28f 100644 --- a/foundry/remappings.txt +++ b/foundry/remappings.txt @@ -7,4 +7,6 @@ @uniswap/v3-updated/=lib/v3-updated/ @uniswap/v3-core/=lib/v3-core/ @uniswap/v4-core/=lib/v4-core/ -@uniswap/v4-periphery/=lib/v4-periphery/ \ No newline at end of file +@uniswap/v4-periphery/=lib/v4-periphery/ +@solady=lib/solady/src/ +@ekubo=lib/ekubo/ diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol new file mode 100644 index 0000000..b934f8c --- /dev/null +++ b/foundry/src/executors/EkuboExecutor.sol @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IExecutor} from "@interfaces/IExecutor.sol"; +import {ICallback} from "@interfaces/ICallback.sol"; +import {ICore} from "@ekubo/interfaces/ICore.sol"; +import {ILocker, IPayer} from "@ekubo/interfaces/IFlashAccountant.sol"; +import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol"; +import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol"; +import {LibBytes} from "@solady/utils/LibBytes.sol"; +import {Config, PoolKey} from "@ekubo/types/poolKey.sol"; +import {MAX_SQRT_RATIO, MIN_SQRT_RATIO} from "@ekubo/types/sqrtRatio.sol"; + +contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { + error EkuboExecutor__InvalidDataLength(); + error EkuboExecutor__CoreOnly(); + error EkuboExecutor__UnknownCallback(); + + ICore immutable core; + + uint256 constant POOL_DATA_OFFSET = 56; + uint256 constant HOP_BYTE_LEN = 52; + + constructor(ICore _core) { + core = _core; + } + + function swap(uint256 amountIn, bytes calldata data) + external + payable + returns (uint256 calculatedAmount) + { + if (data.length < 92) revert EkuboExecutor__InvalidDataLength(); + + uint256 tokenOutOffset = data.length - HOP_BYTE_LEN; + address tokenOut = address(bytes20(LibBytes.loadCalldata(data, tokenOutOffset))); + + uint256 tokenOutBalanceBefore = _balanceOf(tokenOut); + + // amountIn must be at most type(int128).MAX + _lock(bytes.concat(bytes16(uint128(amountIn)), data)); + + uint256 tokenOutBalanceAfter = _balanceOf(tokenOut); + + // It would be better if we could somehow pass back the swapped amount from the lock but the interface doesn't offer that capability. + // Note that the current approach also prevents arbs that return less than their input because of arithmetic underflow. + calculatedAmount = tokenOutBalanceAfter - tokenOutBalanceBefore; + } + + // We can't use the return value here since it won't get propagated (see Dispatcher.sol:_handleCallback) + function handleCallback(bytes calldata raw) + external + returns (bytes memory) + { + verifyCallback(raw); + + // Without selector and locker id + bytes calldata stripped = raw[36:]; + + bytes4 selector = bytes4(raw[:4]); + + if (selector == 0xb45a3c0e) { // Selector of locked(uint256) + _locked(stripped); + } else if (selector == 0x599d0714) { // Selector of payCallback(uint256,address) + _payCallback(stripped); + } else { + revert EkuboExecutor__UnknownCallback(); + } + + return ""; + } + + function verifyCallback(bytes calldata) public view coreOnly {} + + function locked(uint256) external coreOnly { + // Without selector and locker id + _locked(msg.data[36:]); + } + + function payCallback(uint256, address token) external coreOnly { + uint128 amount = uint128(bytes16(LibBytes.loadCalldata(msg.data, 68))); + + SafeTransferLib.safeTransfer(token, address(core), amount); + } + + function _balanceOf(address token) internal view returns (uint256 balance) { + balance = token == NATIVE_TOKEN_ADDRESS + ? address(this).balance + : IERC20(token).balanceOf(address(this)); + } + + function _lock(bytes memory data) internal { + address target = address(core); + + assembly ("memory-safe") { + let args := mload(0x40) + + // Selector of lock() + mstore(args, shl(224, 0xf83d08ba)) + + // We only copy the data, not the length, because the length is read from the calldata size + let len := mload(data) + mcopy(add(args, 4), add(data, 32), len) + + // If the call failed, pass through the revert + if iszero(call(gas(), target, 0, args, add(len, 36), 0, 0)) { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + } + } + + function _locked(bytes calldata swapData) internal { + // For partial swaps this is not equivalent to the given input amount + uint128 tokenInDebtAmount; + + int128 nextAmountIn = int128(uint128(bytes16(swapData[0:16]))); + + address receiver = address(bytes20(swapData[16:36])); + address tokenIn = address(bytes20(swapData[36:POOL_DATA_OFFSET])); + + address nextTokenIn = tokenIn; + + uint256 hopsLength = (swapData.length - POOL_DATA_OFFSET) / HOP_BYTE_LEN; + + uint256 offset = POOL_DATA_OFFSET; + + for (uint256 i = 0; i < hopsLength; i++) { + address nextTokenOut = address(bytes20(LibBytes.loadCalldata(swapData, offset))); + Config poolConfig = Config.wrap(LibBytes.loadCalldata(swapData, offset + 20)); + + ( + address token0, + address token1, + bool isToken1 + ) = nextTokenIn > nextTokenOut ? ( + nextTokenOut, + nextTokenIn, + true + ) : ( + nextTokenIn, + nextTokenOut, + false + ); + + (int128 delta0, int128 delta1) = core.swap_611415377( + PoolKey(token0, token1, poolConfig), + nextAmountIn, + isToken1, + isToken1 ? MAX_SQRT_RATIO : MIN_SQRT_RATIO, + 0 + ); + + if (tokenInDebtAmount == 0) { + tokenInDebtAmount = uint128(isToken1 ? delta1 : delta0); + } + + nextTokenIn = nextTokenOut; + nextAmountIn = -(isToken1 ? delta0 : delta1); + + offset += HOP_BYTE_LEN; + } + + _pay(tokenIn, tokenInDebtAmount); + + core.withdraw( + nextTokenIn, + receiver, + uint128(nextAmountIn) + ); + } + + function _pay(address token, uint128 amount) internal { + address target = address(core); + + if (token == NATIVE_TOKEN_ADDRESS) { + SafeTransferLib.safeTransferETH(target, amount); + } else { + assembly ("memory-safe") { + let free := mload(0x40) + // selector of pay(address) + mstore(free, shl(224, 0x0c11dedd)) + mstore(add(free, 4), token) + mstore(add(free, 36), shl(128, amount)) + + // if it failed, pass through revert + if iszero(call(gas(), target, 0, free, 52, 0, 0)) { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + } + } + } + + function _payCallback(bytes calldata payData) internal { + address token = address(bytes20(payData[0:20])); + uint128 amount = uint128(bytes16(payData[20:36])); + + SafeTransferLib.safeTransfer(address(core), token, amount); + } + + // To receive withdrawals from Core + receive() external payable {} + + modifier coreOnly() { + if (msg.sender != address(core)) revert EkuboExecutor__CoreOnly(); + _; + } +} diff --git a/foundry/test/executors/EkuboExecutor.t.sol b/foundry/test/executors/EkuboExecutor.t.sol new file mode 100644 index 0000000..c323bdb --- /dev/null +++ b/foundry/test/executors/EkuboExecutor.t.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import {EkuboExecutor} from "@src/executors/EkuboExecutor.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Constants} from "../Constants.sol"; +import {Test, console} from "forge-std/Test.sol"; +import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol"; +import {ICore} from "@ekubo/interfaces/ICore.sol"; + +contract EkuboExecutorTest is Test, Constants { + EkuboExecutor executor; + + IERC20 USDC = IERC20(USDC_ADDR); + IERC20 USDT = IERC20(USDT_ADDR); + + address constant CORE_ADDRESS = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444; + + bytes32 constant ORACLE_CONFIG = 0x51d02a5948496a67827242eabc5725531342527c000000000000000000000000; + + function setUp() public { + uint256 forkBlock = 22082754; + vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); + executor = new EkuboExecutor(ICore(payable(CORE_ADDRESS))); + } + + function testSingleSwapEth() public { + uint256 amountIn = 1 ether; + + deal(address(executor), amountIn); + + uint256 ethBalanceBeforeCore = CORE_ADDRESS.balance; + uint256 ethBalanceBeforeExecutor = address(executor).balance; + + uint256 usdcBalanceBeforeCore = USDC.balanceOf(CORE_ADDRESS); + uint256 usdcBalanceBeforeExecutor = USDC.balanceOf(address(executor)); + + bytes memory data = abi.encodePacked( + address(executor), // receiver + NATIVE_TOKEN_ADDRESS, // tokenIn + USDC_ADDR, // tokenOut + ORACLE_CONFIG // poolConfig + ); + + uint256 gasBefore = gasleft(); + uint256 amountOut = executor.swap(amountIn, data); + console.log(gasBefore - gasleft()); + + console.log(amountOut); + + assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore + amountIn); + assertEq(address(executor).balance, ethBalanceBeforeExecutor - amountIn); + + assertEq(USDC.balanceOf(CORE_ADDRESS), usdcBalanceBeforeCore - amountOut); + assertEq(USDC.balanceOf(address(executor)), usdcBalanceBeforeExecutor + amountOut); + } + + function testSingleSwapERC20() public { + uint256 amountIn = 1_000_000_000; + + deal(USDC_ADDR, address(executor), amountIn); + + uint256 usdcBalanceBeforeCore = USDC.balanceOf(CORE_ADDRESS); + uint256 usdcBalanceBeforeExecutor = USDC.balanceOf(address(executor)); + + uint256 ethBalanceBeforeCore = CORE_ADDRESS.balance; + uint256 ethBalanceBeforeExecutor = address(executor).balance; + + bytes memory data = abi.encodePacked( + address(executor), // receiver + USDC_ADDR, // tokenIn + NATIVE_TOKEN_ADDRESS, // tokenOut + ORACLE_CONFIG // config + ); + + uint256 gasBefore = gasleft(); + uint256 amountOut = executor.swap(amountIn, data); + console.log(gasBefore - gasleft()); + + console.log(amountOut); + + assertEq(USDC.balanceOf(CORE_ADDRESS), usdcBalanceBeforeCore + amountIn); + assertEq(USDC.balanceOf(address(executor)), usdcBalanceBeforeExecutor - amountIn); + + assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore - amountOut); + assertEq(address(executor).balance, ethBalanceBeforeExecutor + amountOut); + } + + function testMultiHopSwap() public { + uint256 amountIn = 1 ether; + + deal(address(executor), amountIn); + + uint256 ethBalanceBeforeCore = CORE_ADDRESS.balance; + uint256 ethBalanceBeforeExecutor = address(executor).balance; + + uint256 usdtBalanceBeforeCore = USDT.balanceOf(CORE_ADDRESS); + uint256 usdtBalanceBeforeExecutor = USDT.balanceOf(address(executor)); + + bytes memory data = abi.encodePacked( + address(executor), // receiver + NATIVE_TOKEN_ADDRESS, // tokenIn + USDC_ADDR, // tokenOut of 1st swap + ORACLE_CONFIG, // config of 1st swap + USDT_ADDR, // tokenOut of 2nd swap + bytes32(0x00000000000000000000000000000000000000000001a36e2eb1c43200000032) // config of 2nd swap (0.0025% fee & 0.005% base pool) + ); + + uint256 gasBefore = gasleft(); + uint256 amountOut = executor.swap(amountIn, data); + console.log(gasBefore - gasleft()); + + console.log(amountOut); + + assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore + amountIn); + assertEq(address(executor).balance, ethBalanceBeforeExecutor - amountIn); + + assertEq(USDT.balanceOf(CORE_ADDRESS), usdtBalanceBeforeCore - amountOut); + assertEq(USDT.balanceOf(address(executor)), usdtBalanceBeforeExecutor + amountOut); + } +} diff --git a/src/encoding/evm/constants.rs b/src/encoding/evm/constants.rs index 8f93215..d742100 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -11,5 +11,6 @@ pub static GROUPABLE_PROTOCOLS: LazyLock> = LazyLock::new( let mut set = HashSet::new(); set.insert("uniswap_v4"); set.insert("balancer_v3"); + set.insert("ekubo"); set }); diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 473b250..f5c2752 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{ops::Add, str::FromStr}; use alloy_primitives::{Address, Bytes as AlloyBytes}; use alloy_sol_types::SolValue; @@ -258,6 +258,64 @@ impl SwapEncoder for BalancerV2SwapEncoder { } } +/// Encodes a swap on an Ekubo pool through the given executor address. +/// +/// # Fields +/// * `executor_address` - The address of the executor contract that will perform the swap. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EkuboEncoder { + executor_address: String, +} + +impl SwapEncoder for EkuboEncoder { + fn new(executor_address: String) -> Self { + Self { executor_address } + } + + // TODO Exact out + fn encode_swap( + &self, + swap: Swap, + encoding_context: EncodingContext, + ) -> Result, EncodingError> { + let fee = u64::from_be_bytes(get_static_attribute(&swap, "fee")? + .try_into() + .map_err(|_| EncodingError::FatalError("fee should be an u64".to_string()))? + ); + + let tick_spacing = u32::from_be_bytes(get_static_attribute(&swap, "tick_spacing")? + .try_into() + .map_err(|_| EncodingError::FatalError("tick_spacing should be an u32".to_string()))? + ); + + let extension: Address = get_static_attribute(&swap, "fee")? + .as_slice() + .try_into() + .map_err(|_| EncodingError::FatalError("extension should be an address".to_string()))?; + + let mut encoded = vec![]; + + // TODO What if the token_in appears at the start of a route and later on again? + if encoding_context.group_token_in == swap.token_in { + encoded.extend(bytes_to_address(&encoding_context.receiver)?); + encoded.extend(bytes_to_address(&swap.token_in)?); + } + + encoded.extend(bytes_to_address(&swap.token_out)?); + encoded.extend((extension, fee, tick_spacing).abi_encode_packed()); + + Ok(encoded) + } + + fn executor_address(&self) -> &str { + &self.executor_address + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + #[cfg(test)] mod tests { use std::collections::HashMap; From e954bc7e59cfe5400fded1de2e31ebc00aac6b3e Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:26:52 +0100 Subject: [PATCH 06/79] Fix attribute name --- src/encoding/evm/swap_encoder/swap_encoders.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index f5c2752..ae3a3c2 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -1,4 +1,4 @@ -use std::{ops::Add, str::FromStr}; +use std::str::FromStr; use alloy_primitives::{Address, Bytes as AlloyBytes}; use alloy_sol_types::SolValue; @@ -288,7 +288,7 @@ impl SwapEncoder for EkuboEncoder { .map_err(|_| EncodingError::FatalError("tick_spacing should be an u32".to_string()))? ); - let extension: Address = get_static_attribute(&swap, "fee")? + let extension: Address = get_static_attribute(&swap, "extension")? .as_slice() .try_into() .map_err(|_| EncodingError::FatalError("extension should be an address".to_string()))?; From af5742550f926b663dbe519aba674239a976015c Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:27:06 +0100 Subject: [PATCH 07/79] Add encoder tests --- .../evm/swap_encoder/swap_encoders.rs | 137 +++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index ae3a3c2..4fe9dac 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -263,11 +263,11 @@ impl SwapEncoder for BalancerV2SwapEncoder { /// # Fields /// * `executor_address` - The address of the executor contract that will perform the swap. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct EkuboEncoder { +pub struct EkuboSwapEncoder { executor_address: String, } -impl SwapEncoder for EkuboEncoder { +impl SwapEncoder for EkuboSwapEncoder { fn new(executor_address: String) -> Self { Self { executor_address } } @@ -694,4 +694,137 @@ mod tests { )) ); } + + mod ekubo { + use super::*; + + #[test] + fn test_encode_swap_simple() { + let token_in = Bytes::from(Address::ZERO.as_slice()); + let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC + + let static_attributes = HashMap::from([ + ("fee".to_string(), Bytes::from(0_u64)), + ("tick_spacing".to_string(), Bytes::from(0_u32)), + ("extension".to_string(), Bytes::from("0x51d02a5948496a67827242eabc5725531342527c")), // Oracle + ]); + + let component = ProtocolComponent { + static_attributes, + ..Default::default() + }; + + let swap = Swap { + component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let encoding_context = EncodingContext { + receiver: "0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6".into(), // Random address + group_token_in: token_in.clone(), + group_token_out: token_out.clone(), + exact_out: false, + router_address: Bytes::default(), + }; + + let encoder = EkuboSwapEncoder::new(String::default()); + + let encoded_swap = encoder + .encode_swap(swap, encoding_context) + .unwrap(); + + let hex_swap = encode(&encoded_swap); + + assert_eq!( + hex_swap, + String::from(concat!( + // receiver + "ca4f73fe97d0b987a0d12b39bbd562c779bab6f6", + // group token in + "0000000000000000000000000000000000000000", + // token out 1st swap + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + // pool config 1st swap + "51d02a5948496a67827242eabc5725531342527c000000000000000000000000", + )) + ); + } + + #[test] + fn test_encode_swap_multi() { + let group_token_in = Bytes::from(Address::ZERO.as_slice()); + let group_token_out = Bytes::from("0xdAC17F958D2ee523a2206206994597C13D831ec7"); // USDT + let intermediary_token = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC + + let encoder = EkuboSwapEncoder::new(String::default()); + + let encoding_context = EncodingContext { + receiver: "0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6".into(), // Random address + group_token_in: group_token_in.clone(), + group_token_out: group_token_out.clone(), + exact_out: false, + router_address: Bytes::default(), + }; + + let first_swap = Swap { + component: ProtocolComponent { + static_attributes: HashMap::from([ + ("fee".to_string(), Bytes::from(0_u64)), + ("tick_spacing".to_string(), Bytes::from(0_u32)), + ("extension".to_string(), Bytes::from("0x51d02a5948496a67827242eabc5725531342527c")), // Oracle + ]), + ..Default::default() + }, + token_in: group_token_in.clone(), + token_out: intermediary_token.clone(), + split: 0f64, + }; + + let second_swap = Swap { + component: ProtocolComponent { + // 0.0025% fee & 0.005% base pool + static_attributes: HashMap::from([ + ("fee".to_string(), Bytes::from(461168601842738_u64)), + ("tick_spacing".to_string(), Bytes::from(50_u32)), + ("extension".to_string(), Bytes::zero(20)), + ]), + ..Default::default() + }, + token_in: intermediary_token.clone(), + token_out: group_token_out.clone(), + split: 0f64, + }; + + let first_encoded_swap = encoder + .encode_swap(first_swap, encoding_context.clone()) + .unwrap(); + + let second_encoded_swap = encoder + .encode_swap(second_swap, encoding_context) + .unwrap(); + + let combined_hex = format!("{}{}", encode(first_encoded_swap), encode(second_encoded_swap)); + + assert_eq!( + combined_hex, + String::from(concat!( + // receiver + "ca4f73fe97d0b987a0d12b39bbd562c779bab6f6", + // group token in + "0000000000000000000000000000000000000000", + // token out 1st swap + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + // pool config 1st swap + "51d02a5948496a67827242eabc5725531342527c000000000000000000000000", + // token out 2nd swap + "dac17f958d2ee523a2206206994597c13d831ec7", + // pool config 2nd swap + "00000000000000000000000000000000000000000001a36e2eb1c43200000032", + )) + ); + } + } + } From ce7362ef24ee9b26fa0bc4c1df95e3333bde0f49 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:17:20 +0100 Subject: [PATCH 08/79] Error on unsupported options --- src/encoding/evm/swap_encoder/swap_encoders.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 4fe9dac..68ba13a 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use alloy_primitives::{Address, Bytes as AlloyBytes}; use alloy_sol_types::SolValue; +use num_traits::Zero; use tycho_core::Bytes; use crate::encoding::{ @@ -272,12 +273,19 @@ impl SwapEncoder for EkuboSwapEncoder { Self { executor_address } } - // TODO Exact out fn encode_swap( &self, swap: Swap, encoding_context: EncodingContext, ) -> Result, EncodingError> { + if !swap.split.is_zero() { + return Err(EncodingError::InvalidInput("splits not implemented".to_string())); + } + + if encoding_context.exact_out { + return Err(EncodingError::InvalidInput("exact out swaps not implemented".to_string())); + } + let fee = u64::from_be_bytes(get_static_attribute(&swap, "fee")? .try_into() .map_err(|_| EncodingError::FatalError("fee should be an u64".to_string()))? @@ -295,7 +303,6 @@ impl SwapEncoder for EkuboSwapEncoder { let mut encoded = vec![]; - // TODO What if the token_in appears at the start of a route and later on again? if encoding_context.group_token_in == swap.token_in { encoded.extend(bytes_to_address(&encoding_context.receiver)?); encoded.extend(bytes_to_address(&swap.token_in)?); From 9e6af4b99f5ad98f0fa6398f6508ec16a5610757 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:18:15 +0100 Subject: [PATCH 09/79] cargo fmt & clippy --- .../evm/swap_encoder/swap_encoders.rs | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 68ba13a..baccad3 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -286,14 +286,18 @@ impl SwapEncoder for EkuboSwapEncoder { return Err(EncodingError::InvalidInput("exact out swaps not implemented".to_string())); } - let fee = u64::from_be_bytes(get_static_attribute(&swap, "fee")? - .try_into() - .map_err(|_| EncodingError::FatalError("fee should be an u64".to_string()))? + let fee = u64::from_be_bytes( + get_static_attribute(&swap, "fee")? + .try_into() + .map_err(|_| EncodingError::FatalError("fee should be an u64".to_string()))?, ); - let tick_spacing = u32::from_be_bytes(get_static_attribute(&swap, "tick_spacing")? - .try_into() - .map_err(|_| EncodingError::FatalError("tick_spacing should be an u32".to_string()))? + let tick_spacing = u32::from_be_bytes( + get_static_attribute(&swap, "tick_spacing")? + .try_into() + .map_err(|_| { + EncodingError::FatalError("tick_spacing should be an u32".to_string()) + })?, ); let extension: Address = get_static_attribute(&swap, "extension")? @@ -713,13 +717,13 @@ mod tests { let static_attributes = HashMap::from([ ("fee".to_string(), Bytes::from(0_u64)), ("tick_spacing".to_string(), Bytes::from(0_u32)), - ("extension".to_string(), Bytes::from("0x51d02a5948496a67827242eabc5725531342527c")), // Oracle + ( + "extension".to_string(), + Bytes::from("0x51d02a5948496a67827242eabc5725531342527c"), + ), // Oracle ]); - let component = ProtocolComponent { - static_attributes, - ..Default::default() - }; + let component = ProtocolComponent { static_attributes, ..Default::default() }; let swap = Swap { component, @@ -780,7 +784,10 @@ mod tests { static_attributes: HashMap::from([ ("fee".to_string(), Bytes::from(0_u64)), ("tick_spacing".to_string(), Bytes::from(0_u32)), - ("extension".to_string(), Bytes::from("0x51d02a5948496a67827242eabc5725531342527c")), // Oracle + ( + "extension".to_string(), + Bytes::from("0x51d02a5948496a67827242eabc5725531342527c"), + ), // Oracle ]), ..Default::default() }, @@ -812,7 +819,8 @@ mod tests { .encode_swap(second_swap, encoding_context) .unwrap(); - let combined_hex = format!("{}{}", encode(first_encoded_swap), encode(second_encoded_swap)); + let combined_hex = + format!("{}{}", encode(first_encoded_swap), encode(second_encoded_swap)); assert_eq!( combined_hex, @@ -833,5 +841,4 @@ mod tests { ); } } - } From 2a4ee88cad46dfeb068809bdd885e63094020bcd Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 19 Mar 2025 15:30:58 -0400 Subject: [PATCH 10/79] feat: Support Pancakeswap v3 on ethereum - Needed to take pool code init hash as input param for executors - Added tests for ethereum. Will test base on-chain. - Important note: Pancakeswap uses their deployer instead of their factory (this is a different address) for target verification. --- foundry/scripts/deploy-executors.js | 37 ++++++++++++++----- foundry/src/executors/UniswapV2Executor.sol | 10 ++--- foundry/src/executors/UniswapV3Executor.sol | 12 ++++-- foundry/test/Constants.sol | 20 ++++++++-- foundry/test/TychoRouterTestSetup.sol | 5 ++- .../test/executors/UniswapV2Executor.t.sol | 4 +- .../test/executors/UniswapV3Executor.t.sol | 18 ++++++++- 7 files changed, 78 insertions(+), 28 deletions(-) diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index a7ac3d6..0e0501d 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -5,43 +5,62 @@ const hre = require("hardhat"); // Comment out the executors you don't want to deploy const executors_to_deploy = { "ethereum":[ - // Factory, Pool Init Code Hash + // Args: Factory, Pool Init Code Hash {exchange: "UniswapV2Executor", args: [ "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" ]}, - // Factory, Pool Init Code Hash + // Args: Factory, Pool Init Code Hash {exchange: "SushiSwapV2Executor", args: [ "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" ]}, - // Factory, Pool Init Code Hash + // Args: Factory, Pool Init Code Hash {exchange: "PancakeSwapV2Executor", args: [ "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362", "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" ]}, - {exchange: "UniswapV3Executor", args: ["0x1F98431c8aD98523631AE4a59f267346ea31F984"]}, + // Args: Factory, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ + "0x1F98431c8aD98523631AE4a59f267346ea31F984", + "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" + ]}, + // Args: Deployer, Pool Init Code Hash + {exchange: "PancakeSwapV3Executor", args: [ + "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9 ", + "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" + ]}, + // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "BalancerV2Executor", args: []}, ], "base":[ - // Factory, Pool Init Code Hash + // Args: Factory, Pool Init Code Hash {exchange: "UniswapV2Executor", args: [ "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6", "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" ]}, - // Factory, Pool Init Code Hash + // Args: Factory, Pool Init Code Hash {exchange: "SushiSwapV2Executor", args: [ "0x71524B4f93c58fcbF659783284E38825f0622859", "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" ]}, - // Factory, Pool Init Code Hash + // Args: Factory, Pool Init Code Hash {exchange: "PancakeSwapV2Executor", args: [ "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" ]}, - // Factory, Pool Init Code Hash - {exchange: "UniswapV3Executor", args: ["0x33128a8fC17869897dcE68Ed026d694621f6FDfD"]}, + // Args: Factory, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ + "0x33128a8fC17869897dcE68Ed026d694621f6FDfD", + "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" + ]}, + // Args: Deployer, Pool Init Code Hash + {exchange: "PancakeSwapV3Executor", args: [ + "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9 ", + "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" + ]}, + // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]}, {exchange: "BalancerV2Executor", args: []}, ], diff --git a/foundry/src/executors/UniswapV2Executor.sol b/foundry/src/executors/UniswapV2Executor.sol index 51a33e6..50fb056 100644 --- a/foundry/src/executors/UniswapV2Executor.sol +++ b/foundry/src/executors/UniswapV2Executor.sol @@ -14,18 +14,18 @@ contract UniswapV2Executor is IExecutor { using SafeERC20 for IERC20; address public immutable factory; - bytes32 public immutable init_code; + bytes32 public immutable initCode; address private immutable self; - constructor(address _factory, bytes32 _init_code) { + constructor(address _factory, bytes32 _initCode) { if (_factory == address(0)) { revert UniswapV2Executor__InvalidFactory(); } - if (_init_code == bytes32(0)) { + if (_initCode == bytes32(0)) { revert UniswapV2Executor__InvalidInitCode(); } factory = _factory; - init_code = _init_code; + initCode = _initCode; self = address(this); } @@ -105,7 +105,7 @@ contract UniswapV2Executor is IExecutor { uint160( uint256( keccak256( - abi.encodePacked(hex"ff", factory, salt, init_code) + abi.encodePacked(hex"ff", factory, salt, initCode) ) ) ) diff --git a/foundry/src/executors/UniswapV3Executor.sol b/foundry/src/executors/UniswapV3Executor.sol index c03b5d3..f5878a8 100644 --- a/foundry/src/executors/UniswapV3Executor.sol +++ b/foundry/src/executors/UniswapV3Executor.sol @@ -10,24 +10,28 @@ import "@interfaces/ICallback.sol"; error UniswapV3Executor__InvalidDataLength(); error UniswapV3Executor__InvalidFactory(); error UniswapV3Executor__InvalidTarget(); +error UniswapV3Executor__InvalidInitCode(); contract UniswapV3Executor is IExecutor, ICallback { using SafeERC20 for IERC20; - bytes32 internal constant POOL_INIT_CODE_HASH = - 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; uint160 private constant MIN_SQRT_RATIO = 4295128739; uint160 private constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; address public immutable factory; + bytes32 public immutable initCode; address private immutable self; - constructor(address _factory) { + constructor(address _factory, bytes32 _initCode) { if (_factory == address(0)) { revert UniswapV3Executor__InvalidFactory(); } + if (_initCode == bytes32(0)) { + revert UniswapV3Executor__InvalidInitCode(); + } factory = _factory; + initCode = _initCode; self = address(this); } @@ -167,7 +171,7 @@ contract UniswapV3Executor is IExecutor, ICallback { hex"ff", factory, keccak256(abi.encode(token0, token1, fee)), - POOL_INIT_CODE_HASH + initCode ) ) ) diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index d366db9..1a1fff7 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -55,6 +55,15 @@ contract Constants is Test, BaseConstants { address PANCAKESWAP_WBTC_WETH_POOL = 0x4AB6702B3Ed3877e9b1f203f90cbEF13d663B0e8; + // Uniswap v3 + address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; + address USDC_WETH_USV3 = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640; // 0.05% fee + address USDC_WETH_USV3_2 = 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8; // 0.3% fee + + // Pancakeswap v3 + address PANCAKESWAPV3_WETH_USDT_POOL = + 0x6CA298D2983aB03Aa1dA7679389D955A4eFEE15C; + // Factories address USV3_FACTORY_ETHEREUM = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address USV2_FACTORY_ETHEREUM = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; @@ -63,10 +72,9 @@ contract Constants is Test, BaseConstants { address PANCAKESWAPV2_FACTORY_ETHEREUM = 0x1097053Fd2ea711dad45caCcc45EfF7548fCB362; - // Uniswap v3 - address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; - address USDC_WETH_USV3 = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640; // 0.05% fee - address USDC_WETH_USV3_2 = 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8; // 0.3% fee + // Pancakeswap uses their deployer instead of their factory for target verification + address PANCAKESWAPV3_DEPLOYER_ETHEREUM = + 0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9; // Uniswap universal router address UNIVERSAL_ROUTER = 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af; @@ -77,10 +85,14 @@ contract Constants is Test, BaseConstants { // Pool Code Init Hashes bytes32 USV2_POOL_CODE_INIT_HASH = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f; + bytes32 USV3_POOL_CODE_INIT_HASH = + 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; bytes32 SUSHIV2_POOL_CODE_INIT_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303; bytes32 PANCAKEV2_POOL_CODE_INIT_HASH = 0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d; + bytes32 PANCAKEV3_POOL_CODE_INIT_HASH = + 0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2; /** * @dev Deploys a dummy contract with non-empty bytecode diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index dd4bc57..f6eb0ef 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -44,9 +44,10 @@ contract TychoRouterTestSetup is Test, Constants { vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.startPrank(ADMIN); - address factoryV3 = USV3_FACTORY_ETHEREUM; address factoryV2 = USV2_FACTORY_ETHEREUM; + address factoryV3 = USV3_FACTORY_ETHEREUM; bytes32 initCodeV2 = USV2_POOL_CODE_INIT_HASH; + bytes32 initCodeV3 = USV3_POOL_CODE_INIT_HASH; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; IPoolManager poolManager = IPoolManager(poolManagerAddress); tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); @@ -62,7 +63,7 @@ contract TychoRouterTestSetup is Test, Constants { vm.stopPrank(); usv2Executor = new UniswapV2Executor(factoryV2, initCodeV2); - usv3Executor = new UniswapV3Executor(factoryV3); + usv3Executor = new UniswapV3Executor(factoryV3, initCodeV3); usv4Executor = new UniswapV4Executor(poolManager); vm.startPrank(EXECUTOR_SETTER); address[] memory executors = new address[](3); diff --git a/foundry/test/executors/UniswapV2Executor.t.sol b/foundry/test/executors/UniswapV2Executor.t.sol index 99110ef..a26a9b7 100644 --- a/foundry/test/executors/UniswapV2Executor.t.sol +++ b/foundry/test/executors/UniswapV2Executor.t.sol @@ -6,8 +6,8 @@ import {Test} from "../../lib/forge-std/src/Test.sol"; import {Constants} from "../Constants.sol"; contract UniswapV2ExecutorExposed is UniswapV2Executor { - constructor(address _factory, bytes32 _init_code) - UniswapV2Executor(_factory, _init_code) + constructor(address _factory, bytes32 _initCode) + UniswapV2Executor(_factory, _initCode) {} function decodeParams(bytes calldata data) diff --git a/foundry/test/executors/UniswapV3Executor.t.sol b/foundry/test/executors/UniswapV3Executor.t.sol index 8928627..01f5d1c 100644 --- a/foundry/test/executors/UniswapV3Executor.t.sol +++ b/foundry/test/executors/UniswapV3Executor.t.sol @@ -6,7 +6,9 @@ import {Test} from "../../lib/forge-std/src/Test.sol"; import {Constants} from "../Constants.sol"; contract UniswapV3ExecutorExposed is UniswapV3Executor { - constructor(address _factory) UniswapV3Executor(_factory) {} + constructor(address _factory, bytes32 _initCode) + UniswapV3Executor(_factory, _initCode) + {} function decodeData(bytes calldata data) external @@ -37,6 +39,7 @@ contract UniswapV3ExecutorTest is Test, Constants { using SafeERC20 for IERC20; UniswapV3ExecutorExposed uniswapV3Exposed; + UniswapV3ExecutorExposed pancakeV3Exposed; IERC20 WETH = IERC20(WETH_ADDR); IERC20 DAI = IERC20(DAI_ADDR); @@ -44,7 +47,12 @@ contract UniswapV3ExecutorTest is Test, Constants { uint256 forkBlock = 17323404; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - uniswapV3Exposed = new UniswapV3ExecutorExposed(USV3_FACTORY_ETHEREUM); + uniswapV3Exposed = new UniswapV3ExecutorExposed( + USV3_FACTORY_ETHEREUM, USV3_POOL_CODE_INIT_HASH + ); + pancakeV3Exposed = new UniswapV3ExecutorExposed( + PANCAKESWAPV3_DEPLOYER_ETHEREUM, PANCAKEV3_POOL_CODE_INIT_HASH + ); } function testDecodeParams() public view { @@ -84,6 +92,12 @@ contract UniswapV3ExecutorTest is Test, Constants { ); } + function testVerifyPairAddressPancake() public view { + pancakeV3Exposed.verifyPairAddress( + WETH_ADDR, USDT_ADDR, 500, PANCAKESWAPV3_WETH_USDT_POOL + ); + } + function testUSV3Callback() public { uint24 poolFee = 3000; uint256 amountOwed = 1000000000000000000; From 39bd9df4b6ca8db78bb4d6757c93b30cce29f360 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 19 Mar 2025 23:13:18 -0400 Subject: [PATCH 11/79] fix: proper exchange name when deploying executors - Also update base executors in json --- config/executor_addresses.json | 3 +++ foundry/scripts/deploy-executors.js | 34 ++++++++++++++--------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 02e98e6..d7c9d89 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -13,6 +13,9 @@ }, "base": { "uniswap_v2": "0x852389F3Cde47e7dab5a29Fdd3E58c7050f1a019", + "sushiswap_v2": "0xbAf4569234712233Ae2142A07Bf164f988bDf5B5", + "pancakeswap_v2": "0xd3d73f114CbB40AD17CEEfC046FB18ecDe08678D", + "pancakeswap_v3": "0x2Ab42fdc9cfB3BAc0Db6F0d180A5B03872AFc25B", "uniswap_v3": "0x447135F701A4650e4Bd19c2B2B99cb324f8942c3", "uniswap_v4": "0xc34689d354B42b2780Fbe0A785080d761E5C882b" }, diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index 0e0501d..8459123 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -5,32 +5,32 @@ const hre = require("hardhat"); // Comment out the executors you don't want to deploy const executors_to_deploy = { "ethereum":[ - // Args: Factory, Pool Init Code Hash + // USV2 - Args: Factory, Pool Init Code Hash {exchange: "UniswapV2Executor", args: [ "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" ]}, - // Args: Factory, Pool Init Code Hash - {exchange: "SushiSwapV2Executor", args: [ + // SUSHISWAP - Args: Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" ]}, - // Args: Factory, Pool Init Code Hash - {exchange: "PancakeSwapV2Executor", args: [ + // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362", "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" ]}, - // Args: Factory, Pool Init Code Hash + // USV3 -Args: Factory, Pool Init Code Hash {exchange: "UniswapV3Executor", args: [ "0x1F98431c8aD98523631AE4a59f267346ea31F984", "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" ]}, - // Args: Deployer, Pool Init Code Hash - {exchange: "PancakeSwapV3Executor", args: [ + // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9 ", "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" ]}, - // Args: Pool manager + // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "BalancerV2Executor", args: []}, ], @@ -40,24 +40,24 @@ const executors_to_deploy = { "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6", "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" ]}, - // Args: Factory, Pool Init Code Hash - {exchange: "SushiSwapV2Executor", args: [ + // SUSHISWAP V2 - Args: Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ "0x71524B4f93c58fcbF659783284E38825f0622859", "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" ]}, - // Args: Factory, Pool Init Code Hash - {exchange: "PancakeSwapV2Executor", args: [ + // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" ]}, - // Args: Factory, Pool Init Code Hash + // USV3 - Args: Factory, Pool Init Code Hash {exchange: "UniswapV3Executor", args: [ "0x33128a8fC17869897dcE68Ed026d694621f6FDfD", "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" ]}, - // Args: Deployer, Pool Init Code Hash - {exchange: "PancakeSwapV3Executor", args: [ - "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9 ", + // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ + "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" ]}, // Args: Pool manager From 783712be5d8dae626c735193416ba03701d3a616 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 20 Mar 2025 11:26:18 -0400 Subject: [PATCH 12/79] feat: Set v2/v3 executor addresses on ethereum - Also fix router address env name in readme --- config/executor_addresses.json | 3 +++ foundry/scripts/README.md | 2 +- foundry/scripts/deploy-executors.js | 2 +- src/encoding/evm/swap_encoder/builder.rs | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index d7c9d89..941ce27 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -1,7 +1,10 @@ { "ethereum": { "uniswap_v2": "0xf6c5be66FFf9DC69962d73da0A617a827c382329", + "sushiswap_v2": "0x8ABd4075cF54E0A9C52D18e6951b969AF3249cF9", + "pancakeswap_v2": "0x9fC9e63cCf5F773A8bC79DcfA38c581B0DEa1d11", "uniswap_v3": "0xdD8559c917393FC8DD2b4dD289c52Ff445fDE1B0", + "pancakeswap_v3": "0x455b57440465139482E9E0ED9DC308d7BA6A8515", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" }, diff --git a/foundry/scripts/README.md b/foundry/scripts/README.md index aba7fcb..15fb38d 100644 --- a/foundry/scripts/README.md +++ b/foundry/scripts/README.md @@ -38,7 +38,7 @@ For each of the following, you must select one of `tenderly_ethereum`, `tenderly 1. Deploy router: `npx hardhat run scripts/deploy-router.js --network NETWORK` 2. Define the accounts to grant roles to in `scripts/roles.json` -3. Export the router address to the environment variable `export ROUTER=` +3. Export the router address to the environment variable `export ROUTER_ADDRESS=` 4. Grant roles: `npx hardhat run scripts/set-roles.js --network NETWORK` 5. Set executors: `npx hardhat run scripts/set-executors.js --network NETWORK`. Make sure you change the DEPLOY_WALLET to the executor deployer wallet. If you need to deploy executors, follow the instructions below. diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index 8459123..83ec42e 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -27,7 +27,7 @@ const executors_to_deploy = { ]}, // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash {exchange: "UniswapV3Executor", args: [ - "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9 ", + "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" ]}, // Args: Pool manager diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index 075dad0..f00d1e8 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -23,8 +23,11 @@ impl SwapEncoderBuilder { pub fn build(self) -> Result, EncodingError> { match self.protocol_system.as_str() { "uniswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), + "sushiswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), + "pancakeswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), "vm:balancer_v2" => Ok(Box::new(BalancerV2SwapEncoder::new(self.executor_address))), "uniswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), + "pancakeswap_v3" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), "uniswap_v4" => Ok(Box::new(UniswapV4SwapEncoder::new(self.executor_address))), _ => Err(EncodingError::FatalError(format!( "Unknown protocol system: {}", From 1ed149a9b8ffd41b9cc702c77e146fa234176af0 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 20 Mar 2025 12:20:10 -0400 Subject: [PATCH 13/79] fix: Remove pancakeswap V3 from approved executor addresses - Our router does not properly support pancakeswap. --- config/executor_addresses.json | 2 -- foundry/scripts/deploy-executors.js | 10 ---------- src/encoding/evm/swap_encoder/builder.rs | 1 - 3 files changed, 13 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 941ce27..1ec0465 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -4,7 +4,6 @@ "sushiswap_v2": "0x8ABd4075cF54E0A9C52D18e6951b969AF3249cF9", "pancakeswap_v2": "0x9fC9e63cCf5F773A8bC79DcfA38c581B0DEa1d11", "uniswap_v3": "0xdD8559c917393FC8DD2b4dD289c52Ff445fDE1B0", - "pancakeswap_v3": "0x455b57440465139482E9E0ED9DC308d7BA6A8515", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" }, @@ -18,7 +17,6 @@ "uniswap_v2": "0x852389F3Cde47e7dab5a29Fdd3E58c7050f1a019", "sushiswap_v2": "0xbAf4569234712233Ae2142A07Bf164f988bDf5B5", "pancakeswap_v2": "0xd3d73f114CbB40AD17CEEfC046FB18ecDe08678D", - "pancakeswap_v3": "0x2Ab42fdc9cfB3BAc0Db6F0d180A5B03872AFc25B", "uniswap_v3": "0x447135F701A4650e4Bd19c2B2B99cb324f8942c3", "uniswap_v4": "0xc34689d354B42b2780Fbe0A785080d761E5C882b" }, diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index 83ec42e..77e9dbb 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -25,11 +25,6 @@ const executors_to_deploy = { "0x1F98431c8aD98523631AE4a59f267346ea31F984", "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" ]}, - // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash - {exchange: "UniswapV3Executor", args: [ - "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", - "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" - ]}, // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "BalancerV2Executor", args: []}, @@ -55,11 +50,6 @@ const executors_to_deploy = { "0x33128a8fC17869897dcE68Ed026d694621f6FDfD", "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" ]}, - // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash - {exchange: "UniswapV3Executor", args: [ - "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", - "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" - ]}, // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]}, {exchange: "BalancerV2Executor", args: []}, diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index f00d1e8..40ede0f 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -27,7 +27,6 @@ impl SwapEncoderBuilder { "pancakeswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), "vm:balancer_v2" => Ok(Box::new(BalancerV2SwapEncoder::new(self.executor_address))), "uniswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), - "pancakeswap_v3" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), "uniswap_v4" => Ok(Box::new(UniswapV4SwapEncoder::new(self.executor_address))), _ => Err(EncodingError::FatalError(format!( "Unknown protocol system: {}", From 9ba78cf2d07b99667ced1fb6d443cec180ff57d5 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 20 Mar 2025 19:52:27 +0000 Subject: [PATCH 14/79] chore(release): 0.67.0 [skip ci] ## [0.67.0](https://github.com/propeller-heads/tycho-execution/compare/0.66.1...0.67.0) (2025-03-20) ### Features * Set v2/v3 executor addresses on ethereum ([783712b](https://github.com/propeller-heads/tycho-execution/commit/783712be5d8dae626c735193416ba03701d3a616)) * Support Pancakeswap v3 on ethereum ([2a4ee88](https://github.com/propeller-heads/tycho-execution/commit/2a4ee88cad46dfeb068809bdd885e63094020bcd)) * Support sushiswap v2 and pancakeswap v2 on ethereum ([0a8a34b](https://github.com/propeller-heads/tycho-execution/commit/0a8a34be035588d45e6b72a42f4dd691e3c98d2f)) ### Bug Fixes * proper exchange name when deploying executors ([39bd9df](https://github.com/propeller-heads/tycho-execution/commit/39bd9df4b6ca8db78bb4d6757c93b30cce29f360)) * Remove pancakeswap V3 from approved executor addresses ([1ed149a](https://github.com/propeller-heads/tycho-execution/commit/1ed149a9b8ffd41b9cc702c77e146fa234176af0)) --- CHANGELOG.md | 15 +++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b46ceb1..6e0bd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## [0.67.0](https://github.com/propeller-heads/tycho-execution/compare/0.66.1...0.67.0) (2025-03-20) + + +### Features + +* Set v2/v3 executor addresses on ethereum ([783712b](https://github.com/propeller-heads/tycho-execution/commit/783712be5d8dae626c735193416ba03701d3a616)) +* Support Pancakeswap v3 on ethereum ([2a4ee88](https://github.com/propeller-heads/tycho-execution/commit/2a4ee88cad46dfeb068809bdd885e63094020bcd)) +* Support sushiswap v2 and pancakeswap v2 on ethereum ([0a8a34b](https://github.com/propeller-heads/tycho-execution/commit/0a8a34be035588d45e6b72a42f4dd691e3c98d2f)) + + +### Bug Fixes + +* proper exchange name when deploying executors ([39bd9df](https://github.com/propeller-heads/tycho-execution/commit/39bd9df4b6ca8db78bb4d6757c93b30cce29f360)) +* Remove pancakeswap V3 from approved executor addresses ([1ed149a](https://github.com/propeller-heads/tycho-execution/commit/1ed149a9b8ffd41b9cc702c77e146fa234176af0)) + ## [0.66.1](https://github.com/propeller-heads/tycho-execution/compare/0.66.0...0.66.1) (2025-03-19) diff --git a/Cargo.lock b/Cargo.lock index a326a08..bdbcf8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.66.1" +version = "0.67.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index cc569cd..b3cd474 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.66.1" +version = "0.67.0" edition = "2021" [[bin]] From 15fed02348345ace75d3c0591131952f5fb35e60 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Tue, 25 Mar 2025 13:45:34 +0300 Subject: [PATCH 15/79] chore: updated ci test workflow --- .github/workflows/ci.yaml | 2 +- .../workflows/tests-and-lints-template.yaml | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d8bce90..6da8e5b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,7 +1,7 @@ name: Continuous Integration on: - pull_request: + pull_request_target: permissions: id-token: write diff --git a/.github/workflows/tests-and-lints-template.yaml b/.github/workflows/tests-and-lints-template.yaml index 70f431b..12cd28a 100644 --- a/.github/workflows/tests-and-lints-template.yaml +++ b/.github/workflows/tests-and-lints-template.yaml @@ -39,7 +39,7 @@ jobs: steps: - name: Generate a token id: generate-token - uses: getsentry/action-github-app-token@v2 + uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc with: app_id: ${{ secrets.app_id }} private_key: ${{ secrets.app_private_key }} @@ -48,7 +48,9 @@ jobs: run: sudo apt update && sudo apt install -y git - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Setup git to use https run: | @@ -57,17 +59,17 @@ jobs: git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf ssh://github.com - name: Setup toolchain - uses: dtolnay/rust-toolchain@v1 + uses: dtolnay/rust-toolchain@888c2e1ea69ab0d4330cbf0af1ecc7b68f368cc1 with: toolchain: ${{ matrix.toolchain }} - name: Setup Rust Cache - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 with: cache-on-failure: true - name: Install latest nextest release - uses: taiki-e/install-action@nextest + uses: taiki-e/install-action@b239071b2aedda3db20530301c2d88cd538e90d3 - name: Test run: cargo nextest run --workspace --lib --all-targets && cargo test --doc @@ -79,7 +81,7 @@ jobs: steps: - name: Generate a token id: generate-token - uses: getsentry/action-github-app-token@v2 + uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc with: app_id: ${{ secrets.app_id }} private_key: ${{ secrets.app_private_key }} @@ -88,7 +90,9 @@ jobs: run: sudo apt update && sudo apt install -y git - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + ref: ${{ github.head_ref }} - name: Setup git to use https run: | @@ -97,13 +101,13 @@ jobs: git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf ssh://github.com - name: Setup clippy toolchain - stable - uses: dtolnay/rust-toolchain@v1 + uses: dtolnay/rust-toolchain@888c2e1ea69ab0d4330cbf0af1ecc7b68f368cc1 with: toolchain: stable components: clippy - name: Setup Rust Cache - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 with: cache-on-failure: true @@ -116,7 +120,7 @@ jobs: RUSTFLAGS: -Dwarnings - name: Setup rustfmt toolchain - nightly - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@a02741459ec5e501b9843ed30b535ca0a0376ae4 with: components: rustfmt From d582543057665b737cc0aab5243ccc22db1f0a13 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Fri, 21 Mar 2025 11:26:41 -0400 Subject: [PATCH 16/79] feat: Pancakeswap V3 support --- .../lib/v3-updated/CallbackValidationV2.sol | 11 ++++--- foundry/lib/v3-updated/PoolAddressV2.sol | 6 ++-- foundry/scripts/deploy-executors.js | 11 ++++++- foundry/src/TychoRouter.sol | 19 +++++++++++ foundry/src/executors/UniswapV3Executor.sol | 4 ++- foundry/test/TychoRouter.t.sol | 33 +++++++++++++++++++ foundry/test/TychoRouterTestSetup.sol | 10 ++++-- 7 files changed, 82 insertions(+), 12 deletions(-) diff --git a/foundry/lib/v3-updated/CallbackValidationV2.sol b/foundry/lib/v3-updated/CallbackValidationV2.sol index a3c7a7f..0a24242 100644 --- a/foundry/lib/v3-updated/CallbackValidationV2.sol +++ b/foundry/lib/v3-updated/CallbackValidationV2.sol @@ -18,12 +18,14 @@ library CallbackValidationV2 { address factory, address tokenA, address tokenB, - uint24 fee + uint24 fee, + bytes32 initCode ) internal view returns (IUniswapV3Pool pool) { return verifyCallback( factory, - PoolAddressV2.getPoolKey(tokenA, tokenB, fee) + PoolAddressV2.getPoolKey(tokenA, tokenB, fee), + initCode ); } @@ -33,9 +35,10 @@ library CallbackValidationV2 { /// @return pool The V3 pool contract address function verifyCallback( address factory, - PoolAddressV2.PoolKey memory poolKey + PoolAddressV2.PoolKey memory poolKey, + bytes32 initCode ) internal view returns (IUniswapV3Pool pool) { - pool = IUniswapV3Pool(PoolAddressV2.computeAddress(factory, poolKey)); + pool = IUniswapV3Pool(PoolAddressV2.computeAddress(factory, poolKey, initCode)); require(msg.sender == address(pool), "CV"); } } diff --git a/foundry/lib/v3-updated/PoolAddressV2.sol b/foundry/lib/v3-updated/PoolAddressV2.sol index c63aae7..d30eedb 100644 --- a/foundry/lib/v3-updated/PoolAddressV2.sol +++ b/foundry/lib/v3-updated/PoolAddressV2.sol @@ -5,8 +5,6 @@ pragma solidity >=0.5.0; /// @title Provides functions for deriving a pool address from the factory, tokens, and the fee library PoolAddressV2 { - bytes32 internal constant POOL_INIT_CODE_HASH = - 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; /// @notice The identifying key of the pool struct PoolKey { @@ -33,7 +31,7 @@ library PoolAddressV2 { /// @param factory The Uniswap V3 factory contract address /// @param key The PoolKey /// @return pool The contract address of the V3 pool - function computeAddress(address factory, PoolKey memory key) + function computeAddress(address factory, PoolKey memory key, bytes32 initCode) internal pure returns (address pool) @@ -49,7 +47,7 @@ library PoolAddressV2 { keccak256( abi.encode(key.token0, key.token1, key.fee) ), - POOL_INIT_CODE_HASH + initCode ) ) ) diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index 77e9dbb..c8809b7 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -25,6 +25,11 @@ const executors_to_deploy = { "0x1F98431c8aD98523631AE4a59f267346ea31F984", "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" ]}, + // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ + "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", + "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" + ]}, // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "BalancerV2Executor", args: []}, @@ -50,11 +55,15 @@ const executors_to_deploy = { "0x33128a8fC17869897dcE68Ed026d694621f6FDfD", "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" ]}, + // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ + "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", + "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" + ]}, // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]}, {exchange: "BalancerV2Executor", args: []}, ], - } async function main() { diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 47d6d14..2f29f6e 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -518,6 +518,25 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { _handleCallback(fullData); } + /** + * @dev Called by PancakeV3 pool when swapping on it. + */ + function pancakeV3SwapCallback( + int256, /* amount0Delta */ + int256, /* amount1Delta */ + bytes calldata data + ) external { + if (data.length < 24) revert TychoRouter__InvalidDataLength(); + // We are taking advantage of the fact that the data we need is already encoded in the correct format inside msg.data + // This way we preserve the bytes calldata (and don't need to convert it to bytes memory) + uint256 dataOffset = 4 + 32 + 32 + 32; // Skip selector + 2 ints + data_offset + uint256 dataLength = + uint256(bytes32(msg.data[dataOffset:dataOffset + 32])); + + bytes calldata fullData = msg.data[4:dataOffset + 32 + dataLength]; + _handleCallback(fullData); + } + /** * @dev Called by UniswapV4 pool manager after achieving unlock state. */ diff --git a/foundry/src/executors/UniswapV3Executor.sol b/foundry/src/executors/UniswapV3Executor.sol index f5878a8..050bb83 100644 --- a/foundry/src/executors/UniswapV3Executor.sol +++ b/foundry/src/executors/UniswapV3Executor.sol @@ -107,7 +107,9 @@ contract UniswapV3Executor is IExecutor, ICallback { uint24 poolFee = uint24(bytes3(data[40:43])); // slither-disable-next-line unused-return - CallbackValidationV2.verifyCallback(factory, tokenIn, tokenOut, poolFee); + CallbackValidationV2.verifyCallback( + factory, tokenIn, tokenOut, poolFee, initCode + ); } function uniswapV3SwapCallback( diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 3801a2e..befc8b5 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -753,6 +753,39 @@ contract TychoRouterTest is TychoRouterTestSetup { assertGe(finalBalance, expAmountOut); } + function testSwapSinglePancakeV3() public { + // Trade 1 WETH for USDT with 1 swap on Pancakeswap V3 + // 1 WETH -> USDT + // (PancakeV3) + uint256 amountIn = 10 ** 18; + deal(WETH_ADDR, tychoRouterAddr, amountIn); + + uint256 expAmountOut = 2659_567519; //Swap 1 WETH for 1205.12 DAI + bool zeroForOne = true; + bytes memory protocolData = encodeUniswapV3Swap( + WETH_ADDR, + USDT_ADDR, + tychoRouterAddr, + PANCAKESWAPV3_WETH_USDT_POOL, + zeroForOne + ); + bytes memory swap = encodeSwap( + uint8(0), + uint8(1), + uint24(0), + address(pancakev3Executor), + protocolData + ); + + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps)); + + uint256 finalBalance = IERC20(USDT_ADDR).balanceOf(tychoRouterAddr); + assertGe(finalBalance, expAmountOut); + } + function testSwapSingleUSV3Permit2() public { // Trade 1 WETH for DAI with 1 swap on Uniswap V3 using Permit2 // 1 WETH -> DAI diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index f6eb0ef..1e7b70e 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -36,6 +36,7 @@ contract TychoRouterTestSetup is Test, Constants { address tychoRouterAddr; UniswapV2Executor public usv2Executor; UniswapV3Executor public usv3Executor; + UniswapV3Executor public pancakev3Executor; UniswapV4Executor public usv4Executor; MockERC20[] tokens; @@ -46,8 +47,10 @@ contract TychoRouterTestSetup is Test, Constants { vm.startPrank(ADMIN); address factoryV2 = USV2_FACTORY_ETHEREUM; address factoryV3 = USV3_FACTORY_ETHEREUM; + address factoryPancakeV3 = PANCAKESWAPV3_DEPLOYER_ETHEREUM; bytes32 initCodeV2 = USV2_POOL_CODE_INIT_HASH; bytes32 initCodeV3 = USV3_POOL_CODE_INIT_HASH; + bytes32 initCodePancakeV3 = PANCAKEV3_POOL_CODE_INIT_HASH; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; IPoolManager poolManager = IPoolManager(poolManagerAddress); tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); @@ -65,11 +68,14 @@ contract TychoRouterTestSetup is Test, Constants { usv2Executor = new UniswapV2Executor(factoryV2, initCodeV2); usv3Executor = new UniswapV3Executor(factoryV3, initCodeV3); usv4Executor = new UniswapV4Executor(poolManager); + pancakev3Executor = + new UniswapV3Executor(factoryPancakeV3, initCodePancakeV3); vm.startPrank(EXECUTOR_SETTER); - address[] memory executors = new address[](3); + address[] memory executors = new address[](4); executors[0] = address(usv2Executor); executors[1] = address(usv3Executor); - executors[2] = address(usv4Executor); + executors[2] = address(pancakev3Executor); + executors[3] = address(usv4Executor); tychoRouter.setExecutors(executors); vm.stopPrank(); From c361e1515df25a746ac4f2821e9e243926ff0c8e Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 25 Mar 2025 12:43:00 +0100 Subject: [PATCH 17/79] refactor: remove duplicate code The callback verification was already implemented in _verifyPairAddress. --- .../lib/v3-updated/CallbackValidationV2.sol | 44 -------------- foundry/lib/v3-updated/PoolAddressV2.sol | 57 ------------------- foundry/src/executors/UniswapV3Executor.sol | 6 +- 3 files changed, 1 insertion(+), 106 deletions(-) delete mode 100644 foundry/lib/v3-updated/CallbackValidationV2.sol delete mode 100644 foundry/lib/v3-updated/PoolAddressV2.sol diff --git a/foundry/lib/v3-updated/CallbackValidationV2.sol b/foundry/lib/v3-updated/CallbackValidationV2.sol deleted file mode 100644 index 0a24242..0000000 --- a/foundry/lib/v3-updated/CallbackValidationV2.sol +++ /dev/null @@ -1,44 +0,0 @@ -// Updated v3 lib to solidity >=0.7.6 - -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.7.6; - -import "./PoolAddressV2.sol"; -import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; - -/// @notice Provides validation for callbacks from Uniswap V3 Pools -library CallbackValidationV2 { - /// @notice Returns the address of a valid Uniswap V3 Pool - /// @param factory The contract address of the Uniswap V3 factory - /// @param tokenA The contract address of either token0 or token1 - /// @param tokenB The contract address of the other token - /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// @return pool The V3 pool contract address - function verifyCallback( - address factory, - address tokenA, - address tokenB, - uint24 fee, - bytes32 initCode - ) internal view returns (IUniswapV3Pool pool) { - return - verifyCallback( - factory, - PoolAddressV2.getPoolKey(tokenA, tokenB, fee), - initCode - ); - } - - /// @notice Returns the address of a valid Uniswap V3 Pool - /// @param factory The contract address of the Uniswap V3 factory - /// @param poolKey The identifying key of the V3 pool - /// @return pool The V3 pool contract address - function verifyCallback( - address factory, - PoolAddressV2.PoolKey memory poolKey, - bytes32 initCode - ) internal view returns (IUniswapV3Pool pool) { - pool = IUniswapV3Pool(PoolAddressV2.computeAddress(factory, poolKey, initCode)); - require(msg.sender == address(pool), "CV"); - } -} diff --git a/foundry/lib/v3-updated/PoolAddressV2.sol b/foundry/lib/v3-updated/PoolAddressV2.sol deleted file mode 100644 index d30eedb..0000000 --- a/foundry/lib/v3-updated/PoolAddressV2.sol +++ /dev/null @@ -1,57 +0,0 @@ -// Updated v3 lib to solidity >=0.7.6 - -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee -library PoolAddressV2 { - - /// @notice The identifying key of the pool - struct PoolKey { - address token0; - address token1; - uint24 fee; - } - - /// @notice Returns PoolKey: the ordered tokens with the matched fee levels - /// @param tokenA The first token of a pool, unsorted - /// @param tokenB The second token of a pool, unsorted - /// @param fee The fee level of the pool - /// @return Poolkey The pool details with ordered token0 and token1 assignments - function getPoolKey(address tokenA, address tokenB, uint24 fee) - internal - pure - returns (PoolKey memory) - { - if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA); - return PoolKey({token0: tokenA, token1: tokenB, fee: fee}); - } - - /// @notice Deterministically computes the pool address given the factory and PoolKey - /// @param factory The Uniswap V3 factory contract address - /// @param key The PoolKey - /// @return pool The contract address of the V3 pool - function computeAddress(address factory, PoolKey memory key, bytes32 initCode) - internal - pure - returns (address pool) - { - require(key.token0 < key.token1); - pool = address( - uint160( - uint256( - keccak256( - abi.encodePacked( - hex"ff", - factory, - keccak256( - abi.encode(key.token0, key.token1, key.fee) - ), - initCode - ) - ) - ) - ) - ); - } -} diff --git a/foundry/src/executors/UniswapV3Executor.sol b/foundry/src/executors/UniswapV3Executor.sol index 050bb83..1d46c08 100644 --- a/foundry/src/executors/UniswapV3Executor.sol +++ b/foundry/src/executors/UniswapV3Executor.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.26; import "@interfaces/IExecutor.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; -import "@uniswap/v3-updated/CallbackValidationV2.sol"; import "@interfaces/ICallback.sol"; error UniswapV3Executor__InvalidDataLength(); @@ -106,10 +105,7 @@ contract UniswapV3Executor is IExecutor, ICallback { address tokenOut = address(bytes20(data[20:40])); uint24 poolFee = uint24(bytes3(data[40:43])); - // slither-disable-next-line unused-return - CallbackValidationV2.verifyCallback( - factory, tokenIn, tokenOut, poolFee, initCode - ); + _verifyPairAddress(tokenIn, tokenOut, poolFee, msg.sender); } function uniswapV3SwapCallback( From fa024a4a6702c7809af31a6ba392338d9368c6d2 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 25 Mar 2025 17:49:33 +0100 Subject: [PATCH 18/79] feat: Add PancakeSwapV3 support to encoding - Add deployed executor address to json - Build proper encoder for pancake v3 - Increase gas limit when setting executors (costs more since we set one more) - Fix json filename in scripts/README.md --- config/executor_addresses.json | 1 + foundry/scripts/README.md | 2 +- foundry/scripts/set-executors.js | 2 +- src/encoding/evm/swap_encoder/builder.rs | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 1ec0465..2079fcf 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -4,6 +4,7 @@ "sushiswap_v2": "0x8ABd4075cF54E0A9C52D18e6951b969AF3249cF9", "pancakeswap_v2": "0x9fC9e63cCf5F773A8bC79DcfA38c581B0DEa1d11", "uniswap_v3": "0xdD8559c917393FC8DD2b4dD289c52Ff445fDE1B0", + "pancakeswap_v3": "0x4929B619A8F0D9c06ed0FfD497636580D823F65d", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" }, diff --git a/foundry/scripts/README.md b/foundry/scripts/README.md index 15fb38d..aa93ee2 100644 --- a/foundry/scripts/README.md +++ b/foundry/scripts/README.md @@ -47,4 +47,4 @@ For each of the following, you must select one of `tenderly_ethereum`, `tenderly 1. In `scripts/deploy-executors.js` define the executors to be deployed 2. Deploy executors: `npx hardhat run scripts/deploy-executors.js --network NETWORK` -3. Fill in the executor addresses in `config/executors.json` +3. Fill in the executor addresses in `config/executor_addresses.json` diff --git a/foundry/scripts/set-executors.js b/foundry/scripts/set-executors.js index a1b0d3c..9455af6 100644 --- a/foundry/scripts/set-executors.js +++ b/foundry/scripts/set-executors.js @@ -51,7 +51,7 @@ async function main() { // Set executors const executorAddresses = executorsToSet.map(executor => executor.executor); const tx = await router.setExecutors(executorAddresses, { - gasLimit: 200000 // should be around 50k per executor + gasLimit: 300000 // should be around 50k per executor }); await tx.wait(); // Wait for the transaction to be mined console.log(`Executors set at transaction: ${tx.hash}`); diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index 40ede0f..6918b0a 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -27,6 +27,7 @@ impl SwapEncoderBuilder { "pancakeswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), "vm:balancer_v2" => Ok(Box::new(BalancerV2SwapEncoder::new(self.executor_address))), "uniswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), + "pancakeswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), "uniswap_v4" => Ok(Box::new(UniswapV4SwapEncoder::new(self.executor_address))), _ => Err(EncodingError::FatalError(format!( "Unknown protocol system: {}", From 9accc843460254d57866dff50af9d132b0ce0ed2 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 26 Mar 2025 09:56:44 +0100 Subject: [PATCH 19/79] docs: Clarify validation criteria for cyclical swaps --- src/encoding/evm/tycho_encoder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/encoding/evm/tycho_encoder.rs b/src/encoding/evm/tycho_encoder.rs index 183e215..bc8ba14 100644 --- a/src/encoding/evm/tycho_encoder.rs +++ b/src/encoding/evm/tycho_encoder.rs @@ -54,6 +54,8 @@ impl EVMTychoEncoder { /// swap's input is the chain's wrapped token. /// * If the solution is unwrapping, the checked token is the chain's native token and the last /// swap's output is the chain's wrapped token. + /// * The token cannot appear more than once in the solution unless it is the first and last + /// token (i.e. a true cyclical swap). fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError> { if solution.exact_out { return Err(EncodingError::FatalError( From 92b5a8adb53fda1b31fb51ac46d6cb68c8ddf27a Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Wed, 26 Mar 2025 12:15:50 +0100 Subject: [PATCH 20/79] Remove split check --- src/encoding/evm/swap_encoder/swap_encoders.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index baccad3..447be6d 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -278,10 +278,6 @@ impl SwapEncoder for EkuboSwapEncoder { swap: Swap, encoding_context: EncodingContext, ) -> Result, EncodingError> { - if !swap.split.is_zero() { - return Err(EncodingError::InvalidInput("splits not implemented".to_string())); - } - if encoding_context.exact_out { return Err(EncodingError::InvalidInput("exact out swaps not implemented".to_string())); } From 3e2e9308d48dc40028e2fe199a6ac334d3361a68 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:48:17 +0100 Subject: [PATCH 21/79] Fix slither lints --- foundry/src/executors/EkuboExecutor.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index b934f8c..fc21e6e 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -93,6 +93,7 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { function _lock(bytes memory data) internal { address target = address(core); + // slither-disable-next-line assembly assembly ("memory-safe") { let args := mload(0x40) @@ -113,7 +114,7 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { function _locked(bytes calldata swapData) internal { // For partial swaps this is not equivalent to the given input amount - uint128 tokenInDebtAmount; + uint128 tokenInDebtAmount = 0; int128 nextAmountIn = int128(uint128(bytes16(swapData[0:16]))); @@ -177,6 +178,7 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { if (token == NATIVE_TOKEN_ADDRESS) { SafeTransferLib.safeTransferETH(target, amount); } else { + // slither-disable-next-line assembly assembly ("memory-safe") { let free := mload(0x40) // selector of pay(address) From 47d96c234776784cb42de31aa50fa75352b06efb Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:50:03 +0100 Subject: [PATCH 22/79] Add integration test --- foundry/test/executors/EkuboExecutor.t.sol | 44 ++++++++++----- .../evm/swap_encoder/swap_encoders.rs | 55 ++++++++++--------- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/foundry/test/executors/EkuboExecutor.t.sol b/foundry/test/executors/EkuboExecutor.t.sol index c323bdb..d870352 100644 --- a/foundry/test/executors/EkuboExecutor.t.sol +++ b/foundry/test/executors/EkuboExecutor.t.sol @@ -9,6 +9,7 @@ import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol"; import {ICore} from "@ekubo/interfaces/ICore.sol"; contract EkuboExecutorTest is Test, Constants { + address constant EXECUTOR_ADDRESS = 0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6; // Same address as in swap_encoder.rs tests EkuboExecutor executor; IERC20 USDC = IERC20(USDC_ADDR); @@ -19,9 +20,14 @@ contract EkuboExecutorTest is Test, Constants { bytes32 constant ORACLE_CONFIG = 0x51d02a5948496a67827242eabc5725531342527c000000000000000000000000; function setUp() public { - uint256 forkBlock = 22082754; - vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - executor = new EkuboExecutor(ICore(payable(CORE_ADDRESS))); + vm.createSelectFork(vm.rpcUrl("mainnet"), 22082754); + + deployCodeTo( + "executors/EkuboExecutor.sol", + abi.encode(CORE_ADDRESS), + EXECUTOR_ADDRESS + ); + executor = EkuboExecutor(payable(EXECUTOR_ADDRESS)); } function testSingleSwapEth() public { @@ -86,7 +92,8 @@ contract EkuboExecutorTest is Test, Constants { assertEq(address(executor).balance, ethBalanceBeforeExecutor + amountOut); } - function testMultiHopSwap() public { + // Expects input that encodes the same test case as swap_encoder::tests::ekubo::test_encode_swap_multi + function multiHopSwap(bytes memory data) internal { uint256 amountIn = 1 ether; deal(address(executor), amountIn); @@ -97,15 +104,6 @@ contract EkuboExecutorTest is Test, Constants { uint256 usdtBalanceBeforeCore = USDT.balanceOf(CORE_ADDRESS); uint256 usdtBalanceBeforeExecutor = USDT.balanceOf(address(executor)); - bytes memory data = abi.encodePacked( - address(executor), // receiver - NATIVE_TOKEN_ADDRESS, // tokenIn - USDC_ADDR, // tokenOut of 1st swap - ORACLE_CONFIG, // config of 1st swap - USDT_ADDR, // tokenOut of 2nd swap - bytes32(0x00000000000000000000000000000000000000000001a36e2eb1c43200000032) // config of 2nd swap (0.0025% fee & 0.005% base pool) - ); - uint256 gasBefore = gasleft(); uint256 amountOut = executor.swap(amountIn, data); console.log(gasBefore - gasleft()); @@ -118,4 +116,24 @@ contract EkuboExecutorTest is Test, Constants { assertEq(USDT.balanceOf(CORE_ADDRESS), usdtBalanceBeforeCore - amountOut); assertEq(USDT.balanceOf(address(executor)), usdtBalanceBeforeExecutor + amountOut); } + + // Same test case as in swap_encoder::tests::ekubo::test_encode_swap_multi + function testMultiHopSwap() public { + bytes memory data = abi.encodePacked( + address(executor), // receiver + NATIVE_TOKEN_ADDRESS, // tokenIn + USDC_ADDR, // tokenOut of 1st swap + ORACLE_CONFIG, // config of 1st swap + USDT_ADDR, // tokenOut of 2nd swap + bytes32(0x00000000000000000000000000000000000000000001a36e2eb1c43200000032) // config of 2nd swap (0.0025% fee & 0.005% base pool) + ); + multiHopSwap(data); + } + + // Data is generated by test case in swap_encoder::tests::ekubo::test_encode_swap_multi + function testMultiHopSwapIntegration() public { + multiHopSwap( + hex"ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032" + ); + } } diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 447be6d..0938088 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -2,7 +2,6 @@ use std::str::FromStr; use alloy_primitives::{Address, Bytes as AlloyBytes}; use alloy_sol_types::SolValue; -use num_traits::Zero; use tycho_core::Bytes; use crate::encoding::{ @@ -705,6 +704,8 @@ mod tests { mod ekubo { use super::*; + const RECEIVER: &str = "ca4f73fe97d0b987a0d12b39bbd562c779bab6f6"; // Random address + #[test] fn test_encode_swap_simple() { let token_in = Bytes::from(Address::ZERO.as_slice()); @@ -729,7 +730,7 @@ mod tests { }; let encoding_context = EncodingContext { - receiver: "0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6".into(), // Random address + receiver: RECEIVER.into(), group_token_in: token_in.clone(), group_token_out: token_out.clone(), exact_out: false, @@ -746,16 +747,15 @@ mod tests { assert_eq!( hex_swap, - String::from(concat!( - // receiver - "ca4f73fe97d0b987a0d12b39bbd562c779bab6f6", - // group token in - "0000000000000000000000000000000000000000", - // token out 1st swap - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - // pool config 1st swap - "51d02a5948496a67827242eabc5725531342527c000000000000000000000000", - )) + RECEIVER.to_string() + + concat!( + // group token in + "0000000000000000000000000000000000000000", + // token out 1st swap + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + // pool config 1st swap + "51d02a5948496a67827242eabc5725531342527c000000000000000000000000", + ), ); } @@ -768,7 +768,7 @@ mod tests { let encoder = EkuboSwapEncoder::new(String::default()); let encoding_context = EncodingContext { - receiver: "0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6".into(), // Random address + receiver: RECEIVER.into(), group_token_in: group_token_in.clone(), group_token_out: group_token_out.clone(), exact_out: false, @@ -818,22 +818,23 @@ mod tests { let combined_hex = format!("{}{}", encode(first_encoded_swap), encode(second_encoded_swap)); + println!("{}", combined_hex); + assert_eq!( combined_hex, - String::from(concat!( - // receiver - "ca4f73fe97d0b987a0d12b39bbd562c779bab6f6", - // group token in - "0000000000000000000000000000000000000000", - // token out 1st swap - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - // pool config 1st swap - "51d02a5948496a67827242eabc5725531342527c000000000000000000000000", - // token out 2nd swap - "dac17f958d2ee523a2206206994597c13d831ec7", - // pool config 2nd swap - "00000000000000000000000000000000000000000001a36e2eb1c43200000032", - )) + RECEIVER.to_string() + + concat!( + // group token in + "0000000000000000000000000000000000000000", + // token out 1st swap + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + // pool config 1st swap + "51d02a5948496a67827242eabc5725531342527c000000000000000000000000", + // token out 2nd swap + "dac17f958d2ee523a2206206994597c13d831ec7", + // pool config 2nd swap + "00000000000000000000000000000000000000000001a36e2eb1c43200000032", + ), ); } } From 9fc99caf749e2410686b10ad92b0f3eb7146a3e5 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:57:43 +0100 Subject: [PATCH 23/79] forge fmt --- foundry/src/executors/EkuboExecutor.sol | 45 ++++++++++------------ foundry/test/executors/EkuboExecutor.t.sol | 37 +++++++++++++----- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index fc21e6e..1c0d07c 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -19,6 +19,9 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { ICore immutable core; + bytes4 constant LOCKED_SELECTOR = 0xb45a3c0e; // locked(uint256) + bytes4 constant PAY_CALLBACK_SELECTOR = 0x599d0714; // payCallback(uint256,address) + uint256 constant POOL_DATA_OFFSET = 56; uint256 constant HOP_BYTE_LEN = 52; @@ -34,7 +37,8 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { if (data.length < 92) revert EkuboExecutor__InvalidDataLength(); uint256 tokenOutOffset = data.length - HOP_BYTE_LEN; - address tokenOut = address(bytes20(LibBytes.loadCalldata(data, tokenOutOffset))); + address tokenOut = + address(bytes20(LibBytes.loadCalldata(data, tokenOutOffset))); uint256 tokenOutBalanceBefore = _balanceOf(tokenOut); @@ -60,9 +64,9 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { bytes4 selector = bytes4(raw[:4]); - if (selector == 0xb45a3c0e) { // Selector of locked(uint256) + if (selector == LOCKED_SELECTOR) { _locked(stripped); - } else if (selector == 0x599d0714) { // Selector of payCallback(uint256,address) + } else if (selector == PAY_CALLBACK_SELECTOR) { _payCallback(stripped); } else { revert EkuboExecutor__UnknownCallback(); @@ -84,7 +88,11 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { SafeTransferLib.safeTransfer(token, address(core), amount); } - function _balanceOf(address token) internal view returns (uint256 balance) { + function _balanceOf(address token) + internal + view + returns (uint256 balance) + { balance = token == NATIVE_TOKEN_ADDRESS ? address(this).balance : IERC20(token).balanceOf(address(this)); @@ -128,22 +136,15 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { uint256 offset = POOL_DATA_OFFSET; for (uint256 i = 0; i < hopsLength; i++) { - address nextTokenOut = address(bytes20(LibBytes.loadCalldata(swapData, offset))); - Config poolConfig = Config.wrap(LibBytes.loadCalldata(swapData, offset + 20)); + address nextTokenOut = + address(bytes20(LibBytes.loadCalldata(swapData, offset))); + Config poolConfig = + Config.wrap(LibBytes.loadCalldata(swapData, offset + 20)); - ( - address token0, - address token1, - bool isToken1 - ) = nextTokenIn > nextTokenOut ? ( - nextTokenOut, - nextTokenIn, - true - ) : ( - nextTokenIn, - nextTokenOut, - false - ); + (address token0, address token1, bool isToken1) = nextTokenIn + > nextTokenOut + ? (nextTokenOut, nextTokenIn, true) + : (nextTokenIn, nextTokenOut, false); (int128 delta0, int128 delta1) = core.swap_611415377( PoolKey(token0, token1, poolConfig), @@ -165,11 +166,7 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { _pay(tokenIn, tokenInDebtAmount); - core.withdraw( - nextTokenIn, - receiver, - uint128(nextAmountIn) - ); + core.withdraw(nextTokenIn, receiver, uint128(nextAmountIn)); } function _pay(address token, uint128 amount) internal { diff --git a/foundry/test/executors/EkuboExecutor.t.sol b/foundry/test/executors/EkuboExecutor.t.sol index d870352..0971b40 100644 --- a/foundry/test/executors/EkuboExecutor.t.sol +++ b/foundry/test/executors/EkuboExecutor.t.sol @@ -9,7 +9,8 @@ import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol"; import {ICore} from "@ekubo/interfaces/ICore.sol"; contract EkuboExecutorTest is Test, Constants { - address constant EXECUTOR_ADDRESS = 0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6; // Same address as in swap_encoder.rs tests + address constant EXECUTOR_ADDRESS = + 0xcA4F73Fe97D0B987a0D12B39BBD562c779BAb6f6; // Same address as in swap_encoder.rs tests EkuboExecutor executor; IERC20 USDC = IERC20(USDC_ADDR); @@ -17,7 +18,8 @@ contract EkuboExecutorTest is Test, Constants { address constant CORE_ADDRESS = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444; - bytes32 constant ORACLE_CONFIG = 0x51d02a5948496a67827242eabc5725531342527c000000000000000000000000; + bytes32 constant ORACLE_CONFIG = + 0x51d02a5948496a67827242eabc5725531342527c000000000000000000000000; function setUp() public { vm.createSelectFork(vm.rpcUrl("mainnet"), 22082754); @@ -57,8 +59,13 @@ contract EkuboExecutorTest is Test, Constants { assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore + amountIn); assertEq(address(executor).balance, ethBalanceBeforeExecutor - amountIn); - assertEq(USDC.balanceOf(CORE_ADDRESS), usdcBalanceBeforeCore - amountOut); - assertEq(USDC.balanceOf(address(executor)), usdcBalanceBeforeExecutor + amountOut); + assertEq( + USDC.balanceOf(CORE_ADDRESS), usdcBalanceBeforeCore - amountOut + ); + assertEq( + USDC.balanceOf(address(executor)), + usdcBalanceBeforeExecutor + amountOut + ); } function testSingleSwapERC20() public { @@ -86,10 +93,15 @@ contract EkuboExecutorTest is Test, Constants { console.log(amountOut); assertEq(USDC.balanceOf(CORE_ADDRESS), usdcBalanceBeforeCore + amountIn); - assertEq(USDC.balanceOf(address(executor)), usdcBalanceBeforeExecutor - amountIn); + assertEq( + USDC.balanceOf(address(executor)), + usdcBalanceBeforeExecutor - amountIn + ); assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore - amountOut); - assertEq(address(executor).balance, ethBalanceBeforeExecutor + amountOut); + assertEq( + address(executor).balance, ethBalanceBeforeExecutor + amountOut + ); } // Expects input that encodes the same test case as swap_encoder::tests::ekubo::test_encode_swap_multi @@ -113,8 +125,13 @@ contract EkuboExecutorTest is Test, Constants { assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore + amountIn); assertEq(address(executor).balance, ethBalanceBeforeExecutor - amountIn); - assertEq(USDT.balanceOf(CORE_ADDRESS), usdtBalanceBeforeCore - amountOut); - assertEq(USDT.balanceOf(address(executor)), usdtBalanceBeforeExecutor + amountOut); + assertEq( + USDT.balanceOf(CORE_ADDRESS), usdtBalanceBeforeCore - amountOut + ); + assertEq( + USDT.balanceOf(address(executor)), + usdtBalanceBeforeExecutor + amountOut + ); } // Same test case as in swap_encoder::tests::ekubo::test_encode_swap_multi @@ -125,7 +142,9 @@ contract EkuboExecutorTest is Test, Constants { USDC_ADDR, // tokenOut of 1st swap ORACLE_CONFIG, // config of 1st swap USDT_ADDR, // tokenOut of 2nd swap - bytes32(0x00000000000000000000000000000000000000000001a36e2eb1c43200000032) // config of 2nd swap (0.0025% fee & 0.005% base pool) + bytes32( + 0x00000000000000000000000000000000000000000001a36e2eb1c43200000032 + ) // config of 2nd swap (0.0025% fee & 0.005% base pool) ); multiHopSwap(data); } From 58e2ddd50e131c484ad53a6dca0b09e1d221d0e5 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 15:42:58 +0300 Subject: [PATCH 24/79] fix: fixed git checkout for codelint --- .github/workflows/tests-and-lints-template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-and-lints-template.yaml b/.github/workflows/tests-and-lints-template.yaml index 12cd28a..4cd9a14 100644 --- a/.github/workflows/tests-and-lints-template.yaml +++ b/.github/workflows/tests-and-lints-template.yaml @@ -92,7 +92,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - ref: ${{ github.head_ref }} + ref: refs/pull/${{ github.event.pull_request.number }}/head - name: Setup git to use https run: | From af0197205adb3220673022b690f1d8aa6f6734aa Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 16:10:14 +0300 Subject: [PATCH 25/79] fix: test run outside a PR --- .github/workflows/ci-test.yaml | 19 +++++++++++++++++++ .github/workflows/evm-foundry-ci.yml | 4 ++-- .github/workflows/slither.yml | 6 +++--- .../workflows/tests-and-lints-template.yaml | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/ci-test.yaml diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml new file mode 100644 index 0000000..8c45407 --- /dev/null +++ b/.github/workflows/ci-test.yaml @@ -0,0 +1,19 @@ +name: Continuous Integration + +on: + push: + branches: + - fix/git-checkout-stage-fix + +permissions: + id-token: write + contents: read + +jobs: + tests-and-lints: + uses: ./.github/workflows/tests-and-lints-template.yaml + secrets: + eth_rpc_url: ${{ secrets.ETH_RPC_URL }} + app_id: ${{ secrets.APP_ID }} + app_private_key: ${{ secrets.APP_PRIVATE_KEY }} + diff --git a/.github/workflows/evm-foundry-ci.yml b/.github/workflows/evm-foundry-ci.yml index ee56fa0..be7c9b8 100644 --- a/.github/workflows/evm-foundry-ci.yml +++ b/.github/workflows/evm-foundry-ci.yml @@ -13,12 +13,12 @@ jobs: env: RPC_URL: ${{ secrets.ETH_RPC_URL }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: submodules: recursive - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@de808b1eea699e761c404bda44ba8f21aba30b2c - name: Check formatting run: forge fmt --check diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml index 4134b29..dc18ce8 100644 --- a/.github/workflows/slither.yml +++ b/.github/workflows/slither.yml @@ -10,8 +10,8 @@ jobs: analyze: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: crytic/slither-action@v0.4.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: crytic/slither-action@f197989dea5b53e986d0f88c60a034ddd77ec9a8 with: target: 'foundry/' - slither-args: '--filter-paths foundry/lib/' \ No newline at end of file + slither-args: '--filter-paths foundry/lib/' diff --git a/.github/workflows/tests-and-lints-template.yaml b/.github/workflows/tests-and-lints-template.yaml index 4cd9a14..947695d 100644 --- a/.github/workflows/tests-and-lints-template.yaml +++ b/.github/workflows/tests-and-lints-template.yaml @@ -92,7 +92,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - ref: refs/pull/${{ github.event.pull_request.number }}/head + ref: ${{ github.event_name == 'pull_request' && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }} - name: Setup git to use https run: | From 9f7d605ea5e76d230b5946c618ece76365fb4f02 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 16:13:52 +0300 Subject: [PATCH 26/79] fix: prepared lint workflow for external contributors --- .github/workflows/ci-test.yaml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/ci-test.yaml diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml deleted file mode 100644 index 8c45407..0000000 --- a/.github/workflows/ci-test.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Continuous Integration - -on: - push: - branches: - - fix/git-checkout-stage-fix - -permissions: - id-token: write - contents: read - -jobs: - tests-and-lints: - uses: ./.github/workflows/tests-and-lints-template.yaml - secrets: - eth_rpc_url: ${{ secrets.ETH_RPC_URL }} - app_id: ${{ secrets.APP_ID }} - app_private_key: ${{ secrets.APP_PRIVATE_KEY }} - From b3c4dbc293df758ff4cff949298a819436d83c38 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 16:27:32 +0300 Subject: [PATCH 27/79] fix: added empty line --- .github/workflows/slither.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml index dc18ce8..242b864 100644 --- a/.github/workflows/slither.yml +++ b/.github/workflows/slither.yml @@ -15,3 +15,4 @@ jobs: with: target: 'foundry/' slither-args: '--filter-paths foundry/lib/' + From ae5d7deaccfc1ac527f88371bb3f055b01689801 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 16:29:53 +0300 Subject: [PATCH 28/79] fix: removed empty line --- .github/workflows/slither.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml index 242b864..dc18ce8 100644 --- a/.github/workflows/slither.yml +++ b/.github/workflows/slither.yml @@ -15,4 +15,3 @@ jobs: with: target: 'foundry/' slither-args: '--filter-paths foundry/lib/' - From fdb015118f756fb8017413736f5d8cc308ebfe35 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 27 Mar 2025 13:35:43 +0000 Subject: [PATCH 29/79] chore(release): 0.67.1 [skip ci] ## [0.67.1](https://github.com/propeller-heads/tycho-execution/compare/0.67.0...0.67.1) (2025-03-27) ### Bug Fixes * added empty line ([b3c4dbc](https://github.com/propeller-heads/tycho-execution/commit/b3c4dbc293df758ff4cff949298a819436d83c38)) * fixed git checkout for codelint ([58e2ddd](https://github.com/propeller-heads/tycho-execution/commit/58e2ddd50e131c484ad53a6dca0b09e1d221d0e5)) * prepared lint workflow for external contributors ([9f7d605](https://github.com/propeller-heads/tycho-execution/commit/9f7d605ea5e76d230b5946c618ece76365fb4f02)) * removed empty line ([ae5d7de](https://github.com/propeller-heads/tycho-execution/commit/ae5d7deaccfc1ac527f88371bb3f055b01689801)) * test run outside a PR ([af01972](https://github.com/propeller-heads/tycho-execution/commit/af0197205adb3220673022b690f1d8aa6f6734aa)) --- CHANGELOG.md | 11 +++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e0bd10..edb670d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [0.67.1](https://github.com/propeller-heads/tycho-execution/compare/0.67.0...0.67.1) (2025-03-27) + + +### Bug Fixes + +* added empty line ([b3c4dbc](https://github.com/propeller-heads/tycho-execution/commit/b3c4dbc293df758ff4cff949298a819436d83c38)) +* fixed git checkout for codelint ([58e2ddd](https://github.com/propeller-heads/tycho-execution/commit/58e2ddd50e131c484ad53a6dca0b09e1d221d0e5)) +* prepared lint workflow for external contributors ([9f7d605](https://github.com/propeller-heads/tycho-execution/commit/9f7d605ea5e76d230b5946c618ece76365fb4f02)) +* removed empty line ([ae5d7de](https://github.com/propeller-heads/tycho-execution/commit/ae5d7deaccfc1ac527f88371bb3f055b01689801)) +* test run outside a PR ([af01972](https://github.com/propeller-heads/tycho-execution/commit/af0197205adb3220673022b690f1d8aa6f6734aa)) + ## [0.67.0](https://github.com/propeller-heads/tycho-execution/compare/0.66.1...0.67.0) (2025-03-20) diff --git a/Cargo.lock b/Cargo.lock index bdbcf8f..546f295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.67.0" +version = "0.67.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index b3cd474..611c77d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.67.0" +version = "0.67.1" edition = "2021" [[bin]] From 5162b9e19efcaa5a2137f71a94f6f9e7f7d14da0 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 16:52:00 +0300 Subject: [PATCH 30/79] fix: prepared lint workflow for external contributors --- .github/workflows/ci-test.yaml | 19 +++++++++++++++++++ .../workflows/tests-and-lints-template.yaml | 11 +++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci-test.yaml diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml new file mode 100644 index 0000000..8c45407 --- /dev/null +++ b/.github/workflows/ci-test.yaml @@ -0,0 +1,19 @@ +name: Continuous Integration + +on: + push: + branches: + - fix/git-checkout-stage-fix + +permissions: + id-token: write + contents: read + +jobs: + tests-and-lints: + uses: ./.github/workflows/tests-and-lints-template.yaml + secrets: + eth_rpc_url: ${{ secrets.ETH_RPC_URL }} + app_id: ${{ secrets.APP_ID }} + app_private_key: ${{ secrets.APP_PRIVATE_KEY }} + diff --git a/.github/workflows/tests-and-lints-template.yaml b/.github/workflows/tests-and-lints-template.yaml index 947695d..6c10017 100644 --- a/.github/workflows/tests-and-lints-template.yaml +++ b/.github/workflows/tests-and-lints-template.yaml @@ -89,10 +89,17 @@ jobs: - name: Install git run: sudo apt update && sudo apt install -y git - - name: Checkout + - name: Checkout PR (from fork or branch) + if: github.event_name == 'pull_request' uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - ref: ${{ github.event_name == 'pull_request' && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }} + ref: refs/pull/${{ github.event.pull_request.number }}/head + + - name: Checkout push branch (main or other) + if: github.event_name != 'pull_request' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + ref: ${{ github.ref }} - name: Setup git to use https run: | From 9896f4882940517d61852300420c7c580138406f Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Thu, 27 Mar 2025 16:53:29 +0300 Subject: [PATCH 31/79] fix: prepared lint workflow for external contributors --- .github/workflows/ci-test.yaml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/ci-test.yaml diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml deleted file mode 100644 index 8c45407..0000000 --- a/.github/workflows/ci-test.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Continuous Integration - -on: - push: - branches: - - fix/git-checkout-stage-fix - -permissions: - id-token: write - contents: read - -jobs: - tests-and-lints: - uses: ./.github/workflows/tests-and-lints-template.yaml - secrets: - eth_rpc_url: ${{ secrets.ETH_RPC_URL }} - app_id: ${{ secrets.APP_ID }} - app_private_key: ${{ secrets.APP_PRIVATE_KEY }} - From 382fab0b463272c77d69812d1ddf520a02e4a539 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 27 Mar 2025 14:08:30 +0000 Subject: [PATCH 32/79] chore(release): 0.67.2 [skip ci] ## [0.67.2](https://github.com/propeller-heads/tycho-execution/compare/0.67.1...0.67.2) (2025-03-27) ### Bug Fixes * prepared lint workflow for external contributors ([9896f48](https://github.com/propeller-heads/tycho-execution/commit/9896f4882940517d61852300420c7c580138406f)) * prepared lint workflow for external contributors ([5162b9e](https://github.com/propeller-heads/tycho-execution/commit/5162b9e19efcaa5a2137f71a94f6f9e7f7d14da0)) --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb670d..3f8e5c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.67.2](https://github.com/propeller-heads/tycho-execution/compare/0.67.1...0.67.2) (2025-03-27) + + +### Bug Fixes + +* prepared lint workflow for external contributors ([9896f48](https://github.com/propeller-heads/tycho-execution/commit/9896f4882940517d61852300420c7c580138406f)) +* prepared lint workflow for external contributors ([5162b9e](https://github.com/propeller-heads/tycho-execution/commit/5162b9e19efcaa5a2137f71a94f6f9e7f7d14da0)) + ## [0.67.1](https://github.com/propeller-heads/tycho-execution/compare/0.67.0...0.67.1) (2025-03-27) diff --git a/Cargo.lock b/Cargo.lock index 546f295..c1c2b94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.67.1" +version = "0.67.2" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 611c77d..f0a0725 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.67.1" +version = "0.67.2" edition = "2021" [[bin]] From 0836bf7d530f18a6c0f112542bcad16050e88afa Mon Sep 17 00:00:00 2001 From: kayibal Date: Thu, 27 Mar 2025 14:56:37 +0100 Subject: [PATCH 33/79] feat: switch to tycho_commons --- Cargo.lock | 9 +++++---- Cargo.toml | 2 +- examples/encoding-example/main.rs | 2 +- src/bin/tycho-encode.rs | 2 +- src/encoding/evm/approvals/permit2.rs | 4 ++-- src/encoding/evm/encoder_builder.rs | 2 +- src/encoding/evm/strategy_encoder/group_swaps.rs | 4 ++-- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 6 +++--- src/encoding/evm/strategy_encoder/strategy_validators.rs | 4 ++-- src/encoding/evm/swap_encoder/swap_encoder_registry.rs | 2 +- src/encoding/evm/swap_encoder/swap_encoders.rs | 4 ++-- src/encoding/evm/tycho_encoder.rs | 6 +++--- src/encoding/evm/utils.rs | 2 +- src/encoding/models.rs | 2 +- src/encoding/strategy_encoder.rs | 2 +- 15 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1c2b94..9e21f0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4316,9 +4316,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "tycho-core" -version = "0.61.1" -source = "git+https://github.com/propeller-heads/tycho-indexer.git?tag=0.61.1#aae2c11bffe9ae5e436adc019b769438fa038272" +name = "tycho-common" +version = "0.63.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c64749b24bdb5de6050c37ea91420a67a5fe606247fdb29f2e812dac30b42e" dependencies = [ "anyhow", "async-trait", @@ -4358,7 +4359,7 @@ dependencies = [ "serde_json", "thiserror 1.0.69", "tokio", - "tycho-core", + "tycho-common", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f0a0725..f55872b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ clap = { version = "4.5.3", features = ["derive"] } alloy = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true } alloy-sol-types = { version = "0.8.14", optional = true } alloy-primitives = { version = "0.8.9", optional = true } -tycho-core = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-core", tag = "0.61.1" } +tycho-common = "0.63.1" once_cell = "1.20.2" [dev-dependencies] diff --git a/examples/encoding-example/main.rs b/examples/encoding-example/main.rs index 48ee12b..70b46d9 100644 --- a/examples/encoding-example/main.rs +++ b/examples/encoding-example/main.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use num_bigint::BigUint; -use tycho_core::{ +use tycho_common::{ models::{protocol::ProtocolComponent, Chain}, Bytes, }; diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 06cbe7b..08d157c 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,7 +1,7 @@ use std::io::{self, Read}; use clap::{Parser, Subcommand}; -use tycho_core::models::Chain; +use tycho_common::models::Chain; use tycho_execution::encoding::{ evm::encoder_builder::EVMEncoderBuilder, models::Solution, tycho_encoder::TychoEncoder, }; diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index f1a5153..549197e 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -15,7 +15,7 @@ use tokio::{ runtime::{Handle, Runtime}, task::block_in_place, }; -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, @@ -175,7 +175,7 @@ mod tests { use alloy_primitives::Uint; use num_bigint::BigUint; - use tycho_core::models::Chain as TychoCoreChain; + use tycho_common::models::Chain as TychoCoreChain; use super::*; diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index 11a5f2e..7a13804 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -1,4 +1,4 @@ -use tycho_core::models::Chain; +use tycho_common::models::Chain; use crate::encoding::{ errors::EncodingError, diff --git a/src/encoding/evm/strategy_encoder/group_swaps.rs b/src/encoding/evm/strategy_encoder/group_swaps.rs index 48cb86b..0e4206c 100644 --- a/src/encoding/evm/strategy_encoder/group_swaps.rs +++ b/src/encoding/evm/strategy_encoder/group_swaps.rs @@ -1,4 +1,4 @@ -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{evm::constants::GROUPABLE_PROTOCOLS, models::Swap}; @@ -74,7 +74,7 @@ mod tests { use std::str::FromStr; use alloy_primitives::hex; - use tycho_core::{models::protocol::ProtocolComponent, Bytes}; + use tycho_common::{models::protocol::ProtocolComponent, Bytes}; use super::*; use crate::encoding::models::Swap; diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 92d1af8..a306b27 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -2,7 +2,7 @@ use std::{collections::HashSet, str::FromStr}; use alloy_primitives::{aliases::U24, U256, U8}; use alloy_sol_types::SolValue; -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, @@ -80,7 +80,7 @@ pub struct SplitSwapStrategyEncoder { impl SplitSwapStrategyEncoder { pub fn new( - blockchain: tycho_core::models::Chain, + blockchain: tycho_common::models::Chain, swap_encoder_registry: SwapEncoderRegistry, swapper_pk: Option, ) -> Result { @@ -341,7 +341,7 @@ mod tests { use alloy_primitives::hex; use num_bigint::{BigInt, BigUint}; use rstest::rstest; - use tycho_core::{ + use tycho_common::{ models::{protocol::ProtocolComponent, Chain as TychoCoreChain}, Bytes, }; diff --git a/src/encoding/evm/strategy_encoder/strategy_validators.rs b/src/encoding/evm/strategy_encoder/strategy_validators.rs index d4bf22f..e7ae722 100644 --- a/src/encoding/evm/strategy_encoder/strategy_validators.rs +++ b/src/encoding/evm/strategy_encoder/strategy_validators.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet, VecDeque}; -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, @@ -203,7 +203,7 @@ mod tests { use num_bigint::BigUint; use rstest::rstest; - use tycho_core::{models::protocol::ProtocolComponent, Bytes}; + use tycho_common::{models::protocol::ProtocolComponent, Bytes}; use super::*; use crate::encoding::models::Swap; diff --git a/src/encoding/evm/swap_encoder/swap_encoder_registry.rs b/src/encoding/evm/swap_encoder/swap_encoder_registry.rs index 72ab48a..8a5b9e4 100644 --- a/src/encoding/evm/swap_encoder/swap_encoder_registry.rs +++ b/src/encoding/evm/swap_encoder/swap_encoder_registry.rs @@ -19,7 +19,7 @@ impl SwapEncoderRegistry { /// executors' addresses in the file at the given path. pub fn new( executors_file_path: Option, - blockchain: tycho_core::models::Chain, + blockchain: tycho_common::models::Chain, ) -> Result { let chain = Chain::from(blockchain); let config_str = if let Some(ref path) = executors_file_path { diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 473b250..c732c87 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use alloy_primitives::{Address, Bytes as AlloyBytes}; use alloy_sol_types::SolValue; -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, @@ -264,7 +264,7 @@ mod tests { use alloy::hex::encode; use num_bigint::BigInt; - use tycho_core::{models::protocol::ProtocolComponent, Bytes}; + use tycho_common::{models::protocol::ProtocolComponent, Bytes}; use super::*; diff --git a/src/encoding/evm/tycho_encoder.rs b/src/encoding/evm/tycho_encoder.rs index bc8ba14..5f20b2e 100644 --- a/src/encoding/evm/tycho_encoder.rs +++ b/src/encoding/evm/tycho_encoder.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use num_bigint::BigUint; -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, @@ -34,7 +34,7 @@ impl Clone for EVMTychoEncoder { impl EVMTychoEncoder { pub fn new( - chain: tycho_core::models::Chain, + chain: tycho_common::models::Chain, strategy_encoder: Box, ) -> Result { let chain: Chain = Chain::from(chain); @@ -177,7 +177,7 @@ impl TychoEncoder for EVMTychoEncoder { mod tests { use std::str::FromStr; - use tycho_core::models::{protocol::ProtocolComponent, Chain as TychoCoreChain}; + use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCoreChain}; use super::*; use crate::encoding::{ diff --git a/src/encoding/evm/utils.rs b/src/encoding/evm/utils.rs index f284ee4..b8af4f5 100644 --- a/src/encoding/evm/utils.rs +++ b/src/encoding/evm/utils.rs @@ -3,7 +3,7 @@ use std::{cmp::max, sync::Arc}; use alloy_primitives::{aliases::U24, keccak256, Address, FixedBytes, Keccak256, U256, U8}; use num_bigint::BigUint; use tokio::runtime::{Handle, Runtime}; -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 0abc963..fff40ee 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -1,7 +1,7 @@ use hex; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; -use tycho_core::{ +use tycho_common::{ models::{protocol::ProtocolComponent, Chain as TychoCoreChain}, Bytes, }; diff --git a/src/encoding/strategy_encoder.rs b/src/encoding/strategy_encoder.rs index b0e42fb..57630a0 100644 --- a/src/encoding/strategy_encoder.rs +++ b/src/encoding/strategy_encoder.rs @@ -1,4 +1,4 @@ -use tycho_core::Bytes; +use tycho_common::Bytes; use crate::encoding::{errors::EncodingError, models::Solution, swap_encoder::SwapEncoder}; From 0ff92c5da27fd0fde687dac949af04eb36136da9 Mon Sep 17 00:00:00 2001 From: kayibal Date: Thu, 27 Mar 2025 14:56:58 +0100 Subject: [PATCH 34/79] ci: Automatically publish to crates.io --- .github/workflows/release.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9e0ce55 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,30 @@ +name: Release + +on: + workflow_dispatch: + inputs: { } + release: + types: [ created, prereleased ] + +permissions: + id-token: write + contents: write + +jobs: + publish-crate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Push to crates.io + run: | + cargo publish --locked --verbose --token ${{ secrets.CRATESIO_REGISTRY_TOKEN }} +# we can't use the action because it errors on github dependencies in any workspace crate +# - uses: katyo/publish-crates@v2 +# with: +# path: "./tycho-common" +# registry-token: ${{ secrets.CRATESIO_REGISTRY_TOKEN }} + From 379858bfca27eb5e8180a32351337779e625e0b5 Mon Sep 17 00:00:00 2001 From: kayibal Date: Thu, 27 Mar 2025 15:10:44 +0100 Subject: [PATCH 35/79] fix: Handle unichain chain id --- src/encoding/models.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/encoding/models.rs b/src/encoding/models.rs index fff40ee..af8a780 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -130,6 +130,7 @@ impl From for Chain { TychoCoreChain::Arbitrum => Chain { id: 42161, name: chain.to_string() }, TychoCoreChain::Starknet => Chain { id: 0, name: chain.to_string() }, TychoCoreChain::Base => Chain { id: 8453, name: chain.to_string() }, + TychoCoreChain::Unichain => Chain { id: 130, name: chain.to_string() }, } } } From b1bc98174c1c67a493f9ab00076af647a7f714a0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 27 Mar 2025 16:02:59 +0000 Subject: [PATCH 36/79] chore(release): 0.68.0 [skip ci] ## [0.68.0](https://github.com/propeller-heads/tycho-execution/compare/0.67.2...0.68.0) (2025-03-27) ### Features * switch to tycho_commons ([0836bf7](https://github.com/propeller-heads/tycho-execution/commit/0836bf7d530f18a6c0f112542bcad16050e88afa)) ### Bug Fixes * Handle unichain chain id ([379858b](https://github.com/propeller-heads/tycho-execution/commit/379858bfca27eb5e8180a32351337779e625e0b5)) --- CHANGELOG.md | 12 ++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f8e5c1..a74fa33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [0.68.0](https://github.com/propeller-heads/tycho-execution/compare/0.67.2...0.68.0) (2025-03-27) + + +### Features + +* switch to tycho_commons ([0836bf7](https://github.com/propeller-heads/tycho-execution/commit/0836bf7d530f18a6c0f112542bcad16050e88afa)) + + +### Bug Fixes + +* Handle unichain chain id ([379858b](https://github.com/propeller-heads/tycho-execution/commit/379858bfca27eb5e8180a32351337779e625e0b5)) + ## [0.67.2](https://github.com/propeller-heads/tycho-execution/compare/0.67.1...0.67.2) (2025-03-27) diff --git a/Cargo.lock b/Cargo.lock index 9e21f0f..6b860ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.67.2" +version = "0.68.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index f55872b..00ae0f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.67.2" +version = "0.68.0" edition = "2021" [[bin]] From 7e7fabf51bff842ab20c2f512e4f3a609a266e79 Mon Sep 17 00:00:00 2001 From: kayibal Date: Thu, 27 Mar 2025 19:19:26 +0100 Subject: [PATCH 37/79] fix: Add crate metadata --- Cargo.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 00ae0f2..c9916bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,14 @@ name = "tycho-execution" version = "0.68.0" edition = "2021" +description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." +repository = "https://github.com/propeller-heads/tycho-execution" +homepage = "https://www.propellerheads.xyz/tycho" +documentation = "https://docs.propellerheads.xyz/tycho" +keywords = ["propellerheads", "solver", "defi", "dex", "mev"] +license = "MIT" +categories = ["finance", "cryptography::cryptocurrencies"] +readme = "README.md" [[bin]] name = "tycho-encode" From 16ade9d8731de8471081d31bc2022d9e11f6723b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 27 Mar 2025 18:29:21 +0000 Subject: [PATCH 38/79] chore(release): 0.68.1 [skip ci] ## [0.68.1](https://github.com/propeller-heads/tycho-execution/compare/0.68.0...0.68.1) (2025-03-27) ### Bug Fixes * Add crate metadata ([7e7fabf](https://github.com/propeller-heads/tycho-execution/commit/7e7fabf51bff842ab20c2f512e4f3a609a266e79)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a74fa33..0e713ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.68.1](https://github.com/propeller-heads/tycho-execution/compare/0.68.0...0.68.1) (2025-03-27) + + +### Bug Fixes + +* Add crate metadata ([7e7fabf](https://github.com/propeller-heads/tycho-execution/commit/7e7fabf51bff842ab20c2f512e4f3a609a266e79)) + ## [0.68.0](https://github.com/propeller-heads/tycho-execution/compare/0.67.2...0.68.0) (2025-03-27) diff --git a/Cargo.lock b/Cargo.lock index 6b860ed..c9b5f19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.68.0" +version = "0.68.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index c9916bd..011f77d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.68.0" +version = "0.68.1" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From a9ddb0e6e9ef546f6e851c3056df5a55ee4dfa76 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Fri, 28 Mar 2025 17:20:10 +0300 Subject: [PATCH 39/79] fix: fix for foundry tests external contributors --- .github/workflows/evm-foundry-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/evm-foundry-ci.yml b/.github/workflows/evm-foundry-ci.yml index be7c9b8..8c4a98e 100644 --- a/.github/workflows/evm-foundry-ci.yml +++ b/.github/workflows/evm-foundry-ci.yml @@ -4,7 +4,7 @@ on: push: branches: - main - pull_request: + pull_request_target: jobs: check: From b401358341e60ab6a2dd8faeb943bf864c5f1cf0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 28 Mar 2025 16:11:08 +0000 Subject: [PATCH 40/79] chore(release): 0.68.2 [skip ci] ## [0.68.2](https://github.com/propeller-heads/tycho-execution/compare/0.68.1...0.68.2) (2025-03-28) ### Bug Fixes * fix for foundry tests external contributors ([a9ddb0e](https://github.com/propeller-heads/tycho-execution/commit/a9ddb0e6e9ef546f6e851c3056df5a55ee4dfa76)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e713ac..0f38b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.68.2](https://github.com/propeller-heads/tycho-execution/compare/0.68.1...0.68.2) (2025-03-28) + + +### Bug Fixes + +* fix for foundry tests external contributors ([a9ddb0e](https://github.com/propeller-heads/tycho-execution/commit/a9ddb0e6e9ef546f6e851c3056df5a55ee4dfa76)) + ## [0.68.1](https://github.com/propeller-heads/tycho-execution/compare/0.68.0...0.68.1) (2025-03-27) diff --git a/Cargo.lock b/Cargo.lock index c9b5f19..54ec35b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.68.1" +version = "0.68.2" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 011f77d..00c7abd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.68.1" +version = "0.68.2" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 95e1bf12906f468c862c5cfaef0450e6f84b6834 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:29:21 +0100 Subject: [PATCH 41/79] Fix wrong argument order --- foundry/src/executors/EkuboExecutor.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index 1c0d07c..7802742 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -82,10 +82,9 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { _locked(msg.data[36:]); } - function payCallback(uint256, address token) external coreOnly { - uint128 amount = uint128(bytes16(LibBytes.loadCalldata(msg.data, 68))); - - SafeTransferLib.safeTransfer(token, address(core), amount); + function payCallback(uint256, address /*token*/) external coreOnly { + // Without selector and locker id + _payCallback(msg.data[36:]); } function _balanceOf(address token) @@ -193,10 +192,10 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { } function _payCallback(bytes calldata payData) internal { - address token = address(bytes20(payData[0:20])); - uint128 amount = uint128(bytes16(payData[20:36])); + address token = address(bytes20(payData[12:32])); // This arg is abi-encoded + uint128 amount = uint128(bytes16(payData[32:48])); - SafeTransferLib.safeTransfer(address(core), token, amount); + SafeTransferLib.safeTransfer(token, address(core), amount); } // To receive withdrawals from Core From 68a9198e9c3f0bbb057ecdae61ab88acfe0b3db5 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:32:22 +0100 Subject: [PATCH 42/79] forge fmt --- foundry/src/executors/EkuboExecutor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index 7802742..4f291fe 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -82,7 +82,7 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { _locked(msg.data[36:]); } - function payCallback(uint256, address /*token*/) external coreOnly { + function payCallback(uint256, address /*token*/ ) external coreOnly { // Without selector and locker id _payCallback(msg.data[36:]); } From a5a691d4aa6208f2e861e26694be1cb00a2c8504 Mon Sep 17 00:00:00 2001 From: Valentin Khramtsov Date: Mon, 31 Mar 2025 12:19:41 +0300 Subject: [PATCH 43/79] chore: set checkout action for lint similar to compile and test --- .github/workflows/tests-and-lints-template.yaml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests-and-lints-template.yaml b/.github/workflows/tests-and-lints-template.yaml index 6c10017..2c9ffcc 100644 --- a/.github/workflows/tests-and-lints-template.yaml +++ b/.github/workflows/tests-and-lints-template.yaml @@ -89,17 +89,10 @@ jobs: - name: Install git run: sudo apt update && sudo apt install -y git - - name: Checkout PR (from fork or branch) - if: github.event_name == 'pull_request' + - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - ref: refs/pull/${{ github.event.pull_request.number }}/head - - - name: Checkout push branch (main or other) - if: github.event_name != 'pull_request' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - with: - ref: ${{ github.ref }} + ref: ${{ github.event.pull_request.head.sha }} - name: Setup git to use https run: | From b3078f9c7b99b3c0c9f0008b97855b48483f06dc Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 27 Mar 2025 23:38:32 +0100 Subject: [PATCH 44/79] feat: Support Ekubo callback in TychoRouter - add integration test - cannot directly call _handleCallback from the locked method of the tycho router because of bytes memory to bytes callback conversion - Rename to EkuboPoolKey because of conflict with USV4 pool key - Bonus: fix bug where input token to swap method must be ERC20 (we should also support ETH) --- config/executor_addresses.json | 3 +- foundry/lib/ekubo/interfaces/ICore.sol | 4 +- foundry/lib/ekubo/types/poolKey.sol | 2 +- foundry/src/TychoRouter.sol | 26 ++++++++- foundry/src/executors/EkuboExecutor.sol | 4 +- foundry/test/TychoRouter.t.sol | 21 +++++++ foundry/test/TychoRouterTestSetup.sol | 8 ++- .../evm/strategy_encoder/strategy_encoders.rs | 58 ++++++++++++++++++- src/encoding/evm/swap_encoder/builder.rs | 4 +- 9 files changed, 120 insertions(+), 10 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 2079fcf..46db52c 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -6,7 +6,8 @@ "uniswap_v3": "0xdD8559c917393FC8DD2b4dD289c52Ff445fDE1B0", "pancakeswap_v3": "0x4929B619A8F0D9c06ed0FfD497636580D823F65d", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", - "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" + "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", + "ekubo": "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", diff --git a/foundry/lib/ekubo/interfaces/ICore.sol b/foundry/lib/ekubo/interfaces/ICore.sol index 2bc6b3d..d2388e8 100644 --- a/foundry/lib/ekubo/interfaces/ICore.sol +++ b/foundry/lib/ekubo/interfaces/ICore.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.26; import {IFlashAccountant} from "./IFlashAccountant.sol"; -import {PoolKey} from "../types/poolKey.sol"; +import {EkuboPoolKey} from "../types/poolKey.sol"; import {SqrtRatio} from "../types/sqrtRatio.sol"; interface ICore is IFlashAccountant { function swap_611415377( - PoolKey memory poolKey, + EkuboPoolKey memory poolKey, int128 amount, bool isToken1, SqrtRatio sqrtRatioLimit, diff --git a/foundry/lib/ekubo/types/poolKey.sol b/foundry/lib/ekubo/types/poolKey.sol index b0301e2..ad20db8 100644 --- a/foundry/lib/ekubo/types/poolKey.sol +++ b/foundry/lib/ekubo/types/poolKey.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.26; type Config is bytes32; // Each pool has its own state associated with this key -struct PoolKey { +struct EkuboPoolKey { address token0; address token1; Config config; diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 2f29f6e..d997588 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -143,7 +143,11 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address receiver, bytes calldata swaps ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { - IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); + if (address(tokenIn) != address(0)) { + IERC20(tokenIn).safeTransferFrom( + msg.sender, address(this), amountIn + ); + } return _swapChecked( amountIn, tokenIn, @@ -548,4 +552,24 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { _handleCallback(data); return ""; } + + function locked(uint256) external { + // TODO replace with real executor address once deployed + address executor = address(0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9); + + // slither-disable-next-line controlled-delegatecall,low-level-calls + (bool success, bytes memory result) = executor.delegatecall( + abi.encodeWithSelector(ICallback.handleCallback.selector, msg.data) + ); + + if (!success) { + revert( + string( + result.length > 0 + ? result + : abi.encodePacked("Callback failed") + ) + ); + } + } } diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index 4f291fe..66e508f 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -9,7 +9,7 @@ import {ILocker, IPayer} from "@ekubo/interfaces/IFlashAccountant.sol"; import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol"; import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol"; import {LibBytes} from "@solady/utils/LibBytes.sol"; -import {Config, PoolKey} from "@ekubo/types/poolKey.sol"; +import {Config, EkuboPoolKey} from "@ekubo/types/poolKey.sol"; import {MAX_SQRT_RATIO, MIN_SQRT_RATIO} from "@ekubo/types/sqrtRatio.sol"; contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { @@ -146,7 +146,7 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { : (nextTokenIn, nextTokenOut, false); (int128 delta0, int128 delta1) = core.swap_611415377( - PoolKey(token0, token1, poolConfig), + EkuboPoolKey(token0, token1, poolConfig), nextAmountIn, isToken1, isToken1 ? MAX_SQRT_RATIO : MIN_SQRT_RATIO, diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index befc8b5..128c97a 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1028,6 +1028,27 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(balancerAfter - balancerBefore, 1120007305574805922); } + function testEkuboIntegration() public { + deal(ALICE, 1 ether); + uint256 balancerBefore = IERC20(USDC_ADDR).balanceOf(ALICE); + + // Approve permit2 + vm.startPrank(ALICE); + // Encoded solution generated using `test_split_encoding_strategy_ekubo` + (bool success,) = tychoRouterAddr.call{value: 1 ether}( + hex"0a83cb080000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000077007500010000005991a2df15a8f6a256d3ec51e99254cd3fb576a93ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000" + ); + + uint256 balancerAfter = IERC20(USDC_ADDR).balanceOf(ALICE); + + assertTrue(success, "Call Failed"); + assertGe(balancerAfter - balancerBefore, 26173932); + + // All input tokens are transferred to the router at first. Make sure we used + // all of it (and thus our splits are correct). + assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); + } + function testSplitSwapIntegration() public { // Test created with calldata from our router encoder, replacing the executor // address with the USV2 executor address. diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 1e7b70e..e1e62e9 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -10,6 +10,7 @@ import "@src/TychoRouter.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol"; import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; +import "../src/executors/EkuboExecutor.sol"; contract TychoRouterExposed is TychoRouter { constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {} @@ -38,6 +39,7 @@ contract TychoRouterTestSetup is Test, Constants { UniswapV3Executor public usv3Executor; UniswapV3Executor public pancakev3Executor; UniswapV4Executor public usv4Executor; + EkuboExecutor public ekuboExecutor; MockERC20[] tokens; function setUp() public { @@ -52,6 +54,8 @@ contract TychoRouterTestSetup is Test, Constants { bytes32 initCodeV3 = USV3_POOL_CODE_INIT_HASH; bytes32 initCodePancakeV3 = PANCAKEV3_POOL_CODE_INIT_HASH; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; + ICore ekuboCore = ICore(0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444); + IPoolManager poolManager = IPoolManager(poolManagerAddress); tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); tychoRouterAddr = address(tychoRouter); @@ -70,12 +74,14 @@ contract TychoRouterTestSetup is Test, Constants { usv4Executor = new UniswapV4Executor(poolManager); pancakev3Executor = new UniswapV3Executor(factoryPancakeV3, initCodePancakeV3); + ekuboExecutor = new EkuboExecutor(ekuboCore); vm.startPrank(EXECUTOR_SETTER); - address[] memory executors = new address[](4); + address[] memory executors = new address[](5); executors[0] = address(usv2Executor); executors[1] = address(usv3Executor); executors[2] = address(pancakev3Executor); executors[3] = address(usv4Executor); + executors[3] = address(ekuboExecutor); tychoRouter.setExecutors(executors); vm.stopPrank(); diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index a306b27..b6e4e7f 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -338,7 +338,7 @@ mod tests { use std::{collections::HashMap, str::FromStr}; use alloy::hex::encode; - use alloy_primitives::hex; + use alloy_primitives::{hex, Address}; use num_bigint::{BigInt, BigUint}; use rstest::rstest; use tycho_common::{ @@ -1014,6 +1014,62 @@ mod tests { assert_eq!(hex_calldata[1288..], expected_swaps); } + #[test] + fn test_split_encoding_strategy_ekubo() { + // ETH ──(EKUBO)──> USDC + + let token_in = Bytes::from(Address::ZERO.as_slice()); + let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC + + let static_attributes = HashMap::from([ + ("fee".to_string(), Bytes::from(0_u64)), + ("tick_spacing".to_string(), Bytes::from(0_u32)), + ("extension".to_string(), Bytes::from("0x51d02a5948496a67827242eabc5725531342527c")), /* Oracle */ + ]); + + let component = ProtocolComponent { + // All Ekubo swaps go through the core contract - not necessary to specify pool id + // for test + protocol_system: "ekubo".to_string(), + static_attributes, + ..Default::default() + }; + + let swap = Swap { + component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let swap_encoder_registry = get_swap_encoder_registry(); + let encoder = + SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, None).unwrap(); + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: token_out, + expected_amount: None, + checked_amount: Some(BigUint::from_str("1").unwrap()), + slippage: None, + // Alice + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let (calldata, _) = encoder + .encode_strategy(solution) + .unwrap(); + + let hex_calldata = encode(&calldata); + println!("{}", hex_calldata); + } + #[test] fn test_split_swap_strategy_encoder_simple_route_no_permit2() { // Performs a single swap from WETH to DAI on a USV2 pool, without permit2 and no grouping diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index 6918b0a..7e12789 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -1,7 +1,8 @@ use crate::encoding::{ errors::EncodingError, evm::swap_encoder::swap_encoders::{ - BalancerV2SwapEncoder, UniswapV2SwapEncoder, UniswapV3SwapEncoder, UniswapV4SwapEncoder, + BalancerV2SwapEncoder, EkuboSwapEncoder, UniswapV2SwapEncoder, UniswapV3SwapEncoder, + UniswapV4SwapEncoder, }, swap_encoder::SwapEncoder, }; @@ -29,6 +30,7 @@ impl SwapEncoderBuilder { "uniswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), "pancakeswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), "uniswap_v4" => Ok(Box::new(UniswapV4SwapEncoder::new(self.executor_address))), + "ekubo" => Ok(Box::new(EkuboSwapEncoder::new(self.executor_address))), _ => Err(EncodingError::FatalError(format!( "Unknown protocol system: {}", self.protocol_system From c678f400571d1b001c98595f68d9a99e0cf4900d Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 31 Mar 2025 17:21:21 +0200 Subject: [PATCH 45/79] fix: Take address for EkuboExecutor init - To match the other executor inits and to not leak information about ICore into the deployment script. - Also fix fork block of tests. This had been temporarily changed to test the Ekubo executor. For some reason, vm.rollFork is not working in this case. Skip the Ekubo integration test for now (we know it runs with the correct block, where the pool we call is actually already deployed) --- foundry/src/executors/EkuboExecutor.sol | 4 ++-- foundry/test/TychoRouter.t.sol | 3 +++ foundry/test/TychoRouterTestSetup.sol | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index 66e508f..d4175a3 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -25,8 +25,8 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { uint256 constant POOL_DATA_OFFSET = 56; uint256 constant HOP_BYTE_LEN = 52; - constructor(ICore _core) { - core = _core; + constructor(address _core) { + core = ICore(_core); } function swap(uint256 amountIn, bytes calldata data) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 128c97a..38ae918 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1029,6 +1029,9 @@ contract TychoRouterTest is TychoRouterTestSetup { } function testEkuboIntegration() public { + vm.skip(true); + // Test needs to be run on block 22082754 or later + deal(ALICE, 1 ether); uint256 balancerBefore = IERC20(USDC_ADDR).balanceOf(ALICE); diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index e1e62e9..f53b822 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -54,7 +54,7 @@ contract TychoRouterTestSetup is Test, Constants { bytes32 initCodeV3 = USV3_POOL_CODE_INIT_HASH; bytes32 initCodePancakeV3 = PANCAKEV3_POOL_CODE_INIT_HASH; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; - ICore ekuboCore = ICore(0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444); + address ekuboCore = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444; IPoolManager poolManager = IPoolManager(poolManagerAddress); tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); From c0068d456bbd271d1c74797577e0fe514be0fcc7 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 31 Mar 2025 17:42:56 +0200 Subject: [PATCH 46/79] fix: Finalize ekubo executor address --- config/executor_addresses.json | 2 +- foundry/scripts/deploy-executors.js | 3 +++ foundry/src/TychoRouter.sol | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 46db52c..92caa97 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -7,7 +7,7 @@ "pancakeswap_v3": "0x4929B619A8F0D9c06ed0FfD497636580D823F65d", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", - "ekubo": "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9" + "ekubo": "0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index c8809b7..6c0f55b 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -33,6 +33,9 @@ const executors_to_deploy = { // Args: Pool manager {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "BalancerV2Executor", args: []}, + // Args: Ekubo core contract + {exchange: "EkuboExecutor", args: [ + "0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444" ], "base":[ // Args: Factory, Pool Init Code Hash diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index d997588..66c0be6 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -554,8 +554,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { } function locked(uint256) external { - // TODO replace with real executor address once deployed - address executor = address(0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9); + address executor = address(0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806); // slither-disable-next-line controlled-delegatecall,low-level-calls (bool success, bytes memory result) = executor.delegatecall( From 3638ab9a43cf5fbf78571716e206cd127c7ed452 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 31 Mar 2025 15:50:06 +0000 Subject: [PATCH 47/79] chore(release): 0.69.0 [skip ci] ## [0.69.0](https://github.com/propeller-heads/tycho-execution/compare/0.68.2...0.69.0) (2025-03-31) ### Features * Add PancakeSwapV3 support to encoding ([fa024a4](https://github.com/propeller-heads/tycho-execution/commit/fa024a4a6702c7809af31a6ba392338d9368c6d2)) * Pancakeswap V3 support ([d582543](https://github.com/propeller-heads/tycho-execution/commit/d582543057665b737cc0aab5243ccc22db1f0a13)) --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f38b58..b53d427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.69.0](https://github.com/propeller-heads/tycho-execution/compare/0.68.2...0.69.0) (2025-03-31) + + +### Features + +* Add PancakeSwapV3 support to encoding ([fa024a4](https://github.com/propeller-heads/tycho-execution/commit/fa024a4a6702c7809af31a6ba392338d9368c6d2)) +* Pancakeswap V3 support ([d582543](https://github.com/propeller-heads/tycho-execution/commit/d582543057665b737cc0aab5243ccc22db1f0a13)) + ## [0.68.2](https://github.com/propeller-heads/tycho-execution/compare/0.68.1...0.68.2) (2025-03-28) diff --git a/Cargo.lock b/Cargo.lock index 54ec35b..c1762cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.68.2" +version = "0.69.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 00c7abd..c075dba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.68.2" +version = "0.69.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 28f9f244e6343393a96020a85f5e202012d7ca26 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 31 Mar 2025 17:54:35 +0200 Subject: [PATCH 48/79] fix: test setup fix after rebase --- foundry/test/TychoRouterTestSetup.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index f53b822..4f1343f 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -75,13 +75,14 @@ contract TychoRouterTestSetup is Test, Constants { pancakev3Executor = new UniswapV3Executor(factoryPancakeV3, initCodePancakeV3); ekuboExecutor = new EkuboExecutor(ekuboCore); + vm.startPrank(EXECUTOR_SETTER); address[] memory executors = new address[](5); executors[0] = address(usv2Executor); executors[1] = address(usv3Executor); executors[2] = address(pancakev3Executor); executors[3] = address(usv4Executor); - executors[3] = address(ekuboExecutor); + executors[4] = address(ekuboExecutor); tychoRouter.setExecutors(executors); vm.stopPrank(); From 6c35f114e383b7ba93cb44d314a05f69ab15fadd Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 31 Mar 2025 18:38:12 +0200 Subject: [PATCH 49/79] fix: ekubo -> ekubo_v2 This is how it's called in tycho indexer/simulation. --- config/executor_addresses.json | 2 +- src/encoding/evm/swap_encoder/builder.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 92caa97..cf8fc1a 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -7,7 +7,7 @@ "pancakeswap_v3": "0x4929B619A8F0D9c06ed0FfD497636580D823F65d", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", - "ekubo": "0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806" + "ekubo_v2": "0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index 7e12789..65bc317 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -30,7 +30,7 @@ impl SwapEncoderBuilder { "uniswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), "pancakeswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))), "uniswap_v4" => Ok(Box::new(UniswapV4SwapEncoder::new(self.executor_address))), - "ekubo" => Ok(Box::new(EkuboSwapEncoder::new(self.executor_address))), + "ekubo_v2" => Ok(Box::new(EkuboSwapEncoder::new(self.executor_address))), _ => Err(EncodingError::FatalError(format!( "Unknown protocol system: {}", self.protocol_system From 18fa0cc7adfaa46879aa3637c03563c770582b0d Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 31 Mar 2025 18:45:36 +0200 Subject: [PATCH 50/79] fix: ekubo -> ekubo_v2 This is how it's called in tycho indexer/simulation. --- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index b6e4e7f..7ac8fd7 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1030,7 +1030,7 @@ mod tests { let component = ProtocolComponent { // All Ekubo swaps go through the core contract - not necessary to specify pool id // for test - protocol_system: "ekubo".to_string(), + protocol_system: "ekubo_v2".to_string(), static_attributes, ..Default::default() }; From 9b2710554e5f1545cf690375b6d93e44b8cfca49 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:53:15 +0200 Subject: [PATCH 51/79] chore: update `tycho-common` version --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1762cf..bdf5ffe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4317,9 +4317,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tycho-common" -version = "0.63.1" +version = "0.64.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c64749b24bdb5de6050c37ea91420a67a5fe606247fdb29f2e812dac30b42e" +checksum = "1e318a43fab79199deaab2391c83c75724780151c0337b67914ed835ff04b52f" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index c075dba..ebe447b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ clap = { version = "4.5.3", features = ["derive"] } alloy = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true } alloy-sol-types = { version = "0.8.14", optional = true } alloy-primitives = { version = "0.8.9", optional = true } -tycho-common = "0.63.1" +tycho-common = "0.64.1" once_cell = "1.20.2" [dev-dependencies] From 75516122e1a084f86c34e6eaaf43fe5f53a30d96 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 31 Mar 2025 21:01:36 +0200 Subject: [PATCH 52/79] fix: support payCallback method for Ekubo - depending on the pool, either lock or payCallback may be called. --- foundry/src/TychoRouter.sol | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 66c0be6..21d51f9 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -571,4 +571,23 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { ); } } + + function payCallback(uint256, address /*token*/ ) external { + address executor = address(0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806); + + // slither-disable-next-line controlled-delegatecall,low-level-calls + (bool success, bytes memory result) = executor.delegatecall( + abi.encodeWithSelector(ICallback.handleCallback.selector, msg.data) + ); + + if (!success) { + revert( + string( + result.length > 0 + ? result + : abi.encodePacked("Callback failed") + ) + ); + } + } } From 62b882496d28e658cbffa01f5a0b3692298ee22b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 31 Mar 2025 19:09:05 +0000 Subject: [PATCH 53/79] chore(release): 0.70.0 [skip ci] ## [0.70.0](https://github.com/propeller-heads/tycho-execution/compare/0.69.0...0.70.0) (2025-03-31) ### Features * Support Ekubo callback in TychoRouter ([b3078f9](https://github.com/propeller-heads/tycho-execution/commit/b3078f9c7b99b3c0c9f0008b97855b48483f06dc)) ### Bug Fixes * ekubo -> ekubo_v2 ([18fa0cc](https://github.com/propeller-heads/tycho-execution/commit/18fa0cc7adfaa46879aa3637c03563c770582b0d)) * ekubo -> ekubo_v2 ([6c35f11](https://github.com/propeller-heads/tycho-execution/commit/6c35f114e383b7ba93cb44d314a05f69ab15fadd)) * Finalize ekubo executor address ([c0068d4](https://github.com/propeller-heads/tycho-execution/commit/c0068d456bbd271d1c74797577e0fe514be0fcc7)) * support payCallback method for Ekubo ([7551612](https://github.com/propeller-heads/tycho-execution/commit/75516122e1a084f86c34e6eaaf43fe5f53a30d96)) * Take address for EkuboExecutor init ([c678f40](https://github.com/propeller-heads/tycho-execution/commit/c678f400571d1b001c98595f68d9a99e0cf4900d)) * test setup fix after rebase ([28f9f24](https://github.com/propeller-heads/tycho-execution/commit/28f9f244e6343393a96020a85f5e202012d7ca26)) --- CHANGELOG.md | 17 +++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b53d427..19e2c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## [0.70.0](https://github.com/propeller-heads/tycho-execution/compare/0.69.0...0.70.0) (2025-03-31) + + +### Features + +* Support Ekubo callback in TychoRouter ([b3078f9](https://github.com/propeller-heads/tycho-execution/commit/b3078f9c7b99b3c0c9f0008b97855b48483f06dc)) + + +### Bug Fixes + +* ekubo -> ekubo_v2 ([18fa0cc](https://github.com/propeller-heads/tycho-execution/commit/18fa0cc7adfaa46879aa3637c03563c770582b0d)) +* ekubo -> ekubo_v2 ([6c35f11](https://github.com/propeller-heads/tycho-execution/commit/6c35f114e383b7ba93cb44d314a05f69ab15fadd)) +* Finalize ekubo executor address ([c0068d4](https://github.com/propeller-heads/tycho-execution/commit/c0068d456bbd271d1c74797577e0fe514be0fcc7)) +* support payCallback method for Ekubo ([7551612](https://github.com/propeller-heads/tycho-execution/commit/75516122e1a084f86c34e6eaaf43fe5f53a30d96)) +* Take address for EkuboExecutor init ([c678f40](https://github.com/propeller-heads/tycho-execution/commit/c678f400571d1b001c98595f68d9a99e0cf4900d)) +* test setup fix after rebase ([28f9f24](https://github.com/propeller-heads/tycho-execution/commit/28f9f244e6343393a96020a85f5e202012d7ca26)) + ## [0.69.0](https://github.com/propeller-heads/tycho-execution/compare/0.68.2...0.69.0) (2025-03-31) diff --git a/Cargo.lock b/Cargo.lock index bdf5ffe..97b8c42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.69.0" +version = "0.70.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index ebe447b..aabe1da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.69.0" +version = "0.70.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 9256bf87425edbd00371923b0154c3dd32f45496 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:00:48 +0700 Subject: [PATCH 54/79] Remove Ekubo executor support for partial swaps Partial swaps would likely run out-of-gas anyway and removing the support saves a bit more gas --- foundry/src/executors/EkuboExecutor.sol | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index d4175a3..ade58c1 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -120,10 +120,8 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { } function _locked(bytes calldata swapData) internal { - // For partial swaps this is not equivalent to the given input amount - uint128 tokenInDebtAmount = 0; - int128 nextAmountIn = int128(uint128(bytes16(swapData[0:16]))); + uint128 tokenInDebtAmount = uint128(nextAmountIn); address receiver = address(bytes20(swapData[16:36])); address tokenIn = address(bytes20(swapData[36:POOL_DATA_OFFSET])); @@ -153,10 +151,6 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { 0 ); - if (tokenInDebtAmount == 0) { - tokenInDebtAmount = uint128(isToken1 ? delta1 : delta0); - } - nextTokenIn = nextTokenOut; nextAmountIn = -(isToken1 ? delta0 : delta1); From 57aa1c3402da976cfed38db2b46c584162cdbb69 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 1 Apr 2025 21:58:29 +0200 Subject: [PATCH 55/79] fix: update ekubo_v2 executor This was recently redeployed with a fix. --- config/executor_addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index cf8fc1a..522356c 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -7,7 +7,7 @@ "pancakeswap_v3": "0x4929B619A8F0D9c06ed0FfD497636580D823F65d", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", - "ekubo_v2": "0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806" + "ekubo_v2": "0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", From e3d25fcd5ed4e160fff10d5c346b12f0cb3328c6 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 00:10:21 +0200 Subject: [PATCH 56/79] feat: Update ekubo router address - Both during encoding and hardcoded in the TychoRouter. --- foundry/src/TychoRouter.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 21d51f9..d3e7d0f 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -554,7 +554,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { } function locked(uint256) external { - address executor = address(0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806); + address executor = address(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279); // slither-disable-next-line controlled-delegatecall,low-level-calls (bool success, bytes memory result) = executor.delegatecall( @@ -573,7 +573,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { } function payCallback(uint256, address /*token*/ ) external { - address executor = address(0x5E40985A4d4E8DbAd1dc35fFCfacfCde3e3d1806); + address executor = address(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279); // slither-disable-next-line controlled-delegatecall,low-level-calls (bool success, bytes memory result) = executor.delegatecall( From 3835842e88a5ed7c2b540ec199379b548e62c0fe Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 1 Apr 2025 22:55:10 +0000 Subject: [PATCH 57/79] chore(release): 0.71.0 [skip ci] ## [0.71.0](https://github.com/propeller-heads/tycho-execution/compare/0.70.0...0.71.0) (2025-04-01) ### Features * Update ekubo router address ([e3d25fc](https://github.com/propeller-heads/tycho-execution/commit/e3d25fcd5ed4e160fff10d5c346b12f0cb3328c6)) ### Bug Fixes * update ekubo_v2 executor ([57aa1c3](https://github.com/propeller-heads/tycho-execution/commit/57aa1c3402da976cfed38db2b46c584162cdbb69)) --- CHANGELOG.md | 12 ++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19e2c4c..89f3b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [0.71.0](https://github.com/propeller-heads/tycho-execution/compare/0.70.0...0.71.0) (2025-04-01) + + +### Features + +* Update ekubo router address ([e3d25fc](https://github.com/propeller-heads/tycho-execution/commit/e3d25fcd5ed4e160fff10d5c346b12f0cb3328c6)) + + +### Bug Fixes + +* update ekubo_v2 executor ([57aa1c3](https://github.com/propeller-heads/tycho-execution/commit/57aa1c3402da976cfed38db2b46c584162cdbb69)) + ## [0.70.0](https://github.com/propeller-heads/tycho-execution/compare/0.69.0...0.70.0) (2025-03-31) diff --git a/Cargo.lock b/Cargo.lock index 97b8c42..1654f06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.70.0" +version = "0.71.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index aabe1da..a0c6608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.70.0" +version = "0.71.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From d5c589d2c09da8e7f22b40be6e5b236e0eb16645 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 26 Mar 2025 12:42:28 +0100 Subject: [PATCH 58/79] feat: Remove router_address from Solution, set default - The router address can be set when creating the SplitSwapStrategy, which now takes an Option as input during initialization (another breaking interface change) - This change allows us to change our router address in the future with minimal effect on the users. Users should only pass the router address in the split swap initialization if they intend to use their own router for execution. - This change also means that the router address does not need to be passed with the solution, even when using the executor strategy (which was pointless). - I thought of having a router_address() method to set this in the encoder builder - but it seemed too messy, since we don't need the router address for execution for example. We would then potentially unnecessarily load and set the default router address when not needed. It is also not even used at the highest level EVMTychoEncoder, so it makes more sense for it to be directly associated with the swap strategy instead. - Users will now not be able to encode for different routers without re-initializing the strategy. We assumed this use case to be very rare and not worth supporting. --- config/router_addresses.json | 4 + examples/encoding-example/main.rs | 3 - src/encoding/evm/constants.rs | 1 + src/encoding/evm/encoder_builder.rs | 3 +- .../evm/strategy_encoder/strategy_encoders.rs | 141 ++++++++++++------ src/encoding/evm/tycho_encoder.rs | 1 - src/encoding/models.rs | 5 +- 7 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 config/router_addresses.json diff --git a/config/router_addresses.json b/config/router_addresses.json new file mode 100644 index 0000000..ecc244a --- /dev/null +++ b/config/router_addresses.json @@ -0,0 +1,4 @@ +{ + "ethereum": "0x6512E8f80Ab24e6dD6eB042897898516c3175375", + "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb" +} diff --git a/examples/encoding-example/main.rs b/examples/encoding-example/main.rs index 70b46d9..7ccc0bc 100644 --- a/examples/encoding-example/main.rs +++ b/examples/encoding-example/main.rs @@ -13,8 +13,6 @@ use tycho_execution::encoding::{ fn main() { // Setup variables - let router_address = Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678") - .expect("Failed to create router address"); let swapper_pk = "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") @@ -61,7 +59,6 @@ fn main() { exact_out: false, // it's an exact in solution checked_amount: None, // the amount out will not be checked in execution swaps: vec![simple_swap], - router_address, ..Default::default() }; diff --git a/src/encoding/evm/constants.rs b/src/encoding/evm/constants.rs index d742100..0b6eb48 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -1,6 +1,7 @@ use std::{collections::HashSet, sync::LazyLock}; pub const DEFAULT_EXECUTORS_JSON: &str = include_str!("../../../config/executor_addresses.json"); +pub const DEFAULT_ROUTERS_JSON: &str = include_str!("../../../config/router_addresses.json"); /// These protocols support the optimization of grouping swaps. /// diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index 7a13804..f39d6e6 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -57,7 +57,7 @@ impl EVMEncoderBuilder { let swap_encoder_registry = SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?; let strategy = - Box::new(SplitSwapStrategyEncoder::new(chain, swap_encoder_registry, None)?); + Box::new(SplitSwapStrategyEncoder::new(chain, swap_encoder_registry, None, None)?); Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), @@ -83,6 +83,7 @@ impl EVMEncoderBuilder { chain, swap_encoder_registry, Some(swapper_pk), + None, )?); Ok(EVMEncoderBuilder { chain: Some(chain), diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 7ac8fd7..1dcc3bd 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1,4 +1,7 @@ -use std::{collections::HashSet, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + str::FromStr, +}; use alloy_primitives::{aliases::U24, U256, U8}; use alloy_sol_types::SolValue; @@ -8,6 +11,7 @@ use crate::encoding::{ errors::EncodingError, evm::{ approvals::permit2::Permit2, + constants::DEFAULT_ROUTERS_JSON, strategy_encoder::{group_swaps::group_swaps, strategy_validators::SplitSwapValidator}, swap_encoder::swap_encoder_registry::SwapEncoderRegistry, utils::{ @@ -68,6 +72,7 @@ pub trait EVMStrategyEncoder: StrategyEncoder { /// * `wrapped_address`: Address of the chain's wrapped token /// * `split_swap_validator`: SplitSwapValidator, responsible for checking validity of split swap /// solutions +/// * `router_address`: Address of the router to be used to execute swaps #[derive(Clone)] pub struct SplitSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, @@ -76,6 +81,7 @@ pub struct SplitSwapStrategyEncoder { native_address: Bytes, wrapped_address: Bytes, split_swap_validator: SplitSwapValidator, + router_address: Bytes, } impl SplitSwapStrategyEncoder { @@ -83,6 +89,7 @@ impl SplitSwapStrategyEncoder { blockchain: tycho_common::models::Chain, swap_encoder_registry: SwapEncoderRegistry, swapper_pk: Option, + router_address: Option, ) -> Result { let chain = Chain::from(blockchain); let (permit2, selector) = if let Some(swapper_pk) = swapper_pk { @@ -93,6 +100,21 @@ impl SplitSwapStrategyEncoder { "swap(uint256,address,address,uint256,bool,bool,uint256,address,bytes)".to_string(), ) }; + + let tycho_router_address; + if let Some(address) = router_address { + tycho_router_address = address; + } else { + let default_routers: HashMap = + serde_json::from_str(DEFAULT_ROUTERS_JSON)?; + tycho_router_address = default_routers + .get(&chain.name) + .ok_or(EncodingError::FatalError( + "No default router address found for chain".to_string(), + ))? + .to_owned(); + } + Ok(Self { permit2, selector, @@ -100,6 +122,7 @@ impl SplitSwapStrategyEncoder { native_address: chain.native_token()?, wrapped_address: chain.wrapped_token()?, split_swap_validator: SplitSwapValidator, + router_address: tycho_router_address, }) } } @@ -182,9 +205,9 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { let mut grouped_protocol_data: Vec = vec![]; for swap in grouped_swap.swaps.iter() { let encoding_context = EncodingContext { - receiver: solution.router_address.clone(), + receiver: self.router_address.clone(), exact_out: solution.exact_out, - router_address: solution.router_address.clone(), + router_address: self.router_address.clone(), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; @@ -213,7 +236,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { }; let method_calldata = if let Some(permit2) = self.permit2.clone() { let (permit, signature) = permit2.get_permit( - &solution.router_address, + &self.router_address, &solution.sender, &solution.given_token, &solution.given_amount, @@ -248,7 +271,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { }; let contract_interaction = encode_input(&self.selector, method_calldata); - Ok((contract_interaction, solution.router_address)) + Ok((contract_interaction, self.router_address.clone())) } fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box> { @@ -293,7 +316,6 @@ impl StrategyEncoder for ExecutorStrategyEncoder { .ok_or_else(|| EncodingError::FatalError("Swap grouping failed".to_string()))?; let receiver = solution.receiver; - let router_address = solution.router_address; let swap_encoder = self .get_swap_encoder(&grouped_swap.protocol_system) @@ -309,7 +331,7 @@ impl StrategyEncoder for ExecutorStrategyEncoder { let encoding_context = EncodingContext { receiver: receiver.clone(), exact_out: solution.exact_out, - router_address: router_address.clone(), + router_address: Bytes::from("0x0000000000000000000000000000000000000000"), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; @@ -396,7 +418,6 @@ mod tests { // The receiver was generated with `makeAddr("bob") using forge` receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap], - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None, }; @@ -452,7 +473,6 @@ mod tests { sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(), receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap.clone(), swap], - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None, }; @@ -521,7 +541,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_usdc_eth, swap_eth_pepe], ..Default::default() }; @@ -606,9 +625,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: weth, @@ -619,7 +642,6 @@ mod tests { checked_amount, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], ..Default::default() }; @@ -707,9 +729,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: eth(), @@ -719,7 +745,6 @@ mod tests { checked_amount: Some(BigUint::from_str("2659881924818443699787").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], native_action: Some(NativeAction::Wrap), ..Default::default() @@ -756,9 +781,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: dai, @@ -768,7 +797,6 @@ mod tests { checked_amount: Some(BigUint::from_str("1_000000000000000000").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], native_action: Some(NativeAction::Unwrap), ..Default::default() @@ -846,9 +874,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: weth, @@ -858,7 +890,6 @@ mod tests { checked_amount: Some(BigUint::from_str("26173932").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc], ..Default::default() }; @@ -927,9 +958,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: usdc, @@ -940,7 +975,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_usdc_eth, swap_eth_pepe], ..Default::default() }; @@ -1094,8 +1128,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, None).unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + None, + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: weth, @@ -1106,7 +1145,6 @@ mod tests { checked_amount, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], ..Default::default() }; @@ -1183,9 +1221,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1197,7 +1239,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_eth_pepe], ..Default::default() }; @@ -1247,9 +1288,13 @@ mod tests { }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1261,7 +1306,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_usdc_eth], ..Default::default() }; @@ -1331,9 +1375,13 @@ mod tests { }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1345,7 +1393,6 @@ mod tests { * test */ slippage: None, swaps: vec![swap_usdc_weth, swap_weth_usdc], - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), ..Default::default() @@ -1483,6 +1530,7 @@ mod tests { eth_chain(), swap_encoder_registry, Some(private_key.clone()), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), ) .unwrap(); @@ -1494,7 +1542,6 @@ mod tests { expected_amount: None, checked_amount: Some(BigUint::from_str("99574171").unwrap()), /* Expected output from * test */ - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), slippage: None, @@ -1640,6 +1687,7 @@ mod tests { eth_chain(), swap_encoder_registry, Some(private_key.clone()), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), ) .unwrap(); @@ -1651,7 +1699,6 @@ mod tests { expected_amount: None, checked_amount: Some(BigUint::from_str("99525908").unwrap()), /* Expected output from * test */ - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), slippage: None, diff --git a/src/encoding/evm/tycho_encoder.rs b/src/encoding/evm/tycho_encoder.rs index 5f20b2e..85f541f 100644 --- a/src/encoding/evm/tycho_encoder.rs +++ b/src/encoding/evm/tycho_encoder.rs @@ -249,7 +249,6 @@ mod tests { exact_out: false, given_amount: eth_amount_in.clone(), given_token: eth(), - router_address: Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), swaps: vec![swap], native_action: Some(NativeAction::Wrap), ..Default::default() diff --git a/src/encoding/models.rs b/src/encoding/models.rs index af8a780..04c2c44 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -41,8 +41,6 @@ pub struct Solution { pub checked_amount: Option, /// List of swaps to fulfill the solution. pub swaps: Vec, - /// Address of the router contract to be used for the swaps. - pub router_address: Bytes, /// If set, the corresponding native action will be executed. pub native_action: Option, } @@ -104,7 +102,8 @@ pub struct Transaction { /// /// * `receiver`: Address of the receiver of the out token after the swaps are completed. /// * `exact_out`: true if the solution is a buy order, false if it is a sell order. -/// * `router_address`: Address of the router contract to be used for the swaps. +/// * `router_address`: Address of the router contract to be used for the swaps. Zero address if +/// solution does not require router address. /// * `group_token_in`: Token to be used as the input for the group swap. /// * `group_token_out`: Token to be used as the output for the group swap. #[derive(Clone, Debug)] From 8865e22116dcb8c291caa745de78b4e6241315c8 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 1 Apr 2025 19:55:38 +0200 Subject: [PATCH 59/79] feat: Make EncodingContext.router_address optional --- .../evm/strategy_encoder/strategy_encoders.rs | 4 +-- .../evm/swap_encoder/swap_encoders.rs | 34 ++++++++++++------- src/encoding/models.rs | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 1dcc3bd..aed32da 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -207,7 +207,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { let encoding_context = EncodingContext { receiver: self.router_address.clone(), exact_out: solution.exact_out, - router_address: self.router_address.clone(), + router_address: Some(self.router_address.clone()), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; @@ -331,7 +331,7 @@ impl StrategyEncoder for ExecutorStrategyEncoder { let encoding_context = EncodingContext { receiver: receiver.clone(), exact_out: solution.exact_out, - router_address: Bytes::from("0x0000000000000000000000000000000000000000"), + router_address: None, group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index f119681..6a8ef9d 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -229,13 +229,21 @@ impl SwapEncoder for BalancerV2SwapEncoder { ) -> Result, EncodingError> { let token_approvals_manager = ProtocolApprovalsManager::new()?; let token = bytes_to_address(&swap.token_in)?; - let router_address = bytes_to_address(&encoding_context.router_address)?; - let approval_needed = token_approvals_manager.approval_needed( - token, - router_address, - Address::from_str(&self.vault_address) - .map_err(|_| EncodingError::FatalError("Invalid vault address".to_string()))?, - )?; + let approval_needed: bool; + + if let Some(router_address) = encoding_context.router_address { + let tycho_router_address = bytes_to_address( + &router_address, + )?; + approval_needed = token_approvals_manager.approval_needed( + token, + tycho_router_address, + Address::from_str(&self.vault_address) + .map_err(|_| EncodingError::FatalError("Invalid vault address".to_string()))?, + )?; + } else { + approval_needed = true; + } let component_id = AlloyBytes::from_str(&swap.component.id) .map_err(|_| EncodingError::FatalError("Invalid component ID".to_string()))?; @@ -350,7 +358,7 @@ mod tests { let encoding_context = EncodingContext { receiver: Bytes::from("0x0000000000000000000000000000000000000001"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; @@ -397,7 +405,7 @@ mod tests { let encoding_context = EncodingContext { receiver: Bytes::from("0x0000000000000000000000000000000000000001"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; @@ -445,7 +453,7 @@ mod tests { // The receiver was generated with `makeAddr("bob") using forge` receiver: Bytes::from("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; @@ -503,7 +511,7 @@ mod tests { receiver: Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f"), exact_out: false, // Same as the executor address - router_address: Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f"), + router_address: Some(Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f")), group_token_in: token_in.clone(), group_token_out: token_out.clone(), @@ -567,7 +575,7 @@ mod tests { let encoding_context = EncodingContext { receiver: Bytes::from("0x0000000000000000000000000000000000000001"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: group_token_in.clone(), // Token out is the same as the group token out group_token_out: token_out.clone(), @@ -606,7 +614,7 @@ mod tests { let context = EncodingContext { receiver: receiver_address.clone(), exact_out: false, - router_address: router_address.clone(), + router_address: Some(router_address.clone()), group_token_in: usde_address.clone(), group_token_out: wbtc_address.clone(), }; diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 04c2c44..a8fce6a 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -110,7 +110,7 @@ pub struct Transaction { pub struct EncodingContext { pub receiver: Bytes, pub exact_out: bool, - pub router_address: Bytes, + pub router_address: Option, pub group_token_in: Bytes, pub group_token_out: Bytes, } From b397ddd2beb007d0bed378949d35a9ce5c5b76c9 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 1 Apr 2025 19:59:58 +0200 Subject: [PATCH 60/79] fix: fix Solution.router_address for Ekubo --- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 10 +++++++--- src/encoding/evm/swap_encoder/swap_encoders.rs | 8 +++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index aed32da..e6ff1fd 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1077,8 +1077,13 @@ mod tests { }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, None).unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + None, + Some(Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1091,7 +1096,6 @@ mod tests { // Alice sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], ..Default::default() }; diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 6a8ef9d..ad6ffc9 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -232,9 +232,7 @@ impl SwapEncoder for BalancerV2SwapEncoder { let approval_needed: bool; if let Some(router_address) = encoding_context.router_address { - let tycho_router_address = bytes_to_address( - &router_address, - )?; + let tycho_router_address = bytes_to_address(&router_address)?; approval_needed = token_approvals_manager.approval_needed( token, tycho_router_address, @@ -742,7 +740,7 @@ mod tests { group_token_in: token_in.clone(), group_token_out: token_out.clone(), exact_out: false, - router_address: Bytes::default(), + router_address: Some(Bytes::default()), }; let encoder = EkuboSwapEncoder::new(String::default()); @@ -780,7 +778,7 @@ mod tests { group_token_in: group_token_in.clone(), group_token_out: group_token_out.clone(), exact_out: false, - router_address: Bytes::default(), + router_address: Some(Bytes::default()), }; let first_swap = Swap { From 99adaa97966e1863d0effe430ddc38e6e23ed7ac Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 00:54:56 +0200 Subject: [PATCH 61/79] chore: Update latest router address in router_addresses.json --- config/router_addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/router_addresses.json b/config/router_addresses.json index ecc244a..bac76ba 100644 --- a/config/router_addresses.json +++ b/config/router_addresses.json @@ -1,4 +1,4 @@ { - "ethereum": "0x6512E8f80Ab24e6dD6eB042897898516c3175375", + "ethereum": "0x0178f471f219737c51d6005556d2f44de011a08a", "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb" } From c336a28905a1829da78997ea2126849fdabbcfc6 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 01:12:02 +0200 Subject: [PATCH 62/79] feat: Support manual router address setting in builder --- src/encoding/evm/encoder_builder.rs | 30 ++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index f39d6e6..e20c148 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -1,4 +1,4 @@ -use tycho_common::models::Chain; +use tycho_common::{models::Chain, Bytes}; use crate::encoding::{ errors::EncodingError, @@ -17,6 +17,7 @@ pub struct EVMEncoderBuilder { strategy: Option>, chain: Option, executors_file_path: Option, + router_address: Option, } impl Default for EVMEncoderBuilder { @@ -27,7 +28,12 @@ impl Default for EVMEncoderBuilder { impl EVMEncoderBuilder { pub fn new() -> Self { - EVMEncoderBuilder { chain: None, strategy: None, executors_file_path: None } + EVMEncoderBuilder { + chain: None, + strategy: None, + executors_file_path: None, + router_address: None, + } } pub fn chain(mut self, chain: Chain) -> Self { self.chain = Some(chain); @@ -41,6 +47,13 @@ impl EVMEncoderBuilder { self } + /// Sets the `router_address` manually. + /// If it's not set, the default router address will be used (config/router_addresses.json) + pub fn router_address(mut self, router_address: Bytes) -> Self { + self.router_address = Some(router_address); + self + } + /// Sets the `strategy_encoder` manually. /// /// **Note**: This method should not be used in combination with `tycho_router` or @@ -56,12 +69,17 @@ impl EVMEncoderBuilder { if let Some(chain) = self.chain { let swap_encoder_registry = SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?; - let strategy = - Box::new(SplitSwapStrategyEncoder::new(chain, swap_encoder_registry, None, None)?); + let strategy = Box::new(SplitSwapStrategyEncoder::new( + chain, + swap_encoder_registry, + None, + self.router_address.clone(), + )?); Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), executors_file_path: self.executors_file_path, + router_address: self.router_address, }) } else { Err(EncodingError::FatalError( @@ -83,12 +101,13 @@ impl EVMEncoderBuilder { chain, swap_encoder_registry, Some(swapper_pk), - None, + self.router_address.clone(), )?); Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), executors_file_path: self.executors_file_path, + router_address: self.router_address, }) } else { Err(EncodingError::FatalError( @@ -108,6 +127,7 @@ impl EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), executors_file_path: self.executors_file_path, + router_address: self.router_address, }) } else { Err(EncodingError::FatalError( From 4bcba64347a1ee053caa29baef16412fdaea5899 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Apr 2025 09:33:01 +0000 Subject: [PATCH 63/79] chore(release): 0.72.0 [skip ci] ## [0.72.0](https://github.com/propeller-heads/tycho-execution/compare/0.71.0...0.72.0) (2025-04-02) ### Features * Make EncodingContext.router_address optional ([8865e22](https://github.com/propeller-heads/tycho-execution/commit/8865e22116dcb8c291caa745de78b4e6241315c8)) * Remove router_address from Solution, set default ([d5c589d](https://github.com/propeller-heads/tycho-execution/commit/d5c589d2c09da8e7f22b40be6e5b236e0eb16645)) * Support manual router address setting in builder ([c336a28](https://github.com/propeller-heads/tycho-execution/commit/c336a28905a1829da78997ea2126849fdabbcfc6)) ### Bug Fixes * fix Solution.router_address for Ekubo ([b397ddd](https://github.com/propeller-heads/tycho-execution/commit/b397ddd2beb007d0bed378949d35a9ce5c5b76c9)) --- CHANGELOG.md | 14 ++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89f3b0e..e061741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## [0.72.0](https://github.com/propeller-heads/tycho-execution/compare/0.71.0...0.72.0) (2025-04-02) + + +### Features + +* Make EncodingContext.router_address optional ([8865e22](https://github.com/propeller-heads/tycho-execution/commit/8865e22116dcb8c291caa745de78b4e6241315c8)) +* Remove router_address from Solution, set default ([d5c589d](https://github.com/propeller-heads/tycho-execution/commit/d5c589d2c09da8e7f22b40be6e5b236e0eb16645)) +* Support manual router address setting in builder ([c336a28](https://github.com/propeller-heads/tycho-execution/commit/c336a28905a1829da78997ea2126849fdabbcfc6)) + + +### Bug Fixes + +* fix Solution.router_address for Ekubo ([b397ddd](https://github.com/propeller-heads/tycho-execution/commit/b397ddd2beb007d0bed378949d35a9ce5c5b76c9)) + ## [0.71.0](https://github.com/propeller-heads/tycho-execution/compare/0.70.0...0.71.0) (2025-04-01) diff --git a/Cargo.lock b/Cargo.lock index 1654f06..191096c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.71.0" +version = "0.72.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index a0c6608..189a01c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.71.0" +version = "0.72.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 1f6f1a4236d577e57f50d063a81e9a1ed801a6dd Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 11:56:26 +0200 Subject: [PATCH 64/79] feat: Add router_address to cli --- src/bin/tycho-encode.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 08d157c..8cc3bd6 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,7 +1,7 @@ use std::io::{self, Read}; use clap::{Parser, Subcommand}; -use tycho_common::models::Chain; +use tycho_common::{hex_bytes::Bytes, models::Chain}; use tycho_execution::encoding::{ evm::encoder_builder::EVMEncoderBuilder, models::Solution, tycho_encoder::TychoEncoder, }; @@ -44,6 +44,8 @@ pub struct Cli { pub command: Commands, #[arg(short, long)] executors_file_path: Option, + #[arg(short, long)] + router_address: Option, } #[derive(Subcommand)] @@ -79,6 +81,9 @@ fn main() -> Result<(), Box> { if let Some(config_path) = cli.executors_file_path { builder = builder.executors_file_path(config_path); } + if let Some(router_address) = cli.router_address { + builder = builder.router_address(router_address); + } builder = match cli.command { Commands::TychoRouter => builder.initialize_tycho_router()?, From 69f4e748b57677e4fc97f2fc68e100df39f3f40d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Apr 2025 10:08:01 +0000 Subject: [PATCH 65/79] chore(release): 0.73.0 [skip ci] ## [0.73.0](https://github.com/propeller-heads/tycho-execution/compare/0.72.0...0.73.0) (2025-04-02) ### Features * Add router_address to cli ([1f6f1a4](https://github.com/propeller-heads/tycho-execution/commit/1f6f1a4236d577e57f50d063a81e9a1ed801a6dd)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e061741..95f9cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.73.0](https://github.com/propeller-heads/tycho-execution/compare/0.72.0...0.73.0) (2025-04-02) + + +### Features + +* Add router_address to cli ([1f6f1a4](https://github.com/propeller-heads/tycho-execution/commit/1f6f1a4236d577e57f50d063a81e9a1ed801a6dd)) + ## [0.72.0](https://github.com/propeller-heads/tycho-execution/compare/0.71.0...0.72.0) (2025-04-02) diff --git a/Cargo.lock b/Cargo.lock index 191096c..fe18edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.72.0" +version = "0.73.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 189a01c..bdf9631 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.72.0" +version = "0.73.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From d05e1183d4e85eb57c139b1fd5411833efea92fc Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 15:49:08 +0200 Subject: [PATCH 66/79] feat: Unichain deployment --- config/executor_addresses.json | 5 +++++ config/router_addresses.json | 3 ++- foundry/hardhat.config.js | 21 ++++++++++++++++++++- foundry/scripts/deploy-executors.js | 15 +++++++++++++++ foundry/scripts/deploy-router.js | 4 ++++ foundry/scripts/roles.json | 15 +++++++++++++++ src/bin/tycho-encode.rs | 1 - 7 files changed, 61 insertions(+), 3 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 522356c..6943a1d 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -24,5 +24,10 @@ }, "tenderly_base": { "uniswap_v3": "0x7c7E06d7317e620a185078e236879D2a87fC8d22" + }, + "unichain": { + "uniswap_v2": "0x2E80FBD880379744b46C2aB7249D2949E6696353", + "uniswap_v3": "0x7ba9149EaCC5801279FF8B4d7ECa020bbef18F90", + "uniswap_v4": "0x475EB503D630C2919DC8B24cB6603E17e69Bfe35" } } diff --git a/config/router_addresses.json b/config/router_addresses.json index bac76ba..391aa56 100644 --- a/config/router_addresses.json +++ b/config/router_addresses.json @@ -1,4 +1,5 @@ { "ethereum": "0x0178f471f219737c51d6005556d2f44de011a08a", - "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb" + "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb", + "unichain": "0x9BDC3bE75440dBE563527CB39BB11cFBD1E21b09" } diff --git a/foundry/hardhat.config.js b/foundry/hardhat.config.js index f1395ae..ad1c611 100644 --- a/foundry/hardhat.config.js +++ b/foundry/hardhat.config.js @@ -35,6 +35,11 @@ module.exports = { url: process.env.RPC_URL, accounts: [process.env.PRIVATE_KEY], chainId: 8453 + }, + unichain: { + url: process.env.RPC_URL, + accounts: [process.env.PRIVATE_KEY], + chainId: 130 } }, @@ -45,6 +50,20 @@ module.exports = { }, etherscan: { - apiKey: process.env.BLOCKCHAIN_EXPLORER_API_KEY, + apiKey: { + unichain: process.env.BLOCKCHAIN_EXPLORER_API_KEY, + base: process.env.BLOCKCHAIN_EXPLORER_API_KEY, + ethereum: process.env.BLOCKCHAIN_EXPLORER_API_KEY, + }, + customChains: [ + { + network: "unichain", + chainId: 130, + urls: { + apiURL: "https://api.uniscan.xyz/api", + browserURL: "https://www.uniscan.xyz/" + } + } + ] } }; diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index 6c0f55b..fa32960 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -36,6 +36,7 @@ const executors_to_deploy = { // Args: Ekubo core contract {exchange: "EkuboExecutor", args: [ "0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444" + ]} ], "base":[ // Args: Factory, Pool Init Code Hash @@ -67,6 +68,20 @@ const executors_to_deploy = { {exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]}, {exchange: "BalancerV2Executor", args: []}, ], + "unichain":[ + // Args: Factory, Pool Init Code Hash + {exchange: "UniswapV2Executor", args: [ + "0x1f98400000000000000000000000000000000002", + "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" + ]}, + // USV3 - Args: Factory, Pool Init Code Hash + {exchange: "UniswapV3Executor", args: [ + "0x1f98400000000000000000000000000000000003", + "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" + ]}, + // Args: Pool manager + {exchange: "UniswapV4Executor", args: ["0x1f98400000000000000000000000000000000004"]}, + ], } async function main() { diff --git a/foundry/scripts/deploy-router.js b/foundry/scripts/deploy-router.js index 87e0f15..103fd0d 100644 --- a/foundry/scripts/deploy-router.js +++ b/foundry/scripts/deploy-router.js @@ -13,6 +13,10 @@ async function main() { // permit2 address is the same as on ethereum permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; weth = "0x4200000000000000000000000000000000000006"; + } else if (network === "unichain") { + // permit2 address is the same as on ethereum + permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; + weth = "0x4200000000000000000000000000000000000006"; } else { throw new Error(`Unsupported network: ${network}`); } diff --git a/foundry/scripts/roles.json b/foundry/scripts/roles.json index 3a7ad7a..2d477fe 100644 --- a/foundry/scripts/roles.json +++ b/foundry/scripts/roles.json @@ -58,5 +58,20 @@ "FUND_RESCUER_ROLE": [ "0xb0A77f867Fcec1e9b271Ee17354bC6bBC0dD5662" ] + }, + "unichain": { + "EXECUTOR_SETTER_ROLE": [ + "0x810A00Fa9287700871ba0f870Cd89D7Eac08D48C" + ], + "FEE_SETTER_ROLE": [], + "PAUSER_ROLE": [ + "0x810A00Fa9287700871ba0f870Cd89D7Eac08D48C" + ], + "UNPAUSER_ROLE": [ + "0x810A00Fa9287700871ba0f870Cd89D7Eac08D48C" + ], + "FUND_RESCUER_ROLE": [ + "0x810A00Fa9287700871ba0f870Cd89D7Eac08D48C" + ] } } \ No newline at end of file diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 8cc3bd6..c0216fa 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -35,7 +35,6 @@ use tycho_execution::encoding::{ /// "token_out": "0x...", /// "split": 0.0 /// }], -/// "router_address": "0x..." /// } /// ``` #[command(author, version, about, long_about = None)] From 4878229e1d60a317b0f2d97ea57886e64cc70cb5 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 16:06:26 +0200 Subject: [PATCH 67/79] fix: Set native and wrapped tokens for Unichain --- src/encoding/models.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/encoding/models.rs b/src/encoding/models.rs index a8fce6a..43af861 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -148,6 +148,7 @@ impl Chain { self.decode_hex("0000000000000000000000000000000000000000", decode_err_msg) } 324 => self.decode_hex("000000000000000000000000000000000000800A", decode_err_msg), + 130 => self.decode_hex("0000000000000000000000000000000000000000", decode_err_msg), _ => Err(EncodingError::InvalidInput(format!( "Native token not set for chain {:?}. Double check the chain is supported.", self.name @@ -162,6 +163,7 @@ impl Chain { 8453 => self.decode_hex("4200000000000000000000000000000000000006", decode_err_msg), 324 => self.decode_hex("5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", decode_err_msg), 42161 => self.decode_hex("82aF49447D8a07e3bd95BD0d56f35241523fBab1", decode_err_msg), + 130 => self.decode_hex("4200000000000000000000000000000000000006", decode_err_msg), _ => Err(EncodingError::InvalidInput(format!( "Wrapped token not set for chain {:?}. Double check the chain is supported.", self.name From 6cdca8381e711bc01b7573dd25cafc9aa057aac3 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 1 Apr 2025 13:14:58 +0100 Subject: [PATCH 68/79] feat: Fix rollFork usage for Ekubo test - Created methods to deploy the router and the executors. Whenever we use rollFork we need to redeploy everything! Notice that the addresses will be different then! - Created a test_executor_addresses.json to be used in the encoding tests, this way the calldata for the integration tests is already correct and we don't need to do any manual replacing (this was annoying). The addresses in this file match with the addresses used in the solidity tests --- don't change below this line --- ENG-4411 Took 1 hour 9 minutes Took 28 minutes Took 4 minutes Took 45 minutes --- config/test_executor_addresses.json | 12 ++++ foundry/test/TychoRouter.t.sol | 68 ++++++------------- foundry/test/TychoRouterTestSetup.sol | 64 ++++++++++------- .../evm/strategy_encoder/strategy_encoders.rs | 34 ++++++---- 4 files changed, 91 insertions(+), 87 deletions(-) create mode 100644 config/test_executor_addresses.json diff --git a/config/test_executor_addresses.json b/config/test_executor_addresses.json new file mode 100644 index 0000000..4bc8412 --- /dev/null +++ b/config/test_executor_addresses.json @@ -0,0 +1,12 @@ +{ + "ethereum": { + "uniswap_v2": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "sushiswap_v2": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "pancakeswap_v2": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "uniswap_v3": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "pancakeswap_v3": "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9", + "uniswap_v4": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "vm:balancer_v2": "0xc7183455a4C133Ae270771860664b6B7ec320bB1", + "ekubo_v2": "0x2a07706473244BC757E10F2a9E86fB532828afe3" + } +} diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 38ae918..37c1c2f 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -849,11 +849,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_simple` - // but manually replacing the executor address - // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test - // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067e4225a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9c620000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000411fdbe0ac6bdafd51044f24b158235effa29797f468cd4684efa379053d3d15d47ed8b8206e3f6e7349f40aad231cc7e04ed25cbea1ac659b575be8cc168fc2361c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681362ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdcf2000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a82e9bdde90314de4b1bf918cc2e8b27da98adcab46e8e99d4e77472a572d6381837e9453095f4cc5e9b25691b678288174e547e040a67d12b36ddfdd1e672d21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); vm.stopPrank(); @@ -874,9 +871,6 @@ contract TychoRouterTest is TychoRouterTestSetup { 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` - // but manually replacing the executor address - // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test - // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( hex"0a83cb080000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); @@ -902,10 +896,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_usv4` - // and ensuring that the encoded executor address is the one in this test - // `f62849f9a0b5bf2913b396098f7c7019b51a820a` (bool success,) = tychoRouterAddr.call( - hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000067e4237600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9d7e00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004166b5d3bb274c323e08eeba45d308cc9c11216f9aaafad2a22e94b94fec39293e5480f65f6238d7c8f1e8177f39118373e1041b0ab3a674d3041d119bdb6bc39c1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c008a0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000" + hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006813635000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd58000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a9d2d60e4e7751afcad957b3374d346882998bd46b7ba1c1194fde0e834ed6686c33c9588e7cf395d5cfc92b0c03d834e4087f4e8f64f0ff7579e4f1f93bb5051b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007800760001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000" ); vm.stopPrank(); @@ -927,10 +919,8 @@ contract TychoRouterTest is TychoRouterTestSetup { uint256 balancerBefore = IERC20(PEPE_ADDR).balanceOf(ALICE); // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in` - // and ensuring that the encoded executor address is the one in this test - // `f62849f9a0b5bf2913b396098f7c7019b51a820a` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f01a7800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c894800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416642950b804a47a0abcb17d81cc2a7967d606e00e8de470e0e7827347658160a28b9892f147248b9bf31aad8faa06181aee0c4a612151e9ef4889991b9930b791b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006813636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd68000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ca3b21ccc343ae30cfa6d1430e52701e379222f7345306e7ad5243760f590da26fb81a316249fdaa0686786c0d5e321718908a2ac4c74949b8657ebd7286d89f1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933016982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000" ); vm.stopPrank(); @@ -956,10 +946,8 @@ contract TychoRouterTest is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_out` - // and ensuring that the encoded executor address is the one in this test - // `f62849f9a0b5bf2913b396098f7c7019b51a820a` (bool success,) = tychoRouterAddr.call( - hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000067f01af000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c894f80000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000417811cd10b02278128a9e4df9ef2e099cff6ad46ec6ead5ba0b70dd1db5749d573cf4a8821a524bd6cc5b61ce0faf69d1d4b1f9233b93a4b203e79668f250b1a71c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c0000000000000000000000000000" + hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006813637700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd7f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c94e2c4c06032716ba6f27c574e6e2aba6742f6c618dce347749aed82be3918754a405c2adf80fc544f8b45596462d6f3d2a2fb353b22e8929fdc4d01f2005761c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c0000" ); vm.stopPrank(); @@ -982,13 +970,9 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - // Encoded solution generated using - // `test_split_swap_strategy_encoder_simple_route_wrap` - // but manually replacing the executor address - // `f6c5be66fff9dc69962d73da0a617a827c382329` with the one in this test - // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` + // Encoded solution generated using `test_split_swap_strategy_encoder_simple_route_wrap` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f0192a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c893320000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000419849ede1f59ad3092a3d8f6b5d7a4d3d854c8013d0a728b8556dc9744ddeed6c7edc4987c7724c280d493ca8dd55dd5aa5f5a66a66d85683f8a5b744908752a21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006813638900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd91000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f6ff7411a7ec76cb7dcafecf5e7f11121b1aa88af505635dc7faae6057e4f44e2859712f58331a14a1624f1e5edf2af80ddd2d90b5453d74df1b1fea10b9a2f91c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); vm.stopPrank(); @@ -1011,13 +995,9 @@ contract TychoRouterTest 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` - // but manually replacing the executor address - // `f6c5be66fff9dc69962d73da0a617a827c382329` with the one in this test - // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` + // Encoded solution generated using `test_split_swap_strategy_encoder_simple_route_unwrap` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbd2fc137a30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067f017d700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c891df00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004181b0d9c7bbf7bd3270e22a7ff337b019b006ea60d9e357035b622bfc8e48126343fa9c1342383d3d072c2ddea2072fd5e447e7b6a4b56f5e7973963d18664e5d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006813615200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdb5a000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a7da748b04674485a5da185055affefc85b6d8fe412accce55b6f67842116f0f7f7130de5d74c68c20e1cedcdf93b8741b9171de2e6a3f2567887382a0712e3f1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000" ); vm.stopPrank(); @@ -1029,8 +1009,14 @@ contract TychoRouterTest is TychoRouterTestSetup { } function testEkuboIntegration() public { - vm.skip(true); // Test needs to be run on block 22082754 or later + // notice that the addresses for the tycho router and the executors are different because we are redeploying + vm.rollFork(22082754); + tychoRouter = deployRouter(); + address[] memory executors = deployExecutors(); + vm.startPrank(EXECUTOR_SETTER); + tychoRouter.setExecutors(executors); + vm.stopPrank(); deal(ALICE, 1 ether); uint256 balancerBefore = IERC20(USDC_ADDR).balanceOf(ALICE); @@ -1038,8 +1024,8 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); // Encoded solution generated using `test_split_encoding_strategy_ekubo` - (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"0a83cb080000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000077007500010000005991a2df15a8f6a256d3ec51e99254cd3fb576a93ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000" + (bool success,) = address(tychoRouter).call{value: 1 ether}( + hex"0a83cb080000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000077007500010000002a07706473244bc757e10f2a9e86fb532828afe31d1499e622d69689cdf9004d05ec547d650ff2110000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000000000000000000000" ); uint256 balancerAfter = IERC20(USDC_ADDR).balanceOf(ALICE); @@ -1068,11 +1054,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_complex` - // but manually replacing the executor address - // `f6c5be66FFf9DC69962d73da0A617a827c382329` with the one in this test - // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f0198700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c8938f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041308a3ba881e23ac794deca324cfd959b808c86bb239b81c9db8873c8392382411f87902e6ceb8e59636d8d6fab4ead1863727f9a2168246c93b678f3ae4ae37b1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160005600028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005602030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005601030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d0139501" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681363a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebddab0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415b7ff43991de10c4c3a0372653891d27eb305ce04228bfe46a7d84a0978063fc4cb05183f19b83511bcb689b002d4f8e170f1d3cd77cf18c638229ccb67e0cac1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160005600028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005602030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005601030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d0139501" ); vm.stopPrank(); @@ -1396,11 +1379,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_cyclic_sequential_swap` - // but manually replacing the executor address - // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` with the one in this test - // `2e234dae75c793f67a35089c9d99245e1c58470b` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f67a8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cef493000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c07077fc73bb0f5129006061288fa0583c101631307377281d6b8f3feb50aa2d564f9948c92e0e4abc3771d592bd2f22ebb18ccf21b270459b05f272251ce1c71b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000" + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681363d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebddda0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000418d58a54a3b8afc5d2e228ce6c5a1ab6b342cb5bfd9a00d57b869a4703ca2bb084d10d21f6842be9652a9ff2392673fbdcb961439ccc962de09f6bc64e5e665fe1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000" ); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294); @@ -1415,11 +1395,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_input_cyclic_swap` - // but manually replacing the executor addresses with the ones in this test - // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` to `2e234dae75c793f67a35089c9d99245e1c58470b` - // `f6c5be66fff9dc69962d73da0a617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6c08700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf3a8f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f248bfa39e6801b4173cd4d61e5e5d0c31942eb3c194785f964a82b2c3e05b4b302bccc0924fa4c4ef90854e42865db11f458d3b6a62afddee833f3eb069cd521b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801005601000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681363ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebddf6000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041358738c580b15c5aeb2cd79615e7405569255d599e45d2d537805c4d403a8ce4198cdde7c328a881afeb2f5dc721c5d13dfae03ded6e8e958a96e303e7fa07e91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801005601000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000" ); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171); @@ -1434,11 +1411,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_output_cyclic_swap` - // but manually replacing the executor addresses with the ones in this test - // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` to `2e234dae75c793f67a35089c9d99245e1c58470b` - // `f6c5be66fff9dc69962d73da0a617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6be9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf389c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c02ad8eceede50085f35ce8e8313ebbac9b379396c6e72a35bb4df0970cbdaaa1a91e6f787641af55b13b926199c844df42fdd2ae7bb287db7e5cc2a8bc1d7f51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006813641000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebde18000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041261a267c7d90a230d7f6d0917652953ef5cdaaabc80234a0c3d39ca20687f5af0b56421d0b0bec01d5ba66dd435d7cd63e95abcea114aa9fef6fe9d77589c12e1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000" ); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908); diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 4f1343f..0cfcc6d 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.26; +import "../src/executors/BalancerV2Executor.sol"; +import "../src/executors/EkuboExecutor.sol"; import "../src/executors/UniswapV2Executor.sol"; import "../src/executors/UniswapV3Executor.sol"; import "../src/executors/UniswapV4Executor.sol"; @@ -8,9 +10,8 @@ import "./Constants.sol"; import "./mock/MockERC20.sol"; import "@src/TychoRouter.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; -import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol"; import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; -import "../src/executors/EkuboExecutor.sol"; +import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol"; contract TychoRouterExposed is TychoRouter { constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {} @@ -39,6 +40,7 @@ contract TychoRouterTestSetup is Test, Constants { UniswapV3Executor public usv3Executor; UniswapV3Executor public pancakev3Executor; UniswapV4Executor public usv4Executor; + BalancerV2Executor public balancerv2Executor; EkuboExecutor public ekuboExecutor; MockERC20[] tokens; @@ -47,6 +49,36 @@ contract TychoRouterTestSetup is Test, Constants { vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.startPrank(ADMIN); + tychoRouter = deployRouter(); + deployDummyContract(); + vm.stopPrank(); + + address[] memory executors = deployExecutors(); + 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) { + tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); + tychoRouterAddr = address(tychoRouter); + tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER); + tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER); + tychoRouter.grantRole(keccak256("PAUSER_ROLE"), PAUSER); + tychoRouter.grantRole(keccak256("UNPAUSER_ROLE"), UNPAUSER); + tychoRouter.grantRole( + keccak256("EXECUTOR_SETTER_ROLE"), EXECUTOR_SETTER + ); + return tychoRouter; + } + + function deployExecutors() public returns (address[] memory) { address factoryV2 = USV2_FACTORY_ETHEREUM; address factoryV3 = USV3_FACTORY_ETHEREUM; address factoryPancakeV3 = PANCAKESWAPV3_DEPLOYER_ETHEREUM; @@ -57,40 +89,22 @@ contract TychoRouterTestSetup is Test, Constants { address ekuboCore = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444; IPoolManager poolManager = IPoolManager(poolManagerAddress); - tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); - tychoRouterAddr = address(tychoRouter); - tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER); - tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER); - tychoRouter.grantRole(keccak256("PAUSER_ROLE"), PAUSER); - tychoRouter.grantRole(keccak256("UNPAUSER_ROLE"), UNPAUSER); - tychoRouter.grantRole( - keccak256("EXECUTOR_SETTER_ROLE"), EXECUTOR_SETTER - ); - deployDummyContract(); - vm.stopPrank(); - usv2Executor = new UniswapV2Executor(factoryV2, initCodeV2); usv3Executor = new UniswapV3Executor(factoryV3, initCodeV3); usv4Executor = new UniswapV4Executor(poolManager); pancakev3Executor = new UniswapV3Executor(factoryPancakeV3, initCodePancakeV3); + balancerv2Executor = new BalancerV2Executor(); ekuboExecutor = new EkuboExecutor(ekuboCore); - vm.startPrank(EXECUTOR_SETTER); - address[] memory executors = new address[](5); + address[] memory executors = new address[](6); executors[0] = address(usv2Executor); executors[1] = address(usv3Executor); executors[2] = address(pancakev3Executor); executors[3] = address(usv4Executor); - executors[4] = address(ekuboExecutor); - 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(); + executors[4] = address(balancerv2Executor); + executors[5] = address(ekuboExecutor); + return executors; } /** diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index e6ff1fd..f736bba 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -385,7 +385,8 @@ mod tests { fn get_swap_encoder_registry() -> SwapEncoderRegistry { let eth_chain = eth_chain(); - SwapEncoderRegistry::new(None, eth_chain).unwrap() + SwapEncoderRegistry::new(Some("config/test_executor_addresses.json".to_string()), eth_chain) + .unwrap() } #[test] @@ -428,7 +429,7 @@ mod tests { let hex_protocol_data = encode(&protocol_data); assert_eq!( executor_address, - Bytes::from_str("0xf6c5be66FFf9DC69962d73da0A617a827c382329").unwrap() + Bytes::from_str("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f").unwrap() ); assert_eq!( hex_protocol_data, @@ -551,7 +552,7 @@ mod tests { let hex_protocol_data = encode(&protocol_data); assert_eq!( executor_address, - Bytes::from_str("0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70").unwrap() + Bytes::from_str("0xf62849f9a0b5bf2913b396098f7c7019b51a820a").unwrap() ); assert_eq!( hex_protocol_data, @@ -692,7 +693,7 @@ mod tests { "01", // token out index "000000", // split // Swap data - "f6c5be66fff9dc69962d73da0a617a827c382329", // executor address + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver @@ -1025,7 +1026,7 @@ mod tests { "01", // token out index "000000", // split // Swap data header - "042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf70", // executor address + "f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address // Protocol data "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in @@ -1081,7 +1082,7 @@ mod tests { eth_chain(), swap_encoder_registry, None, - Some(Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()), + Some(Bytes::from_str("0x1d1499e622D69689cdf9004d05Ec547d650Ff211").unwrap()), ) .unwrap(); @@ -1175,7 +1176,7 @@ mod tests { "01", // token out index "000000", // split // Swap data - "f6c5be66fff9dc69962d73da0a617a827c382329", // executor address + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver @@ -1425,7 +1426,7 @@ mod tests { "00", // token in index "01", // token out index "000000", // split - "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out "0001f4", // pool fee @@ -1435,7 +1436,7 @@ mod tests { "006d", // ple encoded swaps "01", // token in index "00000000", // split - "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out "000bb8", // pool fee @@ -1447,6 +1448,7 @@ mod tests { assert_eq!(hex_calldata[..520], expected_input); assert_eq!(hex_calldata[1288..], expected_swaps); + println!("{}", hex_calldata); } #[test] @@ -1576,7 +1578,7 @@ mod tests { "00", // token in index "01", // token out index "999999", // split - "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out "0001f4", // pool fee @@ -1587,7 +1589,7 @@ mod tests { "00", // token in index "01", // token out index "000000", // split - "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out "000bb8", // pool fee @@ -1598,7 +1600,7 @@ mod tests { "01", // token in index "00", // token out index "000000", // split - "f6c5be66fff9dc69962d73da0a617a827c382329", // executor address, + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address, "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id, "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address @@ -1608,6 +1610,7 @@ mod tests { .join(""); assert_eq!(hex_calldata[..520], expected_input); assert_eq!(hex_calldata[1288..], expected_swaps); + println!("{}", hex_calldata); } #[test] @@ -1734,7 +1737,7 @@ mod tests { "00", // token in index "01", // token out index "000000", // split - "f6c5be66fff9dc69962d73da0a617a827c382329", // executor address + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address @@ -1743,7 +1746,7 @@ mod tests { "01", // token in index "00", // token out index "999999", // split - "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out "0001f4", // pool fee @@ -1754,7 +1757,7 @@ mod tests { "01", // token in index "00", // token out index "000000", // split - "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out "000bb8", // pool fee @@ -1767,5 +1770,6 @@ mod tests { assert_eq!(hex_calldata[..520], expected_input); assert_eq!(hex_calldata[1288..], expected_swaps); + println!("{}", hex_calldata); } } From 5336969df8b06238b65a9c9f1a3458f43b89cb54 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 2 Apr 2025 10:08:17 +0100 Subject: [PATCH 69/79] fix: Fix tests after cherry picking --- don't change below this line --- ENG-4411 Took 1 hour 9 minutes Took 18 minutes --- foundry/test/TychoRouter.t.sol | 10 +++++++--- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 37c1c2f..4fde00a 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -897,7 +897,7 @@ contract TychoRouterTest is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_usv4` (bool success,) = tychoRouterAddr.call( - hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006813635000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd58000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a9d2d60e4e7751afcad957b3374d346882998bd46b7ba1c1194fde0e834ed6686c33c9588e7cf395d5cfc92b0c03d834e4087f4e8f64f0ff7579e4f1f93bb5051b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007800760001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000" + hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006814875700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ed015f0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000413a7c6367c69ac46fc2b633fd53e583b74b20ec9b3ea83b069fe564765560a4cb335af200fd90ddb5f56d11e469c11a97420499f1b3ee0c1db13149a74daa90db1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c008a0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000" ); vm.stopPrank(); @@ -920,7 +920,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006813636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd68000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ca3b21ccc343ae30cfa6d1430e52701e379222f7345306e7ad5243760f590da26fb81a316249fdaa0686786c0d5e321718908a2ac4c74949b8657ebd7286d89f1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933016982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006814877000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ed017800000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004193acc98d79044e8ec1bc3ced832dc679e38ac8c6fe9b5befd1e5e44cb44edb0e365f1c5d6e3ca6590ed1a053f1841aede29e5b573f046387aff794520a0f22581b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000" ); vm.stopPrank(); @@ -947,7 +947,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_out` (bool success,) = tychoRouterAddr.call( - hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006813637700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ebdd7f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c94e2c4c06032716ba6f27c574e6e2aba6742f6c618dce347749aed82be3918754a405c2adf80fc544f8b45596462d6f3d2a2fb353b22e8929fdc4d01f2005761c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c0000" + hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006814878000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067ed018800000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004190134d2d142caff6dbea417292a15685119bd676b2b73bad35fe39f720f7c3163f16d057327499019506b6f690a3916fd3375c579c9cb814113b1516187380531b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c0000000000000000000000000000" ); vm.stopPrank(); @@ -1018,6 +1018,10 @@ contract TychoRouterTest is TychoRouterTestSetup { tychoRouter.setExecutors(executors); vm.stopPrank(); + // TEMPORARY while the Ekubo executor address is hardcoded in TychoRouter + // This allows us to change the code at that address to be the testing executor code + vm.etch(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279, 0x2a07706473244BC757E10F2a9E86fB532828afe3.code); + deal(ALICE, 1 ether); uint256 balancerBefore = IERC20(USDC_ADDR).balanceOf(ALICE); diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index f736bba..9359a70 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -564,7 +564,7 @@ mod tests { // zero for one "00", // executor address - "042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf70", + "f62849f9a0b5bf2913b396098f7c7019b51a820a", // first pool intermediary token (ETH) "0000000000000000000000000000000000000000", // fee @@ -1031,7 +1031,7 @@ mod tests { "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in "00", // zero2one - "042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf70", // executor address + "f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address // First pool params "0000000000000000000000000000000000000000", // intermediary token (ETH) "000bb8", // fee @@ -1047,6 +1047,7 @@ mod tests { assert_eq!(hex_calldata[..520], expected_input); assert_eq!(hex_calldata[1288..], expected_swaps); + println!("{}", hex_calldata); } #[test] From f514871e6141dbc817bb4d7c8836941b34ac4dad Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Apr 2025 15:19:00 +0000 Subject: [PATCH 70/79] chore(release): 0.74.0 [skip ci] ## [0.74.0](https://github.com/propeller-heads/tycho-execution/compare/0.73.0...0.74.0) (2025-04-02) ### Features * Fix rollFork usage for Ekubo test ([6cdca83](https://github.com/propeller-heads/tycho-execution/commit/6cdca8381e711bc01b7573dd25cafc9aa057aac3)) ### Bug Fixes * Fix tests after cherry picking ([5336969](https://github.com/propeller-heads/tycho-execution/commit/5336969df8b06238b65a9c9f1a3458f43b89cb54)) --- CHANGELOG.md | 12 ++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f9cf2..e5594d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [0.74.0](https://github.com/propeller-heads/tycho-execution/compare/0.73.0...0.74.0) (2025-04-02) + + +### Features + +* Fix rollFork usage for Ekubo test ([6cdca83](https://github.com/propeller-heads/tycho-execution/commit/6cdca8381e711bc01b7573dd25cafc9aa057aac3)) + + +### Bug Fixes + +* Fix tests after cherry picking ([5336969](https://github.com/propeller-heads/tycho-execution/commit/5336969df8b06238b65a9c9f1a3458f43b89cb54)) + ## [0.73.0](https://github.com/propeller-heads/tycho-execution/compare/0.72.0...0.73.0) (2025-04-02) diff --git a/Cargo.lock b/Cargo.lock index fe18edd..dd8cc6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.73.0" +version = "0.74.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index bdf9631..168b2fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.73.0" +version = "0.74.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 4e34f45bf420a0d6ab2fe1717d9a94495d6daf12 Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:28:11 +0700 Subject: [PATCH 71/79] Remove balanceOf checks in EkuboExecutor & remove ICallback impl --- foundry/src/TychoRouter.sol | 14 +- foundry/src/executors/EkuboExecutor.sol | 168 ++++++++---------------- 2 files changed, 66 insertions(+), 116 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index d3e7d0f..2de02f7 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -557,9 +557,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address executor = address(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279); // slither-disable-next-line controlled-delegatecall,low-level-calls - (bool success, bytes memory result) = executor.delegatecall( - abi.encodeWithSelector(ICallback.handleCallback.selector, msg.data) - ); + (bool success, bytes memory result) = executor.delegatecall(msg.data); if (!success) { revert( @@ -570,15 +568,19 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { ) ); } + + // slither-disable-next-line assembly + assembly ("memory-safe") { + // Propagate the swappedAmount + return(add(result, 32), 16) + } } function payCallback(uint256, address /*token*/ ) external { address executor = address(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279); // slither-disable-next-line controlled-delegatecall,low-level-calls - (bool success, bytes memory result) = executor.delegatecall( - abi.encodeWithSelector(ICallback.handleCallback.selector, msg.data) - ); + (bool success, bytes memory result) = executor.delegatecall(msg.data); if (!success) { revert( diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index ade58c1..eb5b7d4 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.26; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IExecutor} from "@interfaces/IExecutor.sol"; -import {ICallback} from "@interfaces/ICallback.sol"; import {ICore} from "@ekubo/interfaces/ICore.sol"; import {ILocker, IPayer} from "@ekubo/interfaces/IFlashAccountant.sol"; import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol"; @@ -12,17 +11,14 @@ import {LibBytes} from "@solady/utils/LibBytes.sol"; import {Config, EkuboPoolKey} from "@ekubo/types/poolKey.sol"; import {MAX_SQRT_RATIO, MIN_SQRT_RATIO} from "@ekubo/types/sqrtRatio.sol"; -contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { +contract EkuboExecutor is IExecutor, ILocker, IPayer { error EkuboExecutor__InvalidDataLength(); error EkuboExecutor__CoreOnly(); error EkuboExecutor__UnknownCallback(); ICore immutable core; - bytes4 constant LOCKED_SELECTOR = 0xb45a3c0e; // locked(uint256) - bytes4 constant PAY_CALLBACK_SELECTOR = 0x599d0714; // payCallback(uint256,address) - - uint256 constant POOL_DATA_OFFSET = 56; + uint256 constant POOL_DATA_OFFSET = 92; uint256 constant HOP_BYTE_LEN = 52; constructor(address _core) { @@ -36,68 +32,67 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { { if (data.length < 92) revert EkuboExecutor__InvalidDataLength(); - uint256 tokenOutOffset = data.length - HOP_BYTE_LEN; - address tokenOut = - address(bytes20(LibBytes.loadCalldata(data, tokenOutOffset))); - - uint256 tokenOutBalanceBefore = _balanceOf(tokenOut); - // amountIn must be at most type(int128).MAX - _lock(bytes.concat(bytes16(uint128(amountIn)), data)); - - uint256 tokenOutBalanceAfter = _balanceOf(tokenOut); - - // It would be better if we could somehow pass back the swapped amount from the lock but the interface doesn't offer that capability. - // Note that the current approach also prevents arbs that return less than their input because of arithmetic underflow. - calculatedAmount = tokenOutBalanceAfter - tokenOutBalanceBefore; + calculatedAmount = uint256(_lock(bytes.concat(bytes16(uint128(amountIn)), data))); } - // We can't use the return value here since it won't get propagated (see Dispatcher.sol:_handleCallback) - function handleCallback(bytes calldata raw) - external - returns (bytes memory) - { - verifyCallback(raw); - - // Without selector and locker id - bytes calldata stripped = raw[36:]; - - bytes4 selector = bytes4(raw[:4]); - - if (selector == LOCKED_SELECTOR) { - _locked(stripped); - } else if (selector == PAY_CALLBACK_SELECTOR) { - _payCallback(stripped); - } else { - revert EkuboExecutor__UnknownCallback(); - } - - return ""; - } - - function verifyCallback(bytes calldata) public view coreOnly {} - function locked(uint256) external coreOnly { - // Without selector and locker id - _locked(msg.data[36:]); + int128 nextAmountIn = int128(uint128(bytes16(msg.data[36:52]))); + uint128 tokenInDebtAmount = uint128(nextAmountIn); + + address receiver = address(bytes20(msg.data[52:72])); + address tokenIn = address(bytes20(msg.data[72:POOL_DATA_OFFSET])); + + address nextTokenIn = tokenIn; + + uint256 hopsLength = (msg.data.length - POOL_DATA_OFFSET) / HOP_BYTE_LEN; + + uint256 offset = POOL_DATA_OFFSET; + + for (uint256 i = 0; i < hopsLength; i++) { + address nextTokenOut = + address(bytes20(LibBytes.loadCalldata(msg.data, offset))); + Config poolConfig = + Config.wrap(LibBytes.loadCalldata(msg.data, offset + 20)); + + (address token0, address token1, bool isToken1) = nextTokenIn + > nextTokenOut + ? (nextTokenOut, nextTokenIn, true) + : (nextTokenIn, nextTokenOut, false); + + // slither-disable-next-line calls-loop + (int128 delta0, int128 delta1) = core.swap_611415377( + EkuboPoolKey(token0, token1, poolConfig), + nextAmountIn, + isToken1, + isToken1 ? MAX_SQRT_RATIO : MIN_SQRT_RATIO, + 0 + ); + + nextTokenIn = nextTokenOut; + nextAmountIn = -(isToken1 ? delta0 : delta1); + + offset += HOP_BYTE_LEN; + } + + _pay(tokenIn, tokenInDebtAmount); + + core.withdraw(nextTokenIn, receiver, uint128(nextAmountIn)); + + // slither-disable-next-line assembly + assembly ("memory-safe") { + mstore(0, nextAmountIn) + return(0x10, 16) + } } - function payCallback(uint256, address /*token*/ ) external coreOnly { - // Without selector and locker id - _payCallback(msg.data[36:]); + function payCallback(uint256, address token) external coreOnly { + uint128 amount = uint128(bytes16(msg.data[68:84])); + + SafeTransferLib.safeTransfer(token, address(core), amount); } - function _balanceOf(address token) - internal - view - returns (uint256 balance) - { - balance = token == NATIVE_TOKEN_ADDRESS - ? address(this).balance - : IERC20(token).balanceOf(address(this)); - } - - function _lock(bytes memory data) internal { + function _lock(bytes memory data) internal returns (uint128 swappedAmount) { address target = address(core); // slither-disable-next-line assembly @@ -116,52 +111,12 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } + + returndatacopy(0, 0, 16) + swappedAmount := shr(128, mload(0)) } } - function _locked(bytes calldata swapData) internal { - int128 nextAmountIn = int128(uint128(bytes16(swapData[0:16]))); - uint128 tokenInDebtAmount = uint128(nextAmountIn); - - address receiver = address(bytes20(swapData[16:36])); - address tokenIn = address(bytes20(swapData[36:POOL_DATA_OFFSET])); - - address nextTokenIn = tokenIn; - - uint256 hopsLength = (swapData.length - POOL_DATA_OFFSET) / HOP_BYTE_LEN; - - uint256 offset = POOL_DATA_OFFSET; - - for (uint256 i = 0; i < hopsLength; i++) { - address nextTokenOut = - address(bytes20(LibBytes.loadCalldata(swapData, offset))); - Config poolConfig = - Config.wrap(LibBytes.loadCalldata(swapData, offset + 20)); - - (address token0, address token1, bool isToken1) = nextTokenIn - > nextTokenOut - ? (nextTokenOut, nextTokenIn, true) - : (nextTokenIn, nextTokenOut, false); - - (int128 delta0, int128 delta1) = core.swap_611415377( - EkuboPoolKey(token0, token1, poolConfig), - nextAmountIn, - isToken1, - isToken1 ? MAX_SQRT_RATIO : MIN_SQRT_RATIO, - 0 - ); - - nextTokenIn = nextTokenOut; - nextAmountIn = -(isToken1 ? delta0 : delta1); - - offset += HOP_BYTE_LEN; - } - - _pay(tokenIn, tokenInDebtAmount); - - core.withdraw(nextTokenIn, receiver, uint128(nextAmountIn)); - } - function _pay(address token, uint128 amount) internal { address target = address(core); @@ -185,13 +140,6 @@ contract EkuboExecutor is IExecutor, ICallback, ILocker, IPayer { } } - function _payCallback(bytes calldata payData) internal { - address token = address(bytes20(payData[12:32])); // This arg is abi-encoded - uint128 amount = uint128(bytes16(payData[32:48])); - - SafeTransferLib.safeTransfer(token, address(core), amount); - } - // To receive withdrawals from Core receive() external payable {} From 5540503a59cd340d4a93d23e6855b48712285eed Mon Sep 17 00:00:00 2001 From: die-herdplatte <173669014+die-herdplatte@users.noreply.github.com> Date: Thu, 3 Apr 2025 00:42:10 +0700 Subject: [PATCH 72/79] forge fmt --- foundry/src/executors/EkuboExecutor.sol | 8 ++++++-- foundry/test/TychoRouter.t.sol | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/foundry/src/executors/EkuboExecutor.sol b/foundry/src/executors/EkuboExecutor.sol index eb5b7d4..564b9d2 100644 --- a/foundry/src/executors/EkuboExecutor.sol +++ b/foundry/src/executors/EkuboExecutor.sol @@ -33,7 +33,8 @@ contract EkuboExecutor is IExecutor, ILocker, IPayer { if (data.length < 92) revert EkuboExecutor__InvalidDataLength(); // amountIn must be at most type(int128).MAX - calculatedAmount = uint256(_lock(bytes.concat(bytes16(uint128(amountIn)), data))); + calculatedAmount = + uint256(_lock(bytes.concat(bytes16(uint128(amountIn)), data))); } function locked(uint256) external coreOnly { @@ -92,7 +93,10 @@ contract EkuboExecutor is IExecutor, ILocker, IPayer { SafeTransferLib.safeTransfer(token, address(core), amount); } - function _lock(bytes memory data) internal returns (uint128 swappedAmount) { + function _lock(bytes memory data) + internal + returns (uint128 swappedAmount) + { address target = address(core); // slither-disable-next-line assembly diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 4fde00a..a54712b 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1020,7 +1020,10 @@ contract TychoRouterTest is TychoRouterTestSetup { // TEMPORARY while the Ekubo executor address is hardcoded in TychoRouter // This allows us to change the code at that address to be the testing executor code - vm.etch(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279, 0x2a07706473244BC757E10F2a9E86fB532828afe3.code); + vm.etch( + 0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279, + 0x2a07706473244BC757E10F2a9E86fB532828afe3.code + ); deal(ALICE, 1 ether); uint256 balancerBefore = IERC20(USDC_ADDR).balanceOf(ALICE); From 25cafa31e75abc4a869a1a783d6f293cb2b539e5 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 3 Apr 2025 10:53:55 +0200 Subject: [PATCH 73/79] chore: forge fmt --- foundry/test/TychoRouter.t.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 4fde00a..a54712b 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1020,7 +1020,10 @@ contract TychoRouterTest is TychoRouterTestSetup { // TEMPORARY while the Ekubo executor address is hardcoded in TychoRouter // This allows us to change the code at that address to be the testing executor code - vm.etch(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279, 0x2a07706473244BC757E10F2a9E86fB532828afe3.code); + vm.etch( + 0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279, + 0x2a07706473244BC757E10F2a9E86fB532828afe3.code + ); deal(ALICE, 1 ether); uint256 balancerBefore = IERC20(USDC_ADDR).balanceOf(ALICE); From b4c687bc3f51167fc942b8b7b00b17df4a2bec30 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 3 Apr 2025 11:09:31 +0200 Subject: [PATCH 74/79] fix: Proper ekubo protocol name in GROUPABLE_PROTOCOLS --- src/encoding/evm/constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/evm/constants.rs b/src/encoding/evm/constants.rs index 0b6eb48..b3d33fc 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -12,6 +12,6 @@ pub static GROUPABLE_PROTOCOLS: LazyLock> = LazyLock::new( let mut set = HashSet::new(); set.insert("uniswap_v4"); set.insert("balancer_v3"); - set.insert("ekubo"); + set.insert("ekubo_v2"); set }); From 43f1a0701707d2dbf5bfa4f6ccde50aea48e46a1 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 3 Apr 2025 11:26:06 +0200 Subject: [PATCH 75/79] fix: Run foundry tests on PR branch (not main) --- .github/workflows/evm-foundry-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/evm-foundry-ci.yml b/.github/workflows/evm-foundry-ci.yml index 8c4a98e..67647d7 100644 --- a/.github/workflows/evm-foundry-ci.yml +++ b/.github/workflows/evm-foundry-ci.yml @@ -15,6 +15,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: + ref: ${{ github.event.pull_request.head.sha }} submodules: recursive - name: Install Foundry From f2c29c2c7cef344a5052c551a4ef7aaa40679cba Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 3 Apr 2025 09:46:01 +0000 Subject: [PATCH 76/79] chore(release): 0.75.0 [skip ci] ## [0.75.0](https://github.com/propeller-heads/tycho-execution/compare/0.74.0...0.75.0) (2025-04-03) ### Features * Unichain deployment ([d05e118](https://github.com/propeller-heads/tycho-execution/commit/d05e1183d4e85eb57c139b1fd5411833efea92fc)) ### Bug Fixes * Run foundry tests on PR branch (not main) ([43f1a07](https://github.com/propeller-heads/tycho-execution/commit/43f1a0701707d2dbf5bfa4f6ccde50aea48e46a1)) * Set native and wrapped tokens for Unichain ([4878229](https://github.com/propeller-heads/tycho-execution/commit/4878229e1d60a317b0f2d97ea57886e64cc70cb5)) --- CHANGELOG.md | 13 +++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5594d4..a7bcc68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [0.75.0](https://github.com/propeller-heads/tycho-execution/compare/0.74.0...0.75.0) (2025-04-03) + + +### Features + +* Unichain deployment ([d05e118](https://github.com/propeller-heads/tycho-execution/commit/d05e1183d4e85eb57c139b1fd5411833efea92fc)) + + +### Bug Fixes + +* Run foundry tests on PR branch (not main) ([43f1a07](https://github.com/propeller-heads/tycho-execution/commit/43f1a0701707d2dbf5bfa4f6ccde50aea48e46a1)) +* Set native and wrapped tokens for Unichain ([4878229](https://github.com/propeller-heads/tycho-execution/commit/4878229e1d60a317b0f2d97ea57886e64cc70cb5)) + ## [0.74.0](https://github.com/propeller-heads/tycho-execution/compare/0.73.0...0.74.0) (2025-04-02) diff --git a/Cargo.lock b/Cargo.lock index dd8cc6b..1653da2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.74.0" +version = "0.75.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 168b2fa..cde194e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.74.0" +version = "0.75.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 568b17f074f57ea865b46db73a146b919c937595 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 3 Apr 2025 10:10:12 +0000 Subject: [PATCH 77/79] chore(release): 0.75.1 [skip ci] ## [0.75.1](https://github.com/propeller-heads/tycho-execution/compare/0.75.0...0.75.1) (2025-04-03) ### Bug Fixes * Proper ekubo protocol name in GROUPABLE_PROTOCOLS ([b4c687b](https://github.com/propeller-heads/tycho-execution/commit/b4c687bc3f51167fc942b8b7b00b17df4a2bec30)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7bcc68..2e0fc6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.75.1](https://github.com/propeller-heads/tycho-execution/compare/0.75.0...0.75.1) (2025-04-03) + + +### Bug Fixes + +* Proper ekubo protocol name in GROUPABLE_PROTOCOLS ([b4c687b](https://github.com/propeller-heads/tycho-execution/commit/b4c687bc3f51167fc942b8b7b00b17df4a2bec30)) + ## [0.75.0](https://github.com/propeller-heads/tycho-execution/compare/0.74.0...0.75.0) (2025-04-03) diff --git a/Cargo.lock b/Cargo.lock index 1653da2..6b8363f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.75.0" +version = "0.75.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index cde194e..4fda3bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.75.0" +version = "0.75.1" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 9012d7b4d1745a3c5315d12c7c412d6e3267e4ba Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Thu, 3 Apr 2025 12:51:23 +0200 Subject: [PATCH 78/79] feat: deploy Ekubo gas optimizations new executor and new router (since the executor is hardcoded for now). --- config/executor_addresses.json | 2 +- config/router_addresses.json | 2 +- foundry/hardhat.config.js | 6 +----- foundry/src/TychoRouter.sol | 4 ++-- foundry/test/TychoRouter.t.sol | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 6943a1d..77d684b 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -7,7 +7,7 @@ "pancakeswap_v3": "0x4929B619A8F0D9c06ed0FfD497636580D823F65d", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", - "ekubo_v2": "0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279" + "ekubo_v2": "0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", diff --git a/config/router_addresses.json b/config/router_addresses.json index 391aa56..95ba8e0 100644 --- a/config/router_addresses.json +++ b/config/router_addresses.json @@ -1,5 +1,5 @@ { - "ethereum": "0x0178f471f219737c51d6005556d2f44de011a08a", + "ethereum": "0xabA2fC41e2dB95E77C6799D0F580034395FF2B9E", "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb", "unichain": "0x9BDC3bE75440dBE563527CB39BB11cFBD1E21b09" } diff --git a/foundry/hardhat.config.js b/foundry/hardhat.config.js index ad1c611..0603641 100644 --- a/foundry/hardhat.config.js +++ b/foundry/hardhat.config.js @@ -50,11 +50,7 @@ module.exports = { }, etherscan: { - apiKey: { - unichain: process.env.BLOCKCHAIN_EXPLORER_API_KEY, - base: process.env.BLOCKCHAIN_EXPLORER_API_KEY, - ethereum: process.env.BLOCKCHAIN_EXPLORER_API_KEY, - }, + apiKey: process.env.BLOCKCHAIN_EXPLORER_API_KEY, customChains: [ { network: "unichain", diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 2de02f7..3a549cd 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -554,7 +554,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { } function locked(uint256) external { - address executor = address(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279); + address executor = address(0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D); // slither-disable-next-line controlled-delegatecall,low-level-calls (bool success, bytes memory result) = executor.delegatecall(msg.data); @@ -577,7 +577,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { } function payCallback(uint256, address /*token*/ ) external { - address executor = address(0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279); + address executor = address(0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D); // slither-disable-next-line controlled-delegatecall,low-level-calls (bool success, bytes memory result) = executor.delegatecall(msg.data); diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index a54712b..c446f45 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1021,7 +1021,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // TEMPORARY while the Ekubo executor address is hardcoded in TychoRouter // This allows us to change the code at that address to be the testing executor code vm.etch( - 0xA612f60d3C49E5f13f0e067b14E0eD6656F3F279, + 0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D, 0x2a07706473244BC757E10F2a9E86fB532828afe3.code ); From 6c679c043404fa71c999b60c43edbf39d9183f7d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 3 Apr 2025 13:34:26 +0000 Subject: [PATCH 79/79] chore(release): 0.76.0 [skip ci] ## [0.76.0](https://github.com/propeller-heads/tycho-execution/compare/0.75.1...0.76.0) (2025-04-03) ### Features * deploy Ekubo gas optimizations ([9012d7b](https://github.com/propeller-heads/tycho-execution/commit/9012d7b4d1745a3c5315d12c7c412d6e3267e4ba)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e0fc6b..b4ffaa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.76.0](https://github.com/propeller-heads/tycho-execution/compare/0.75.1...0.76.0) (2025-04-03) + + +### Features + +* deploy Ekubo gas optimizations ([9012d7b](https://github.com/propeller-heads/tycho-execution/commit/9012d7b4d1745a3c5315d12c7c412d6e3267e4ba)) + ## [0.75.1](https://github.com/propeller-heads/tycho-execution/compare/0.75.0...0.75.1) (2025-04-03) diff --git a/Cargo.lock b/Cargo.lock index 6b8363f..7d69956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.75.1" +version = "0.76.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 4fda3bb..3979f0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.75.1" +version = "0.76.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution"