Merge pull request #159 from propeller-heads/router/dc/ENG-4307-deploy-curve

feat: Deploy Curve Executor
This commit is contained in:
dianacarvalho1
2025-04-11 16:58:43 +01:00
committed by GitHub
5 changed files with 166 additions and 89 deletions

View File

@@ -8,7 +8,7 @@
"uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70",
"vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91",
"ekubo_v2": "0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D", "ekubo_v2": "0x4f88f6630a33dB05BEa1FeF7Dc7ff7508D1c531D",
"vm:curve": "0x1d1499e622D69689cdf9004d05Ec547d650Ff211" "vm:curve": "0x2751999a30A0026c909c4f1EB92d123254CABa7F"
}, },
"tenderly_ethereum": { "tenderly_ethereum": {
"uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E", "uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E",

View File

@@ -4,84 +4,116 @@ const hre = require("hardhat");
// Comment out the executors you don't want to deploy // Comment out the executors you don't want to deploy
const executors_to_deploy = { const executors_to_deploy = {
"ethereum":[ "ethereum": [
// USV2 - Args: Factory, Pool Init Code Hash // USV2 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [ {
"0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", exchange: "UniswapV2Executor", args: [
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
]}, "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"
// SUSHISWAP - Args: Factory, Pool Init Code Hash ]
{exchange: "UniswapV2Executor", args: [ },
"0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", // SUSHISWAP - Args: Factory, Pool Init Code Hash
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" {
]}, exchange: "UniswapV2Executor", args: [
// PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac",
{exchange: "UniswapV2Executor", args: [ "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303"
"0x1097053Fd2ea711dad45caCcc45EfF7548fCB362", ]
"0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" },
]}, // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash
// USV3 -Args: Factory, Pool Init Code Hash {
{exchange: "UniswapV3Executor", args: [ exchange: "UniswapV2Executor", args: [
"0x1F98431c8aD98523631AE4a59f267346ea31F984", "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d"
]}, ]
// PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash },
{exchange: "UniswapV3Executor", args: [ // USV3 -Args: Factory, Pool Init Code Hash
"0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", {
"0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" exchange: "UniswapV3Executor", args: [
]}, "0x1F98431c8aD98523631AE4a59f267346ea31F984",
// Args: Pool manager "0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
{exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, ]
{exchange: "BalancerV2Executor", args: []}, },
// Args: Ekubo core contract // PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash
{exchange: "EkuboExecutor", args: [ {
"0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444" exchange: "UniswapV3Executor", args: [
]} "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9",
], "0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2"
"base":[ ]
// Args: Factory, Pool Init Code Hash },
{exchange: "UniswapV2Executor", args: [ // Args: Pool manager
"0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6", {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]},
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" {exchange: "BalancerV2Executor", args: []},
]}, // Args: Ekubo core contract
// SUSHISWAP V2 - Args: Factory, Pool Init Code Hash {
{exchange: "UniswapV2Executor", args: [ exchange: "EkuboExecutor", args: [
"0x71524B4f93c58fcbF659783284E38825f0622859", "0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444"
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" ]
]}, },
// PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash // Args: ETH address in curve pools
{exchange: "UniswapV2Executor", args: [ {
"0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", exchange: "CurveExecutor", args: [
"0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d" "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
]}, ]
// USV3 - Args: Factory, Pool Init Code Hash }
{exchange: "UniswapV3Executor", args: [ ],
"0x33128a8fC17869897dcE68Ed026d694621f6FDfD", "base": [
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" // Args: Factory, Pool Init Code Hash
]}, {
// PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash exchange: "UniswapV2Executor", args: [
{exchange: "UniswapV3Executor", args: [ "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6",
"0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9", "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"
"0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2" ]
]}, },
// Args: Pool manager // SUSHISWAP V2 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]}, {
{exchange: "BalancerV2Executor", args: []}, exchange: "UniswapV2Executor", args: [
], "0x71524B4f93c58fcbF659783284E38825f0622859",
"unichain":[ "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303"
// Args: Factory, Pool Init Code Hash ]
{exchange: "UniswapV2Executor", args: [ },
"0x1f98400000000000000000000000000000000002", // PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" {
]}, exchange: "UniswapV2Executor", args: [
// USV3 - Args: Factory, Pool Init Code Hash "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
{exchange: "UniswapV3Executor", args: [ "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d"
"0x1f98400000000000000000000000000000000003", ]
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54" },
]}, // USV3 - Args: Factory, Pool Init Code Hash
// Args: Pool manager {
{exchange: "UniswapV4Executor", args: ["0x1f98400000000000000000000000000000000004"]}, 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() { async function main() {

View File

@@ -62,7 +62,7 @@ contract CurveExecutor is IExecutor {
if (tokenApprovalNeeded && tokenIn != nativeToken) { if (tokenApprovalNeeded && tokenIn != nativeToken) {
// slither-disable-next-line unused-return // 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 /// Inspired by Curve's router contract: https://github.com/curvefi/curve-router-ng/blob/9ab006ca848fc7f1995b6fbbecfecc1e0eb29e2a/contracts/Router.vy#L44

View File

@@ -266,16 +266,16 @@ contract CurveExecutorTest is Test, Constants {
function testStableSwapPool() public { function testStableSwapPool() public {
// Swapping CRVUSD -> USDT on a StableSwap pool, deployed by factory 0x4F8846Ae9380B90d2E71D5e3D042dff3E7ebb40d (plain pool) // Swapping CRVUSD -> USDT on a StableSwap pool, deployed by factory 0x4F8846Ae9380B90d2E71D5e3D042dff3E7ebb40d (plain pool)
uint256 amountIn = 1 ether; uint256 amountIn = 1 ether;
deal(CRVUSD_ADDR, address(curveExecutorExposed), amountIn); deal(USDT_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = 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); uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 999910); assertEq(amountOut, 10436946786333182306400100);
assertEq( assertEq(
IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)), IERC20(CRVUSD_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut amountOut
); );
} }

View File

@@ -377,6 +377,7 @@ pub struct CurveSwapEncoder {
meta_registry_address: String, meta_registry_address: String,
native_token_curve_address: String, native_token_curve_address: String,
native_token_address: Bytes, native_token_address: Bytes,
wrapped_native_token_address: Bytes,
} }
impl CurveSwapEncoder { impl CurveSwapEncoder {
@@ -449,10 +450,34 @@ impl CurveSwapEncoder {
let j = U8::from(j_256); let j = U8::from(j_256);
Ok((i, j)) Ok((i, j))
} }
Err(err) => Err(EncodingError::RecoverableError(format!( Err(err) => {
"Curve meta registry call failed with error: {:?}", // Temporary until we get the coin indexes from the indexer
err // 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, executor_address,
meta_registry_address, meta_registry_address,
native_token_address: chain.native_token()?, native_token_address: chain.native_token()?,
wrapped_native_token_address: chain.wrapped_token()?,
native_token_curve_address, native_token_curve_address,
}) })
} }
@@ -535,7 +561,10 @@ impl SwapEncoder for CurveSwapEncoder {
})?) })?)
.map_err(|_| EncodingError::FatalError("Invalid curve factory address".to_string()))?; .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)?; let (i, j) = self.get_coin_indexes(component_address, token_in, token_out)?;
@@ -1165,6 +1194,22 @@ mod tests {
2, 2,
0 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( fn test_curve_get_coin_indexes(
#[case] pool: &str, #[case] pool: &str,
#[case] token_in: &str, #[case] token_in: &str,