From 0ac722d91f6c2f7c09531f5f56f057d1fe014c6f Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 19 Mar 2025 09:23:43 +0800 Subject: [PATCH 01/11] feat: add mav executor --- foundry/src/executors/MaverickV2Executor.sol | 93 +++++++++++++++++++ foundry/test/Constants.sol | 6 ++ .../test/executors/MaverickV2Executor.t.sol | 77 +++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 foundry/src/executors/MaverickV2Executor.sol create mode 100644 foundry/test/executors/MaverickV2Executor.t.sol diff --git a/foundry/src/executors/MaverickV2Executor.sol b/foundry/src/executors/MaverickV2Executor.sol new file mode 100644 index 0000000..a5971c2 --- /dev/null +++ b/foundry/src/executors/MaverickV2Executor.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import "@interfaces/IExecutor.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +error MaverickV2Executor__InvalidDataLength(); +error MaverickV2Executor__InvalidTarget(); +error MaverickV2Executor__InvalidFactory(); + +contract MaverickV2Executor is IExecutor { + using SafeERC20 for IERC20; + + address public immutable factory; + address private immutable self; + + constructor(address _factory) { + if (_factory == address(0)) { + revert MaverickV2Executor__InvalidFactory(); + } + factory = _factory; + self = address(this); + } + + // slither-disable-next-line locked-ether + function swap(uint256 givenAmount, bytes calldata data) + external + payable + returns (uint256 calculatedAmount) + { + address target; + address receiver; + IERC20 tokenIn; + + (tokenIn, target, receiver) = _decodeData(data); + + _verifyPairAddress(target); + IMaverickV2Pool pool = IMaverickV2Pool(target); + + bool isTokenAIn = pool.tokenA() == tokenIn; + int32 tickLimit = isTokenAIn ? type(int32).max : type(int32).min; + IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool + .SwapParams({ + amount: givenAmount, + tokenAIn: isTokenAIn, + exactOutput: false, + tickLimit: tickLimit + }); + IERC20(tokenIn).safeTransfer(target, givenAmount); + (, calculatedAmount) = pool.swap(receiver, swapParams, ""); + } + + function _decodeData(bytes calldata data) + internal + pure + returns (IERC20 inToken, address target, address receiver) + { + if (data.length != 60) { + revert MaverickV2Executor__InvalidDataLength(); + } + inToken = IERC20(address(bytes20(data[0:20]))); + target = address(bytes20(data[20:40])); + receiver = address(bytes20(data[40:60])); + } + + function _verifyPairAddress(address target) internal view { + if (!IMaverickV2Factory(factory).isFactoryPool(IMaverickV2Pool(target))) { + revert MaverickV2Executor__InvalidTarget(); + } + } +} + +interface IMaverickV2Factory { + function isFactoryPool(IMaverickV2Pool pool) external view returns (bool); +} + +interface IMaverickV2Pool { + struct SwapParams { + uint256 amount; + bool tokenAIn; + bool exactOutput; + int32 tickLimit; + } + + function swap( + address recipient, + SwapParams memory params, + bytes calldata data + ) external returns (uint256 amountIn, uint256 amountOut); + + function tokenA() external view returns (IERC20); + function tokenB() external view returns (IERC20); +} diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index 9b0f488..51b292d 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -56,6 +56,12 @@ contract Constants is Test, BaseConstants { address WSTTAO_ADDR = address(0xe9633C52f4c8B7BDeb08c4A7fE8a5c1B84AFCf67); address WTAO_ADDR = address(0x77E06c9eCCf2E797fd462A92B6D7642EF85b0A44); address BSGG_ADDR = address(0xdA16Cf041E2780618c49Dbae5d734B89a6Bac9b3); + address GHO_ADDR = address(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f); + + // Maverick v2 + address MAVERICK_V2_FACTORY = 0x0A7e848Aca42d879EF06507Fca0E7b33A0a63c1e; + address GHO_USDC_POOL = 0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67; + // Uniswap v2 address WETH_DAI_POOL = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11; address DAI_USDC_POOL = 0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5; diff --git a/foundry/test/executors/MaverickV2Executor.t.sol b/foundry/test/executors/MaverickV2Executor.t.sol new file mode 100644 index 0000000..8d7fe6b --- /dev/null +++ b/foundry/test/executors/MaverickV2Executor.t.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import "@src/executors/MaverickV2Executor.sol"; +import {Test} from "../../lib/forge-std/src/Test.sol"; +import {Constants} from "../Constants.sol"; + +contract MaverickV2ExecutorExposed is MaverickV2Executor { + constructor(address _factory) MaverickV2Executor(_factory) {} + function decodeParams(bytes calldata data) + external + pure + returns ( + IERC20 tokenIn, + address target, + address receiver + ) + { + return _decodeData(data); + } +} + +contract MaverickV2ExecutorTest is + Test, + Constants +{ + using SafeERC20 for IERC20; + + MaverickV2ExecutorExposed maverickV2Exposed; + IERC20 GHO = IERC20(GHO_ADDR); + IERC20 USDC = IERC20(USDC_ADDR); + + function setUp() public { + uint256 forkBlock = 20127232; + vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); + maverickV2Exposed = new MaverickV2ExecutorExposed(MAVERICK_V2_FACTORY); + } + + function testDecodeParams() public view { + bytes memory params = abi.encodePacked( + GHO_ADDR, GHO_USDC_POOL, address(2) + ); + + ( + IERC20 tokenIn, + address target, + address receiver + ) = maverickV2Exposed.decodeParams(params); + + assertEq(address(tokenIn), GHO_ADDR); + assertEq(target, GHO_USDC_POOL); + assertEq(receiver, address(2)); + } + + function testDecodeParamsInvalidDataLength() public { + bytes memory invalidParams = + abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2), true); + + vm.expectRevert(MaverickV2Executor__InvalidDataLength.selector); + maverickV2Exposed.decodeParams(invalidParams); + } + + function testSwap() public { + uint256 amountIn = 10 ** 18; + bytes memory protocolData = + abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2)); + + deal(GHO_ADDR, address(maverickV2Exposed), amountIn); + uint256 balanceBefore = GHO.balanceOf(BOB); + + uint256 amountOut = maverickV2Exposed.swap(amountIn, protocolData); + + uint256 balanceAfter = GHO.balanceOf(BOB); + assertGt(balanceAfter, balanceBefore); + assertEq(balanceAfter - balanceBefore, amountOut); + } +} From 72a651d453943d554722d0fa4818255fbec01ce5 Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 20 Mar 2025 09:17:48 +0800 Subject: [PATCH 02/11] feat: add swap encode --- foundry/src/executors/MaverickV2Executor.sol | 3 +- .../test/executors/MaverickV2Executor.t.sol | 53 ++++++++---- .../evm/swap_encoder/swap_encoders.rs | 82 +++++++++++++++++++ 3 files changed, 120 insertions(+), 18 deletions(-) diff --git a/foundry/src/executors/MaverickV2Executor.sol b/foundry/src/executors/MaverickV2Executor.sol index a5971c2..08c0350 100644 --- a/foundry/src/executors/MaverickV2Executor.sol +++ b/foundry/src/executors/MaverickV2Executor.sol @@ -64,7 +64,8 @@ contract MaverickV2Executor is IExecutor { } function _verifyPairAddress(address target) internal view { - if (!IMaverickV2Factory(factory).isFactoryPool(IMaverickV2Pool(target))) { + if (!IMaverickV2Factory(factory).isFactoryPool(IMaverickV2Pool(target))) + { revert MaverickV2Executor__InvalidTarget(); } } diff --git a/foundry/test/executors/MaverickV2Executor.t.sol b/foundry/test/executors/MaverickV2Executor.t.sol index 8d7fe6b..14769e9 100644 --- a/foundry/test/executors/MaverickV2Executor.t.sol +++ b/foundry/test/executors/MaverickV2Executor.t.sol @@ -7,23 +7,17 @@ import {Constants} from "../Constants.sol"; contract MaverickV2ExecutorExposed is MaverickV2Executor { constructor(address _factory) MaverickV2Executor(_factory) {} + function decodeParams(bytes calldata data) external pure - returns ( - IERC20 tokenIn, - address target, - address receiver - ) + returns (IERC20 tokenIn, address target, address receiver) { return _decodeData(data); } } -contract MaverickV2ExecutorTest is - Test, - Constants -{ +contract MaverickV2ExecutorTest is Test, Constants { using SafeERC20 for IERC20; MaverickV2ExecutorExposed maverickV2Exposed; @@ -37,15 +31,11 @@ contract MaverickV2ExecutorTest is } function testDecodeParams() public view { - bytes memory params = abi.encodePacked( - GHO_ADDR, GHO_USDC_POOL, address(2) - ); + bytes memory params = + abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2)); - ( - IERC20 tokenIn, - address target, - address receiver - ) = maverickV2Exposed.decodeParams(params); + (IERC20 tokenIn, address target, address receiver) = + maverickV2Exposed.decodeParams(params); assertEq(address(tokenIn), GHO_ADDR); assertEq(target, GHO_USDC_POOL); @@ -74,4 +64,33 @@ contract MaverickV2ExecutorTest is assertGt(balanceAfter, balanceBefore); assertEq(balanceAfter - balanceBefore, amountOut); } + + function testDecodeIntegration() public view { + // Generated by the SwapEncoder - test_encode_maverick_v2 + bytes memory protocolData = + hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"; + + (IERC20 tokenIn, address pool, address receiver) = + maverickV2Exposed.decodeParams(protocolData); + + assertEq(address(tokenIn), GHO_ADDR); + assertEq(pool, GHO_USDC_POOL); + assertEq(receiver, BOB); + } + + function testSwapIntegration() public { + // Generated by the SwapEncoder - test_encode_maverick_v2 + bytes memory protocolData = + hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"; + + uint256 amountIn = 10 ** 18; + deal(GHO_ADDR, address(maverickV2Exposed), amountIn); + uint256 balanceBefore = GHO.balanceOf(BOB); + + uint256 amountOut = maverickV2Exposed.swap(amountIn, protocolData); + + uint256 balanceAfter = USDC.balanceOf(BOB); + assertGt(balanceAfter, balanceBefore); + assertEq(balanceAfter - balanceBefore, amountOut); + } } diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index cf41e88..7f9c8c4 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -560,6 +560,45 @@ impl SwapEncoder for CurveSwapEncoder { } } +/// Encodes a swap on a Maverick V2 pool through the given executor address. +/// +/// # Fields +/// * `executor_address` - The address of the executor contract that will perform the swap. +#[derive(Clone)] +pub struct MaverickV2SwapEncoder { + executor_address: String, +} + +impl SwapEncoder for MaverickV2SwapEncoder { + fn new(executor_address: String) -> Self { + Self { + executor_address, + } + } + fn encode_swap( + &self, + swap: Swap, + encoding_context: EncodingContext, + ) -> Result, EncodingError> { + let component_id = AlloyBytes::from_str(&swap.component.id) + .map_err(|_| EncodingError::FatalError("Invalid component ID".to_string()))?; + + let args = ( + bytes_to_address(&swap.token_in)?, + component_id, + bytes_to_address(&encoding_context.receiver)?, + ); + Ok(args.abi_encode_packed()) + } + + 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; @@ -1394,4 +1433,47 @@ mod tests { ); } } + + #[test] + fn test_encode_maverick_v2() { + let maverick_pool = ProtocolComponent { + id: String::from("0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67"), + protocol_system: String::from("vm:maverick_v2"), + ..Default::default() + }; + let token_in = Bytes::from("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"); + let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); + let swap = Swap { + component: maverick_pool, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + let encoding_context = EncodingContext { + // The receiver was generated with `makeAddr("bob") using forge` + receiver: Bytes::from("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"), + exact_out: false, + router_address: Bytes::zero(20), + group_token_in: token_in.clone(), + group_token_out: token_out.clone(), + }; + let encoder = + MaverickV2SwapEncoder::new(String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4")); + let encoded_swap = encoder + .encode_swap(swap, encoding_context) + .unwrap(); + let hex_swap = encode(&encoded_swap); + + assert_eq!( + hex_swap, + String::from(concat!( + // token in + "40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f", + // pool + "14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67", + // receiver + "1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e", + )).to_lowercase() + ); + } } From d103ca9e33f931f18c8af79efa5d77d801bab2e9 Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 26 Mar 2025 23:04:45 +0800 Subject: [PATCH 03/11] fix: swap test --- foundry/test/executors/MaverickV2Executor.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry/test/executors/MaverickV2Executor.t.sol b/foundry/test/executors/MaverickV2Executor.t.sol index 14769e9..09b6793 100644 --- a/foundry/test/executors/MaverickV2Executor.t.sol +++ b/foundry/test/executors/MaverickV2Executor.t.sol @@ -53,7 +53,7 @@ contract MaverickV2ExecutorTest is Test, Constants { function testSwap() public { uint256 amountIn = 10 ** 18; bytes memory protocolData = - abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2)); + abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, BOB); deal(GHO_ADDR, address(maverickV2Exposed), amountIn); uint256 balanceBefore = GHO.balanceOf(BOB); From 4c938306bda62d37b8932697f98d0d667a96532b Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 27 Mar 2025 06:27:07 +0800 Subject: [PATCH 04/11] fix: maverick test fork block --- foundry/test/executors/MaverickV2Executor.t.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/foundry/test/executors/MaverickV2Executor.t.sol b/foundry/test/executors/MaverickV2Executor.t.sol index 09b6793..dcba37c 100644 --- a/foundry/test/executors/MaverickV2Executor.t.sol +++ b/foundry/test/executors/MaverickV2Executor.t.sol @@ -25,7 +25,7 @@ contract MaverickV2ExecutorTest is Test, Constants { IERC20 USDC = IERC20(USDC_ADDR); function setUp() public { - uint256 forkBlock = 20127232; + uint256 forkBlock = 22096000; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); maverickV2Exposed = new MaverickV2ExecutorExposed(MAVERICK_V2_FACTORY); } @@ -51,16 +51,16 @@ contract MaverickV2ExecutorTest is Test, Constants { } function testSwap() public { - uint256 amountIn = 10 ** 18; + uint256 amountIn = 10e18; bytes memory protocolData = abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, BOB); deal(GHO_ADDR, address(maverickV2Exposed), amountIn); - uint256 balanceBefore = GHO.balanceOf(BOB); + uint256 balanceBefore = USDC.balanceOf(BOB); uint256 amountOut = maverickV2Exposed.swap(amountIn, protocolData); - uint256 balanceAfter = GHO.balanceOf(BOB); + uint256 balanceAfter = USDC.balanceOf(BOB); assertGt(balanceAfter, balanceBefore); assertEq(balanceAfter - balanceBefore, amountOut); } @@ -85,7 +85,7 @@ contract MaverickV2ExecutorTest is Test, Constants { uint256 amountIn = 10 ** 18; deal(GHO_ADDR, address(maverickV2Exposed), amountIn); - uint256 balanceBefore = GHO.balanceOf(BOB); + uint256 balanceBefore = USDC.balanceOf(BOB); uint256 amountOut = maverickV2Exposed.swap(amountIn, protocolData); From bd642d7b453e1b860c1b5909022e41de1c70b5bd Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 3 Apr 2025 09:30:34 +0800 Subject: [PATCH 05/11] update --- src/encoding/evm/swap_encoder/swap_encoders.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 7f9c8c4..370f638 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -571,9 +571,7 @@ pub struct MaverickV2SwapEncoder { impl SwapEncoder for MaverickV2SwapEncoder { fn new(executor_address: String) -> Self { - Self { - executor_address, - } + Self { executor_address } } fn encode_swap( &self, @@ -1473,7 +1471,8 @@ mod tests { "14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67", // receiver "1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e", - )).to_lowercase() + )) + .to_lowercase() ); } } From bab30e3958ee62091b322ac0bc540939c050ee9e Mon Sep 17 00:00:00 2001 From: zach Date: Fri, 11 Apr 2025 08:43:03 +0800 Subject: [PATCH 06/11] fix: add maverick for build --- src/encoding/evm/swap_encoder/builder.rs | 9 +++++++-- .../evm/swap_encoder/swap_encoders.rs | 20 ++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index bbe2c2d..735029c 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use crate::encoding::{ errors::EncodingError, evm::swap_encoder::swap_encoders::{ - BalancerV2SwapEncoder, CurveSwapEncoder, EkuboSwapEncoder, UniswapV2SwapEncoder, - UniswapV3SwapEncoder, UniswapV4SwapEncoder, + BalancerV2SwapEncoder, CurveSwapEncoder, EkuboSwapEncoder, MaverickV2SwapEncoder, + UniswapV2SwapEncoder, UniswapV3SwapEncoder, UniswapV4SwapEncoder, }, models::Chain, swap_encoder::SwapEncoder, @@ -76,6 +76,11 @@ impl SwapEncoderBuilder { "vm:curve" => { Ok(Box::new(CurveSwapEncoder::new(self.executor_address, self.chain, self.config)?)) } + "vm::maverick_v2" => Ok(Box::new(MaverickV2SwapEncoder::new( + self.executor_address, + self.chain, + self.config, + )?)), _ => Err(EncodingError::FatalError(format!( "Unknown protocol system: {}", self.protocol_system diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 370f638..b38a3cf 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -570,9 +570,14 @@ pub struct MaverickV2SwapEncoder { } impl SwapEncoder for MaverickV2SwapEncoder { - fn new(executor_address: String) -> Self { - Self { executor_address } + fn new( + executor_address: String, + _chain: Chain, + _config: Option>, + ) -> Result { + Ok(Self { executor_address }) } + fn encode_swap( &self, swap: Swap, @@ -1451,12 +1456,17 @@ 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::default()), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; - let encoder = - MaverickV2SwapEncoder::new(String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4")); + let encoder = MaverickV2SwapEncoder::new( + String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"), + TychoCoreChain::Ethereum.into(), + None, + ) + .unwrap(); + let encoded_swap = encoder .encode_swap(swap, encoding_context) .unwrap(); From bcef8f69f628c3a37a7cb5462e26200ad5aab1ee Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 29 Apr 2025 15:36:21 -0400 Subject: [PATCH 07/11] feat: Transfer Optimizations in MaverickV2 - Also added integration test to test the optimizations, where we can see the in and out transfers being optimized if we enable verbose foundry testing - Fixed typo in swap encoder builder initialization --- config/test_executor_addresses.json | 3 +- foundry/src/executors/MaverickV2Executor.sol | 24 ++++++-- .../test/TychoRouterProtocolIntegration.t.sol | 21 +++++++ foundry/test/TychoRouterTestSetup.sol | 7 ++- foundry/test/assets/calldata.txt | 1 + .../test/executors/MaverickV2Executor.t.sol | 60 ++++++++++++++----- src/encoding/evm/constants.rs | 3 +- .../evm/strategy_encoder/strategy_encoders.rs | 50 ++++++++++++++++ src/encoding/evm/swap_encoder/builder.rs | 2 +- .../evm/swap_encoder/swap_encoders.rs | 5 ++ 10 files changed, 152 insertions(+), 24 deletions(-) diff --git a/config/test_executor_addresses.json b/config/test_executor_addresses.json index 8c6dc5c..d04630d 100644 --- a/config/test_executor_addresses.json +++ b/config/test_executor_addresses.json @@ -8,6 +8,7 @@ "uniswap_v4": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", "vm:balancer_v2": "0xc7183455a4C133Ae270771860664b6B7ec320bB1", "ekubo_v2": "0xa0Cb889707d426A7A386870A03bc70d1b0697598", - "vm:curve": "0x1d1499e622D69689cdf9004d05Ec547d650Ff211" + "vm:curve": "0x1d1499e622D69689cdf9004d05Ec547d650Ff211", + "vm:maverick_v2": "0xA4AD4f68d0b91CFD19687c881e50f3A00242828c" } } diff --git a/foundry/src/executors/MaverickV2Executor.sol b/foundry/src/executors/MaverickV2Executor.sol index 08c0350..0775177 100644 --- a/foundry/src/executors/MaverickV2Executor.sol +++ b/foundry/src/executors/MaverickV2Executor.sol @@ -3,18 +3,19 @@ pragma solidity ^0.8.26; import "@interfaces/IExecutor.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "./TokenTransfer.sol"; error MaverickV2Executor__InvalidDataLength(); error MaverickV2Executor__InvalidTarget(); error MaverickV2Executor__InvalidFactory(); -contract MaverickV2Executor is IExecutor { +contract MaverickV2Executor is IExecutor, TokenTransfer { using SafeERC20 for IERC20; address public immutable factory; address private immutable self; - constructor(address _factory) { + constructor(address _factory, address _permit2) TokenTransfer(_permit2) { if (_factory == address(0)) { revert MaverickV2Executor__InvalidFactory(); } @@ -31,8 +32,9 @@ contract MaverickV2Executor is IExecutor { address target; address receiver; IERC20 tokenIn; + TransferType transferType; - (tokenIn, target, receiver) = _decodeData(data); + (tokenIn, target, receiver, transferType) = _decodeData(data); _verifyPairAddress(target); IMaverickV2Pool pool = IMaverickV2Pool(target); @@ -46,21 +48,31 @@ contract MaverickV2Executor is IExecutor { exactOutput: false, tickLimit: tickLimit }); - IERC20(tokenIn).safeTransfer(target, givenAmount); + + _transfer( + address(tokenIn), msg.sender, target, givenAmount, transferType + ); + // slither-disable-next-line unused-return (, calculatedAmount) = pool.swap(receiver, swapParams, ""); } function _decodeData(bytes calldata data) internal pure - returns (IERC20 inToken, address target, address receiver) + returns ( + IERC20 inToken, + address target, + address receiver, + TransferType transferType + ) { - if (data.length != 60) { + if (data.length != 61) { revert MaverickV2Executor__InvalidDataLength(); } inToken = IERC20(address(bytes20(data[0:20]))); target = address(bytes20(data[20:40])); receiver = address(bytes20(data[40:60])); + transferType = TransferType(uint8(data[60])); } function _verifyPairAddress(address target) internal view { diff --git a/foundry/test/TychoRouterProtocolIntegration.t.sol b/foundry/test/TychoRouterProtocolIntegration.t.sol index 08eb97b..baf8c9d 100644 --- a/foundry/test/TychoRouterProtocolIntegration.t.sol +++ b/foundry/test/TychoRouterProtocolIntegration.t.sol @@ -186,6 +186,27 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup { assertEq(balanceAfter - balanceBefore, 1474406268748155809); } + function testSingleMaverickIntegration() public { + vm.stopPrank(); + + deal(GHO_ADDR, ALICE, 1 ether); + uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE); + + // Approve permit2 + vm.startPrank(ALICE); + IERC20(GHO_ADDR).approve(tychoRouterAddr, type(uint256).max); + + bytes memory callData = + loadCallDataFromFile("test_single_encoding_strategy_maverick"); + (bool success,) = tychoRouterAddr.call(callData); + + uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE); + + assertTrue(success, "Call Failed"); + assertGe(balanceAfter - balanceBefore, 999725); + assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); + } + function testSingleEkuboIntegration() public { vm.stopPrank(); diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 7757d49..540027c 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -15,6 +15,7 @@ import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol"; import {Permit2TestHelper} from "./Permit2TestHelper.sol"; import "./TestUtils.sol"; +import {MaverickV2Executor} from "../src/executors/MaverickV2Executor.sol"; contract TychoRouterExposed is TychoRouter { constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {} @@ -53,6 +54,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils { BalancerV2Executor public balancerv2Executor; EkuboExecutor public ekuboExecutor; CurveExecutor public curveExecutor; + MaverickV2Executor public maverickv2Executor; MockERC20[] tokens; function setUp() public { @@ -110,8 +112,10 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils { balancerv2Executor = new BalancerV2Executor(PERMIT2_ADDRESS); ekuboExecutor = new EkuboExecutor(ekuboCore, PERMIT2_ADDRESS); curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS); + maverickv2Executor = + new MaverickV2Executor(MAVERICK_V2_FACTORY, PERMIT2_ADDRESS); - address[] memory executors = new address[](7); + address[] memory executors = new address[](8); executors[0] = address(usv2Executor); executors[1] = address(usv3Executor); executors[2] = address(pancakev3Executor); @@ -119,6 +123,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils { executors[4] = address(balancerv2Executor); executors[5] = address(ekuboExecutor); executors[6] = address(curveExecutor); + executors[7] = address(maverickv2Executor); return executors; } diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index bf5c417..320a1fd 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -24,3 +24,4 @@ test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754 test_ekubo_encode_swap_multi:00ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001 +test_single_encoding_strategy_maverick:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc201000000000000000000000000000000 diff --git a/foundry/test/executors/MaverickV2Executor.t.sol b/foundry/test/executors/MaverickV2Executor.t.sol index dcba37c..ba08663 100644 --- a/foundry/test/executors/MaverickV2Executor.t.sol +++ b/foundry/test/executors/MaverickV2Executor.t.sol @@ -6,12 +6,19 @@ import {Test} from "../../lib/forge-std/src/Test.sol"; import {Constants} from "../Constants.sol"; contract MaverickV2ExecutorExposed is MaverickV2Executor { - constructor(address _factory) MaverickV2Executor(_factory) {} + constructor(address _factory, address _permit2) + MaverickV2Executor(_factory, _permit2) + {} function decodeParams(bytes calldata data) external pure - returns (IERC20 tokenIn, address target, address receiver) + returns ( + IERC20 tokenIn, + address target, + address receiver, + TransferType transferType + ) { return _decodeData(data); } @@ -27,24 +34,37 @@ contract MaverickV2ExecutorTest is Test, Constants { function setUp() public { uint256 forkBlock = 22096000; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - maverickV2Exposed = new MaverickV2ExecutorExposed(MAVERICK_V2_FACTORY); + maverickV2Exposed = + new MaverickV2ExecutorExposed(MAVERICK_V2_FACTORY, PERMIT2_ADDRESS); } function testDecodeParams() public view { - bytes memory params = - abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2)); + bytes memory params = abi.encodePacked( + GHO_ADDR, + GHO_USDC_POOL, + address(2), + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ); - (IERC20 tokenIn, address target, address receiver) = - maverickV2Exposed.decodeParams(params); + ( + IERC20 tokenIn, + address target, + address receiver, + TokenTransfer.TransferType transferType + ) = maverickV2Exposed.decodeParams(params); assertEq(address(tokenIn), GHO_ADDR); assertEq(target, GHO_USDC_POOL); assertEq(receiver, address(2)); + assertEq( + uint8(transferType), + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) + ); } function testDecodeParamsInvalidDataLength() public { bytes memory invalidParams = - abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2), true); + abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2)); vm.expectRevert(MaverickV2Executor__InvalidDataLength.selector); maverickV2Exposed.decodeParams(invalidParams); @@ -52,8 +72,12 @@ contract MaverickV2ExecutorTest is Test, Constants { function testSwap() public { uint256 amountIn = 10e18; - bytes memory protocolData = - abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, BOB); + bytes memory protocolData = abi.encodePacked( + GHO_ADDR, + GHO_USDC_POOL, + BOB, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ); deal(GHO_ADDR, address(maverickV2Exposed), amountIn); uint256 balanceBefore = USDC.balanceOf(BOB); @@ -68,20 +92,28 @@ contract MaverickV2ExecutorTest is Test, Constants { function testDecodeIntegration() public view { // Generated by the SwapEncoder - test_encode_maverick_v2 bytes memory protocolData = - hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"; + hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00"; - (IERC20 tokenIn, address pool, address receiver) = - maverickV2Exposed.decodeParams(protocolData); + ( + IERC20 tokenIn, + address pool, + address receiver, + TokenTransfer.TransferType transferType + ) = maverickV2Exposed.decodeParams(protocolData); assertEq(address(tokenIn), GHO_ADDR); assertEq(pool, GHO_USDC_POOL); assertEq(receiver, BOB); + assertEq( + uint8(transferType), + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) + ); } function testSwapIntegration() public { // Generated by the SwapEncoder - test_encode_maverick_v2 bytes memory protocolData = - hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"; + hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00"; uint256 amountIn = 10 ** 18; deal(GHO_ADDR, address(maverickV2Exposed), amountIn); diff --git a/src/encoding/evm/constants.rs b/src/encoding/evm/constants.rs index 37d8aff..2777040 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -20,7 +20,7 @@ pub static GROUPABLE_PROTOCOLS: LazyLock> = LazyLock::new( /// These protocols need an external in transfer to the pool. This transfer can be from the router, /// from the user or from the previous pool. Any protocols that are not defined here expect funds to -/// be in the router at the time of swap and do the transfer themselves from msg.sender +/// be in the router at the time of swap and do the transfer themselves from `msg.sender` pub static IN_TRANSFER_REQUIRED_PROTOCOLS: LazyLock> = LazyLock::new(|| { let mut set = HashSet::new(); set.insert("uniswap_v2"); @@ -30,6 +30,7 @@ pub static IN_TRANSFER_REQUIRED_PROTOCOLS: LazyLock> = Laz set.insert("pancakeswap_v3"); set.insert("uniswap_v4"); set.insert("ekubo_v2"); + set.insert("vm:maverick_v2"); set }); diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 34f2a47..24cba3a 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -2318,6 +2318,56 @@ mod tests { write_calldata_to_file("test_single_encoding_strategy_ekubo", hex_calldata.as_str()); } + #[test] + fn test_single_encoding_strategy_maverick() { + // GHO -> (maverick) -> USDC + let maverick_pool = ProtocolComponent { + id: String::from("0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67"), + protocol_system: String::from("vm:maverick_v2"), + ..Default::default() + }; + let token_in = Bytes::from("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"); + let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); + let swap = Swap { + component: maverick_pool, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let swap_encoder_registry = get_swap_encoder_registry(); + let encoder = SingleSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + None, + Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), + false, + ) + .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("1000").unwrap()), + slippage: None, + // Alice + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let (calldata, _) = encoder + .encode_strategy(solution) + .unwrap(); + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_single_encoding_strategy_maverick", hex_calldata.as_str()); + } + #[test] fn test_single_encoding_strategy_usv4_eth_in() { // Performs a single swap from ETH to PEPE using a USV4 pool diff --git a/src/encoding/evm/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs index 735029c..9b4967c 100644 --- a/src/encoding/evm/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -76,7 +76,7 @@ impl SwapEncoderBuilder { "vm:curve" => { Ok(Box::new(CurveSwapEncoder::new(self.executor_address, self.chain, self.config)?)) } - "vm::maverick_v2" => Ok(Box::new(MaverickV2SwapEncoder::new( + "vm:maverick_v2" => Ok(Box::new(MaverickV2SwapEncoder::new( self.executor_address, self.chain, self.config, diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 9a82bfc..b419809 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -620,6 +620,7 @@ impl SwapEncoder for MaverickV2SwapEncoder { bytes_to_address(&swap.token_in)?, component_id, bytes_to_address(&encoding_context.receiver)?, + (encoding_context.transfer_type as u8).to_be_bytes(), ); Ok(args.abi_encode_packed()) } @@ -1545,6 +1546,7 @@ mod tests { #[test] fn test_encode_maverick_v2() { + // GHO -> (maverick) -> USDC let maverick_pool = ProtocolComponent { id: String::from("0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67"), protocol_system: String::from("vm:maverick_v2"), @@ -1565,6 +1567,7 @@ mod tests { router_address: Some(Bytes::default()), group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::TransferToProtocol, }; let encoder = MaverickV2SwapEncoder::new( String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"), @@ -1587,6 +1590,8 @@ mod tests { "14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67", // receiver "1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e", + // transfer from router to protocol + "00", )) .to_lowercase() ); From d09497facad453c8d083ea33742215897399617a Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 30 Apr 2025 11:18:04 -0400 Subject: [PATCH 08/11] chore: Load maverick calldata from file - Also remove irrelevant comment --- foundry/test/TychoRouterProtocolIntegration.t.sol | 1 - foundry/test/assets/calldata.txt | 1 + foundry/test/executors/MaverickV2Executor.t.sol | 8 ++++---- src/encoding/evm/swap_encoder/swap_encoders.rs | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/foundry/test/TychoRouterProtocolIntegration.t.sol b/foundry/test/TychoRouterProtocolIntegration.t.sol index baf8c9d..441c133 100644 --- a/foundry/test/TychoRouterProtocolIntegration.t.sol +++ b/foundry/test/TychoRouterProtocolIntegration.t.sol @@ -192,7 +192,6 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup { deal(GHO_ADDR, ALICE, 1 ether); uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE); - // Approve permit2 vm.startPrank(ALICE); IERC20(GHO_ADDR).approve(tychoRouterAddr, type(uint256).max); diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index 320a1fd..2ebfdc5 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -25,3 +25,4 @@ test_ekubo_encode_swap_multi:00ca4f73fe97d0b987a0d12b39bbd562c779bab6f6000000000 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001 test_single_encoding_strategy_maverick:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc201000000000000000000000000000000 +test_encode_maverick_v2:40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00 diff --git a/foundry/test/executors/MaverickV2Executor.t.sol b/foundry/test/executors/MaverickV2Executor.t.sol index ba08663..755570d 100644 --- a/foundry/test/executors/MaverickV2Executor.t.sol +++ b/foundry/test/executors/MaverickV2Executor.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.26; import "@src/executors/MaverickV2Executor.sol"; -import {Test} from "../../lib/forge-std/src/Test.sol"; import {Constants} from "../Constants.sol"; +import "../TestUtils.sol"; contract MaverickV2ExecutorExposed is MaverickV2Executor { constructor(address _factory, address _permit2) @@ -24,7 +24,7 @@ contract MaverickV2ExecutorExposed is MaverickV2Executor { } } -contract MaverickV2ExecutorTest is Test, Constants { +contract MaverickV2ExecutorTest is TestUtils, Constants { using SafeERC20 for IERC20; MaverickV2ExecutorExposed maverickV2Exposed; @@ -92,7 +92,7 @@ contract MaverickV2ExecutorTest is Test, Constants { function testDecodeIntegration() public view { // Generated by the SwapEncoder - test_encode_maverick_v2 bytes memory protocolData = - hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00"; + loadCallDataFromFile("test_encode_maverick_v2"); ( IERC20 tokenIn, @@ -113,7 +113,7 @@ contract MaverickV2ExecutorTest is Test, Constants { function testSwapIntegration() public { // Generated by the SwapEncoder - test_encode_maverick_v2 bytes memory protocolData = - hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00"; + loadCallDataFromFile("test_encode_maverick_v2"); uint256 amountIn = 10 ** 18; deal(GHO_ADDR, address(maverickV2Exposed), amountIn); diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index b419809..5fb82fe 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -643,6 +643,7 @@ mod tests { models::{protocol::ProtocolComponent, Chain as TychoCoreChain}, Bytes, }; + use crate::encoding::evm::utils::write_calldata_to_file; use super::*; use crate::encoding::models::TransferType; @@ -1595,5 +1596,7 @@ mod tests { )) .to_lowercase() ); + + write_calldata_to_file("test_encode_maverick_v2", hex_swap.as_str()); } } From 5f7ce7d5da67e6769ec8ec83964702faf7f164ec Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 30 Apr 2025 11:20:52 -0400 Subject: [PATCH 09/11] chore: nightly fmt --- src/encoding/evm/swap_encoder/swap_encoders.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 5fb82fe..5e74855 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -643,10 +643,9 @@ mod tests { models::{protocol::ProtocolComponent, Chain as TychoCoreChain}, Bytes, }; - use crate::encoding::evm::utils::write_calldata_to_file; use super::*; - use crate::encoding::models::TransferType; + use crate::encoding::{evm::utils::write_calldata_to_file, models::TransferType}; mod uniswap_v2 { use super::*; From 58e8e67494b02b67d4dca39a009b189f968161f3 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 30 Apr 2025 20:24:21 -0400 Subject: [PATCH 10/11] chore: dummy commit CI won't run --- src/encoding/tycho_encoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/tycho_encoder.rs b/src/encoding/tycho_encoder.rs index 16e0bb0..b724c39 100644 --- a/src/encoding/tycho_encoder.rs +++ b/src/encoding/tycho_encoder.rs @@ -10,7 +10,7 @@ pub trait TychoEncoder { /// /// # Arguments /// * `solutions` - Vector of solutions to encode, each potentially using different setups (swap - /// paths, protocols, etc.) + /// paths, protocols, wrapping, etc.) /// /// # Returns /// * `Result, EncodingError>` - Vector of executable transactions From b43d5cad96da3adf6ac72658ba96a7c29e675c82 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 1 May 2025 00:39:21 +0000 Subject: [PATCH 11/11] chore(release): 0.85.0 [skip ci] ## [0.85.0](https://github.com/propeller-heads/tycho-execution/compare/0.84.0...0.85.0) (2025-05-01) ### Features * add mav executor ([0ac722d](https://github.com/propeller-heads/tycho-execution/commit/0ac722d91f6c2f7c09531f5f56f057d1fe014c6f)) * add swap encode ([72a651d](https://github.com/propeller-heads/tycho-execution/commit/72a651d453943d554722d0fa4818255fbec01ce5)) * Transfer Optimizations in MaverickV2 ([bcef8f6](https://github.com/propeller-heads/tycho-execution/commit/bcef8f69f628c3a37a7cb5462e26200ad5aab1ee)) ### Bug Fixes * add maverick for build ([bab30e3](https://github.com/propeller-heads/tycho-execution/commit/bab30e3958ee62091b322ac0bc540939c050ee9e)) * maverick test fork block ([4c93830](https://github.com/propeller-heads/tycho-execution/commit/4c938306bda62d37b8932697f98d0d667a96532b)) * swap test ([d103ca9](https://github.com/propeller-heads/tycho-execution/commit/d103ca9e33f931f18c8af79efa5d77d801bab2e9)) --- CHANGELOG.md | 16 ++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f1fa06..090e14e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## [0.85.0](https://github.com/propeller-heads/tycho-execution/compare/0.84.0...0.85.0) (2025-05-01) + + +### Features + +* add mav executor ([0ac722d](https://github.com/propeller-heads/tycho-execution/commit/0ac722d91f6c2f7c09531f5f56f057d1fe014c6f)) +* add swap encode ([72a651d](https://github.com/propeller-heads/tycho-execution/commit/72a651d453943d554722d0fa4818255fbec01ce5)) +* Transfer Optimizations in MaverickV2 ([bcef8f6](https://github.com/propeller-heads/tycho-execution/commit/bcef8f69f628c3a37a7cb5462e26200ad5aab1ee)) + + +### Bug Fixes + +* add maverick for build ([bab30e3](https://github.com/propeller-heads/tycho-execution/commit/bab30e3958ee62091b322ac0bc540939c050ee9e)) +* maverick test fork block ([4c93830](https://github.com/propeller-heads/tycho-execution/commit/4c938306bda62d37b8932697f98d0d667a96532b)) +* swap test ([d103ca9](https://github.com/propeller-heads/tycho-execution/commit/d103ca9e33f931f18c8af79efa5d77d801bab2e9)) + ## [0.84.0](https://github.com/propeller-heads/tycho-execution/compare/0.83.0...0.84.0) (2025-04-28) diff --git a/Cargo.lock b/Cargo.lock index 7cce6e3..9762d0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4469,7 +4469,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.84.0" +version = "0.85.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index ff9d729..d438640 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.84.0" +version = "0.85.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"