From 5d4d6d1ff891766c067e3ff6355ffbb5c50bbf16 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Thu, 10 Apr 2025 12:18:21 +0100 Subject: [PATCH 1/5] feat: Deploy Curve Executor --- don't change below this line --- ENG-4307 Took 27 minutes --- config/executor_addresses.json | 2 +- foundry/scripts/deploy-executors.js | 188 ++++++++++++++++------------ 2 files changed, 111 insertions(+), 79 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 89ebca7..f6b0579 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -8,7 +8,7 @@ "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", "ekubo_v2": "0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D", - "vm:curve": "0x1d1499e622D69689cdf9004d05Ec547d650Ff211" + "vm:curve": "0x8495985edA37081173A00F2F7d2cd55D42fecE35" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", diff --git a/foundry/scripts/deploy-executors.js b/foundry/scripts/deploy-executors.js index fa32960..7149d63 100644 --- a/foundry/scripts/deploy-executors.js +++ b/foundry/scripts/deploy-executors.js @@ -4,84 +4,116 @@ const hre = require("hardhat"); // Comment out the executors you don't want to deploy const executors_to_deploy = { - "ethereum":[ - // USV2 - Args: Factory, Pool Init Code Hash - {exchange: "UniswapV2Executor", args: [ - "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", - "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" - ]}, - // SUSHISWAP - Args: Factory, Pool Init Code Hash - {exchange: "UniswapV2Executor", args: [ - "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", - "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" - ]}, - // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash - {exchange: "UniswapV2Executor", args: [ - "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362", - "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" - ]}, - // USV3 -Args: Factory, Pool Init Code Hash - {exchange: "UniswapV3Executor", args: [ - "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: []}, - // Args: Ekubo core contract - {exchange: "EkuboExecutor", args: [ - "0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444" - ]} - ], - "base":[ - // Args: Factory, Pool Init Code Hash - {exchange: "UniswapV2Executor", args: [ - "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6", - "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" - ]}, - // SUSHISWAP V2 - Args: Factory, Pool Init Code Hash - {exchange: "UniswapV2Executor", args: [ - "0x71524B4f93c58fcbF659783284E38825f0622859", - "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" - ]}, - // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash - {exchange: "UniswapV2Executor", args: [ - "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", - "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" - ]}, - // USV3 - Args: Factory, Pool Init Code Hash - {exchange: "UniswapV3Executor", args: [ - "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: []}, - ], - "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"]}, - ], + "ethereum": [ + // USV2 - Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV2Executor", args: [ + "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", + "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" + ] + }, + // SUSHISWAP - Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV2Executor", args: [ + "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", + "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" + ] + }, + // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV2Executor", args: [ + "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362", + "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" + ] + }, + // USV3 -Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV3Executor", args: [ + "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: []}, + // Args: Ekubo core contract + { + exchange: "EkuboExecutor", args: [ + "0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444" + ] + }, + // Args: ETH address in curve pools + { + exchange: "CurveExecutor", args: [ + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + ] + } + ], + "base": [ + // Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV2Executor", args: [ + "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6", + "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" + ] + }, + // SUSHISWAP V2 - Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV2Executor", args: [ + "0x71524B4f93c58fcbF659783284E38825f0622859", + "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" + ] + }, + // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV2Executor", args: [ + "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", + "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" + ] + }, + // USV3 - Args: Factory, Pool Init Code Hash + { + exchange: "UniswapV3Executor", args: [ + "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: []}, + ], + "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() { From 9e68ab8b0127831ee9dbc1f168e8aac9e28991c0 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Thu, 10 Apr 2025 12:38:39 +0100 Subject: [PATCH 2/5] fix: Checksum curve pool addresses --- don't change below this line --- ENG-4307 Took 5 minutes --- src/encoding/evm/swap_encoder/swap_encoders.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index cf41e88..c6a5d49 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -535,7 +535,10 @@ impl SwapEncoder for CurveSwapEncoder { })?) .map_err(|_| EncodingError::FatalError("Invalid curve factory address".to_string()))?; - let pool_type = self.get_pool_type(&swap.component.id, &factory_address.to_string())?; + let pool_address = Address::from_str(&swap.component.id) + .map_err(|_| EncodingError::FatalError("Invalid curve pool address".to_string()))?; + let pool_type = + self.get_pool_type(&pool_address.to_string(), &factory_address.to_string())?; let (i, j) = self.get_coin_indexes(component_address, token_in, token_out)?; From c963f3b2f61e9d1a6e333149b091c3df90fd857b Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Thu, 10 Apr 2025 15:44:02 +0100 Subject: [PATCH 3/5] fix: Use forceApprove instead of regular Approve This is necessary for USDT (it was failing) --- don't change below this line --- ENG-4307 Took 29 minutes --- foundry/src/executors/CurveExecutor.sol | 2 +- foundry/test/executors/CurveExecutor.t.sol | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/foundry/src/executors/CurveExecutor.sol b/foundry/src/executors/CurveExecutor.sol index 569072f..5a72019 100644 --- a/foundry/src/executors/CurveExecutor.sol +++ b/foundry/src/executors/CurveExecutor.sol @@ -62,7 +62,7 @@ contract CurveExecutor is IExecutor { if (tokenApprovalNeeded && tokenIn != nativeToken) { // slither-disable-next-line unused-return - IERC20(tokenIn).approve(address(pool), type(uint256).max); + IERC20(tokenIn).forceApprove(address(pool), type(uint256).max); } /// Inspired by Curve's router contract: https://github.com/curvefi/curve-router-ng/blob/9ab006ca848fc7f1995b6fbbecfecc1e0eb29e2a/contracts/Router.vy#L44 diff --git a/foundry/test/executors/CurveExecutor.t.sol b/foundry/test/executors/CurveExecutor.t.sol index cd15079..601e54e 100644 --- a/foundry/test/executors/CurveExecutor.t.sol +++ b/foundry/test/executors/CurveExecutor.t.sol @@ -266,16 +266,16 @@ contract CurveExecutorTest is Test, Constants { function testStableSwapPool() public { // Swapping CRVUSD -> USDT on a StableSwap pool, deployed by factory 0x4F8846Ae9380B90d2E71D5e3D042dff3E7ebb40d (plain pool) uint256 amountIn = 1 ether; - deal(CRVUSD_ADDR, address(curveExecutorExposed), amountIn); + deal(USDT_ADDR, address(curveExecutorExposed), amountIn); bytes memory data = - _getData(CRVUSD_ADDR, USDT_ADDR, CRVUSD_USDT_POOL, 1); + _getData(USDT_ADDR, CRVUSD_ADDR, CRVUSD_USDT_POOL, 1); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); - assertEq(amountOut, 999910); + assertEq(amountOut, 10436946786333182306400100); assertEq( - IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)), + IERC20(CRVUSD_ADDR).balanceOf(address(curveExecutorExposed)), amountOut ); } From 916c2b7dba2c1c424efcbf884932a05427816cf8 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Thu, 10 Apr 2025 15:52:21 +0100 Subject: [PATCH 4/5] feat: Add new CurveExecutor address --- don't change below this line --- ENG-4307 Took 5 minutes --- 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 f6b0579..e7a8efc 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -8,7 +8,7 @@ "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", "ekubo_v2": "0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D", - "vm:curve": "0x8495985edA37081173A00F2F7d2cd55D42fecE35" + "vm:curve": "0x2751999a30A0026c909c4f1EB92d123254CABa7F" }, "tenderly_ethereum": { "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", From 2e8392ab40c6c0e99089fae71873755dedb6e925 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Fri, 11 Apr 2025 16:17:37 +0100 Subject: [PATCH 5/5] fix: Support pools that hold ETH but the coin is WETH --- don't change below this line --- ENG-4307 Took 1 hour 46 minutes --- .../evm/swap_encoder/swap_encoders.rs | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index c6a5d49..8ce45da 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -377,6 +377,7 @@ pub struct CurveSwapEncoder { meta_registry_address: String, native_token_curve_address: String, native_token_address: Bytes, + wrapped_native_token_address: Bytes, } impl CurveSwapEncoder { @@ -449,10 +450,34 @@ impl CurveSwapEncoder { let j = U8::from(j_256); Ok((i, j)) } - Err(err) => Err(EncodingError::RecoverableError(format!( - "Curve meta registry call failed with error: {:?}", - err - ))), + Err(err) => { + // Temporary until we get the coin indexes from the indexer + // This is because some curve pools hold ETH but the coin is defined as WETH + // Our indexer reports this pool as holding ETH but then here we need to use WETH + // This is valid only for some pools, that's why we are doing the trial and error + // approach + let native_token_curve_address = + Address::from_str(&self.native_token_curve_address).map_err(|_| { + EncodingError::FatalError( + "Invalid Curve native token curve address".to_string(), + ) + })?; + if token_in != native_token_curve_address && token_out != native_token_curve_address + { + Err(EncodingError::RecoverableError(format!( + "Curve meta registry call failed with error: {:?}", + err + ))) + } else { + let wrapped_token = bytes_to_address(&self.wrapped_native_token_address)?; + let (i, j) = if token_in == native_token_curve_address { + self.get_coin_indexes(pool_id, wrapped_token, token_out)? + } else { + self.get_coin_indexes(pool_id, token_in, wrapped_token)? + }; + Ok((i, j)) + } + } } } } @@ -482,6 +507,7 @@ impl SwapEncoder for CurveSwapEncoder { executor_address, meta_registry_address, native_token_address: chain.native_token()?, + wrapped_native_token_address: chain.wrapped_token()?, native_token_curve_address, }) } @@ -1168,6 +1194,22 @@ mod tests { 2, 0 )] + // Pool that holds ETH but coin is WETH + #[case( + "0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B", + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + 2, + 0 + )] + // Pool that holds ETH but coin is WETH + #[case( + "0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + 0, + 2 + )] fn test_curve_get_coin_indexes( #[case] pool: &str, #[case] token_in: &str,