feat: add curve executor with router tests
Took 36 minutes Took 2 minutes
This commit is contained in:
committed by
Diana Carvalho
parent
6c679c0434
commit
7cde5130d6
32
foundry/interfaces/ICurveRouter.sol
Normal file
32
foundry/interfaces/ICurveRouter.sol
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
interface ICurveRouter {
|
||||||
|
function exchange(
|
||||||
|
address[11] memory route,
|
||||||
|
uint256[5][5] memory swapParams,
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 minAmountOut,
|
||||||
|
address[5] memory pools,
|
||||||
|
address receiver
|
||||||
|
) external payable returns (uint256);
|
||||||
|
|
||||||
|
function get_dy(
|
||||||
|
address[] memory route,
|
||||||
|
uint256[] memory swapParams,
|
||||||
|
uint256 amountIn,
|
||||||
|
address[] memory pools
|
||||||
|
) external view returns (uint256);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CurveRouterParams {
|
||||||
|
address[11] route;
|
||||||
|
uint256[5][5] swapParams;
|
||||||
|
uint256 amountIn;
|
||||||
|
uint256 minAmountOut;
|
||||||
|
address[5] pools;
|
||||||
|
address receiver;
|
||||||
|
}
|
||||||
|
|
||||||
41
foundry/src/executors/CurveExecutor.sol
Normal file
41
foundry/src/executors/CurveExecutor.sol
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import "@interfaces/IExecutor.sol";
|
||||||
|
import "@interfaces/ICurveRouter.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
|
||||||
|
contract CurveExecutor is IExecutor {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
ICurveRouter public immutable curveRouter;
|
||||||
|
|
||||||
|
constructor(address _curveRouter) {
|
||||||
|
curveRouter = ICurveRouter(_curveRouter);
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(uint256 amountIn, bytes calldata data)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
CurveRouterParams memory params = _decodeData(data);
|
||||||
|
IERC20(params.route[0]).approve(address(curveRouter), amountIn);
|
||||||
|
return curveRouter.exchange(
|
||||||
|
params.route,
|
||||||
|
params.swapParams,
|
||||||
|
amountIn,
|
||||||
|
params.minAmountOut,
|
||||||
|
params.pools,
|
||||||
|
params.receiver
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _decodeData(bytes calldata data)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (CurveRouterParams memory params)
|
||||||
|
{
|
||||||
|
return abi.decode(data, (CurveRouterParams));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,10 @@ contract Constants is Test, BaseConstants {
|
|||||||
address PANCAKESWAPV3_DEPLOYER_ETHEREUM =
|
address PANCAKESWAPV3_DEPLOYER_ETHEREUM =
|
||||||
0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9;
|
0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9;
|
||||||
|
|
||||||
|
// Curve
|
||||||
|
address TRICRYPTO_USDC_WBTC_WETH =
|
||||||
|
0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B;
|
||||||
|
|
||||||
// Uniswap universal router
|
// Uniswap universal router
|
||||||
address UNIVERSAL_ROUTER = 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af;
|
address UNIVERSAL_ROUTER = 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af;
|
||||||
|
|
||||||
@@ -94,6 +98,9 @@ contract Constants is Test, BaseConstants {
|
|||||||
bytes32 PANCAKEV3_POOL_CODE_INIT_HASH =
|
bytes32 PANCAKEV3_POOL_CODE_INIT_HASH =
|
||||||
0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2;
|
0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2;
|
||||||
|
|
||||||
|
// Curve router
|
||||||
|
address CURVE_ROUTER = 0x16C6521Dff6baB339122a0FE25a9116693265353;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Deploys a dummy contract with non-empty bytecode
|
* @dev Deploys a dummy contract with non-empty bytecode
|
||||||
*/
|
*/
|
||||||
|
|||||||
43
foundry/test/CurveRouterGasTest.t.sol
Normal file
43
foundry/test/CurveRouterGasTest.t.sol
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import {Constants} from "./Constants.sol";
|
||||||
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import {console} from "forge-std/console.sol";
|
||||||
|
import {ICurveRouter} from "../interfaces/ICurveRouter.sol";
|
||||||
|
|
||||||
|
contract CurveRouterGasTest is Constants {
|
||||||
|
ICurveRouter curveRouter = ICurveRouter(CURVE_ROUTER);
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
uint256 forkBlock = 22031795;
|
||||||
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCurveRouter() public {
|
||||||
|
address[11] memory route;
|
||||||
|
route[0] = WETH_ADDR;
|
||||||
|
route[1] = TRICRYPTO_USDC_WBTC_WETH;
|
||||||
|
route[2] = USDC_ADDR;
|
||||||
|
|
||||||
|
uint256[5][5] memory swapParams;
|
||||||
|
swapParams[0][0] = 2; // tokenIn Index
|
||||||
|
swapParams[0][1] = 0; // tokenOut Index
|
||||||
|
swapParams[0][2] = 1; // swap type
|
||||||
|
swapParams[0][3] = 3; // pool type
|
||||||
|
swapParams[0][4] = 3; // n_coins
|
||||||
|
|
||||||
|
uint256 amountIn = 1 ether;
|
||||||
|
uint256 minAmountOut = 0;
|
||||||
|
address[5] memory pools;
|
||||||
|
|
||||||
|
deal(WETH_ADDR, ALICE, amountIn);
|
||||||
|
|
||||||
|
vm.startPrank(ALICE);
|
||||||
|
IERC20(WETH_ADDR).approve(address(curveRouter), amountIn);
|
||||||
|
uint256 amountOut = curveRouter.exchange(
|
||||||
|
route, swapParams, amountIn, minAmountOut, pools, address(this)
|
||||||
|
);
|
||||||
|
vm.stopPrank();
|
||||||
|
}
|
||||||
|
}
|
||||||
91
foundry/test/executors/CurveExecutor.t.sol
Normal file
91
foundry/test/executors/CurveExecutor.t.sol
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import "@src/executors/CurveExecutor.sol";
|
||||||
|
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||||
|
import {Constants} from "../Constants.sol";
|
||||||
|
|
||||||
|
contract CurveExecutorExposed is CurveExecutor {
|
||||||
|
constructor(address _curveRouter) CurveExecutor(_curveRouter) {}
|
||||||
|
|
||||||
|
function decodeParams(bytes calldata data)
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (CurveRouterParams memory params)
|
||||||
|
{
|
||||||
|
return _decodeData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract CurveExecutorTest is Test, Constants {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
CurveExecutorExposed curveExecutorExposed;
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
uint256 forkBlock = 22031795;
|
||||||
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
|
curveExecutorExposed = new CurveExecutorExposed(CURVE_ROUTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDecodeParams() public view {
|
||||||
|
address[11] memory route;
|
||||||
|
route[0] = WETH_ADDR;
|
||||||
|
route[1] = TRICRYPTO_USDC_WBTC_WETH;
|
||||||
|
route[2] = USDC_ADDR;
|
||||||
|
|
||||||
|
uint256[5][5] memory swapParams;
|
||||||
|
swapParams[0][0] = 2; // tokenIn Index
|
||||||
|
swapParams[0][1] = 0; // tokenOut Index
|
||||||
|
swapParams[0][2] = 1; // swap type
|
||||||
|
swapParams[0][3] = 3; // pool type
|
||||||
|
swapParams[0][4] = 3; // n_coins
|
||||||
|
|
||||||
|
uint256 amountIn = 1 ether;
|
||||||
|
uint256 minAmountOut = 0;
|
||||||
|
address[5] memory pools;
|
||||||
|
|
||||||
|
bytes memory data = abi.encode(
|
||||||
|
route, swapParams, amountIn, minAmountOut, pools, address(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
CurveRouterParams memory params =
|
||||||
|
curveExecutorExposed.decodeParams(data);
|
||||||
|
|
||||||
|
assertEq(params.route[0], WETH_ADDR);
|
||||||
|
assertEq(params.route[1], TRICRYPTO_USDC_WBTC_WETH);
|
||||||
|
assertEq(params.route[2], USDC_ADDR);
|
||||||
|
assertEq(params.swapParams[0][0], 2);
|
||||||
|
assertEq(params.swapParams[0][1], 0);
|
||||||
|
assertEq(params.swapParams[0][2], 1);
|
||||||
|
assertEq(params.swapParams[0][3], 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSwapCurve() public {
|
||||||
|
address[11] memory route;
|
||||||
|
route[0] = WETH_ADDR;
|
||||||
|
route[1] = TRICRYPTO_USDC_WBTC_WETH;
|
||||||
|
route[2] = USDC_ADDR;
|
||||||
|
|
||||||
|
uint256[5][5] memory swapParams;
|
||||||
|
swapParams[0][0] = 2; // tokenIn Index
|
||||||
|
swapParams[0][1] = 0; // tokenOut Index
|
||||||
|
swapParams[0][2] = 1; // swap type
|
||||||
|
swapParams[0][3] = 3; // pool type
|
||||||
|
swapParams[0][4] = 3; // n_coins
|
||||||
|
|
||||||
|
uint256 amountIn = 1 ether;
|
||||||
|
uint256 minAmountOut = 0;
|
||||||
|
address[5] memory pools;
|
||||||
|
|
||||||
|
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
|
bytes memory data = abi.encode(
|
||||||
|
route, swapParams, amountIn, minAmountOut, pools, address(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
|
assertEq(amountOut, 1861130973);
|
||||||
|
assertEq(IERC20(USDC_ADDR).balanceOf(address(this)), amountOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user