diff --git a/foundry/interfaces/ICurveRouter.sol b/foundry/interfaces/ICurveRouter.sol index 357128c..29475f3 100644 --- a/foundry/interfaces/ICurveRouter.sol +++ b/foundry/interfaces/ICurveRouter.sol @@ -7,31 +7,6 @@ pragma solidity ^0.8.26; * @dev This interface allows for executing swaps through Curve's router, which can handle different pool types */ interface ICurveRouter { - - /** - * @notice Parameters for executing a swap through the Curve router - * @dev This struct encapsulates all necessary parameters for a Curve swap - * @param route Array of addresses representing the swap path (tokens and pools) - * @param swapParams 2D array containing swap parameters for each hop: - * [0]: tokenIn index in the pool - * [1]: tokenOut index in the pool - * [2]: swap type (1 for regular swap) - * [3]: pool type (1-4 depending on the Curve pool implementation) - * [4]: number of coins in the pool - * @param amountIn Amount of input token to swap - * @param minAmountOut Minimum amount of output token to receive - * @param pools Array of pool addresses involved in the swap - * @param receiver Address to receive the output tokens - */ - struct CurveRouterParams { - address[11] route; - uint256[5][5] swapParams; - uint256 amountIn; - uint256 minAmountOut; - address[5] pools; - address receiver; - } - /** * @notice Executes a token swap through Curve pools * @dev This function handles the routing of tokens through one or more Curve pools diff --git a/foundry/src/executors/CurveExecutor.sol b/foundry/src/executors/CurveExecutor.sol index c46a138..df13894 100644 --- a/foundry/src/executors/CurveExecutor.sol +++ b/foundry/src/executors/CurveExecutor.sol @@ -11,14 +11,24 @@ contract CurveExecutor is IExecutor { using SafeERC20 for IERC20; ICurveRouter public immutable curveRouter; - address public immutable nativeTokens; + address public immutable nativeToken; - constructor(address _curveRouter, address _nativeTokens) { - if (_curveRouter == address(0) || _nativeTokens == address(0)) { + struct SwapParams { + address[11] route; + uint256[5][5] swapParams; + uint256 amountIn; + uint256 minAmountOut; + address[5] pools; + address receiver; + bool needsApproval; + } + + constructor(address _curveRouter, address _nativeToken) { + if (_curveRouter == address(0) || _nativeToken == address(0)) { revert CurveExecutor__InvalidAddresses(); } curveRouter = ICurveRouter(_curveRouter); - nativeTokens = _nativeTokens; + nativeToken = _nativeToken; } // slither-disable-next-line locked-ether @@ -27,36 +37,30 @@ contract CurveExecutor is IExecutor { payable returns (uint256) { - ICurveRouter.CurveRouterParams memory params = _decodeData(data); - if (params.route[0] != nativeTokens) { - // slither-disable-next-line unused-return - IERC20(params.route[0]).approve(address(curveRouter), amountIn); + SwapParams memory params = _decodeData(data); - return curveRouter.exchange( - params.route, - params.swapParams, - amountIn, - params.minAmountOut, - params.pools, - params.receiver - ); - } else { - return curveRouter.exchange{value: amountIn}( - params.route, - params.swapParams, - amountIn, - params.minAmountOut, - params.pools, - params.receiver + if (params.needsApproval) { + // slither-disable-next-line unused-return + IERC20(params.route[0]).approve( + address(curveRouter), type(uint256).max ); } + // Only add the value parameter when the first token is the native token + return curveRouter.exchange{value: params.route[0] == nativeToken ? amountIn : 0}( + params.route, + params.swapParams, + amountIn, + params.minAmountOut, + params.pools, + params.receiver + ); } function _decodeData(bytes calldata data) internal pure - returns (ICurveRouter.CurveRouterParams memory params) + returns (SwapParams memory params) { - return abi.decode(data, (ICurveRouter.CurveRouterParams)); + return abi.decode(data, (SwapParams)); } } diff --git a/foundry/test/executors/CurveExecutor.t.sol b/foundry/test/executors/CurveExecutor.t.sol index 0dd0e30..b09ecb2 100644 --- a/foundry/test/executors/CurveExecutor.t.sol +++ b/foundry/test/executors/CurveExecutor.t.sol @@ -42,7 +42,7 @@ contract CurveExecutorExposed is CurveExecutor { function decodeParams(bytes calldata data) external pure - returns (ICurveRouter.CurveRouterParams memory params) + returns (SwapParams memory params) { return _decodeData(data); } @@ -79,10 +79,10 @@ contract CurveExecutorTest is Test, Constants { address[5] memory pools; bytes memory data = abi.encode( - route, swapParams, amountIn, minAmountOut, pools, address(this) + route, swapParams, amountIn, minAmountOut, pools, address(this), true ); - ICurveRouter.CurveRouterParams memory params = + CurveExecutor.SwapParams memory params = curveExecutorExposed.decodeParams(data); assertEq(params.route[0], WETH_ADDR); @@ -92,6 +92,11 @@ contract CurveExecutorTest is Test, Constants { assertEq(params.swapParams[0][1], 0); assertEq(params.swapParams[0][2], 1); assertEq(params.swapParams[0][3], 3); + assertEq(params.swapParams[0][4], 3); + assertEq(params.amountIn, amountIn); + assertEq(params.minAmountOut, minAmountOut); + assertEq(params.receiver, address(this)); + assertEq(params.needsApproval, true); } // The following pools are unique and do not have a factory @@ -107,7 +112,7 @@ contract CurveExecutorTest is Test, Constants { deal(DAI_ADDR, address(curveExecutorExposed), amountIn); bytes memory data = abi.encode( - route, swapParams, amountIn, minAmountOut, pools, address(this) + route, swapParams, amountIn, minAmountOut, pools, address(this), true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -127,13 +132,13 @@ contract CurveExecutorTest is Test, Constants { deal(address(curveExecutorExposed), amountIn); bytes memory data = abi.encode( - route, swapParams, amountIn, minAmountOut, pools, address(this) + route, swapParams, amountIn, minAmountOut, pools, address(this), false ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); - assertTrue(amountOut == 1 ether - 1); //// Gets 1 wei less than amountOut - assertEq(IERC20(STETH_ADDR).balanceOf(address(this)), amountOut - 1); + assertTrue(amountOut == 1001072414418410898); + assertEq(IERC20(STETH_ADDR).balanceOf(address(this)), amountOut - 1); //// Gets 1 wei less than amountOut } function testSwapTricrypto2Pool() public { @@ -153,7 +158,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -181,7 +187,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -210,7 +217,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -241,7 +249,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -272,7 +281,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -301,7 +311,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -326,7 +337,7 @@ contract CurveExecutorTest is Test, Constants { deal(WETH_ADDR, address(curveExecutorExposed), amountIn); bytes memory data = abi.encode( - route, swapParams, amountIn, minAmountOut, pools, address(this) + route, swapParams, amountIn, minAmountOut, pools, address(this), true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -352,7 +363,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -383,7 +395,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -411,7 +424,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -439,7 +453,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -467,7 +482,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -495,7 +511,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data); @@ -529,7 +546,8 @@ contract CurveExecutorTest is Test, Constants { amountIn, minAmountOut, pools, - address(curveExecutorExposed) + address(curveExecutorExposed), + true ); uint256 amountOut = curveExecutorExposed.swap(amountIn, data);