test: add curve swap tests with pools from indexer

This commit is contained in:
royvardhan
2025-03-14 00:52:11 +05:30
committed by Diana Carvalho
parent 93bdc86dc6
commit c3d36ae25f
4 changed files with 379 additions and 84 deletions

View File

@@ -5,11 +5,18 @@ import "@src/executors/CurveExecutor.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol";
interface ICurvePool {
function coins(uint256 i) external view returns (address);
}
interface MetaRegistry {
function get_n_coins(address pool) external view returns (uint256);
function get_coin_indices(address pool, address from, address to)
external
view
returns (int128, int128, bool);
}
interface ILendingPool {
function deposit(
address asset,
@@ -24,7 +31,9 @@ interface ILendingPool {
}
contract CurveExecutorExposed is CurveExecutor {
constructor(address _curveRouter, address _ethAddress) CurveExecutor(_curveRouter, _ethAddress) {}
constructor(address _curveRouter, address _ethAddress)
CurveExecutor(_curveRouter, _ethAddress)
{}
function decodeParams(bytes calldata data)
external
@@ -39,11 +48,13 @@ contract CurveExecutorTest is Test, Constants {
using SafeERC20 for IERC20;
CurveExecutorExposed curveExecutorExposed;
MetaRegistry metaRegistry;
function setUp() public {
uint256 forkBlock = 22031795;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
curveExecutorExposed = new CurveExecutorExposed(CURVE_ROUTER, ETH_ADDR);
metaRegistry = MetaRegistry(CURVE_META_REGISTRY);
}
function testDecodeParams() public view {
@@ -79,10 +90,10 @@ contract CurveExecutorTest is Test, Constants {
assertEq(params.swapParams[0][3], 3);
}
function testCurveSwapPoolType0() public {
address[11] memory route = _getRoute(ADAI_ADDR, AUSDC_ADDR, AAVE_POOL);
uint256[5][5] memory swapParams = _getSwapParams(AAVE_POOL, ADAI_ADDR, AUSDC_ADDR, 1, 1);
uint256[5][5] memory swapParams =
_getSwapParams(AAVE_POOL, ADAI_ADDR, AUSDC_ADDR, 1, 1);
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0;
@@ -90,16 +101,24 @@ contract CurveExecutorTest is Test, Constants {
dealAaveDai();
bytes memory data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 999734);
assertEq(IERC20(AUSDC_ADDR).balanceOf(address(curveExecutorExposed)), amountOut);
assertEq(
IERC20(AUSDC_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapPoolType1() public {
function testCurveSwapPoolType1() public {
address[11] memory route =
_getRoute(DAI_ADDR, USDC_ADDR, TRIPOOL_USDT_USDC_DAI);
uint256[5][5] memory swapParams =
@@ -142,8 +161,7 @@ contract CurveExecutorTest is Test, Constants {
}
function testCurveSwapPoolType4() public {
address[11] memory route =
_getRoute(ETH_ADDR, STETH_ADDR, STETH_POOL);
address[11] memory route = _getRoute(ETH_ADDR, STETH_ADDR, STETH_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(STETH_POOL, ETH_ADDR, STETH_ADDR, 1, 1);
@@ -153,40 +171,45 @@ contract CurveExecutorTest is Test, Constants {
deal(address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(
amountIn, data
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertTrue(amountOut >= 1 ether);
assertEq(IERC20(STETH_ADDR).balanceOf(address(curveExecutorExposed)), amountOut - 1); // Gets 1 wei less than amountOut
assertEq(
IERC20(STETH_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut - 1
); // Gets 1 wei less than amountOut
// Now reverse the swap
amountIn = amountOut - 1;
route =
_getRoute(STETH_ADDR, ETH_ADDR, STETH_POOL);
swapParams =
_getSwapParams(STETH_POOL, STETH_ADDR, ETH_ADDR, 1, 1);
route = _getRoute(STETH_ADDR, ETH_ADDR, STETH_POOL);
swapParams = _getSwapParams(STETH_POOL, STETH_ADDR, ETH_ADDR, 1, 1);
data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
amountOut = curveExecutorExposed.swap(
amountIn, data
);
amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(address(curveExecutorExposed).balance, 999800010006950374);
}
function testCurveSwapPoolType5() public {
address[11] memory route =
_getRoute(LUSD_ADDR, USDT_ADDR, LUSD_POOL);
address[11] memory route = _getRoute(LUSD_ADDR, USDT_ADDR, LUSD_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(LUSD_POOL, LUSD_ADDR, USDT_ADDR, 2, 1);
_getSwapParams(LUSD_POOL, LUSD_ADDR, USDT_ADDR, 2, 1);
// pool.coins(index) reverts, defaulting tokenOut index to 0
swapParams[0][1] = 3;
@@ -197,24 +220,28 @@ contract CurveExecutorTest is Test, Constants {
deal(LUSD_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(
amountIn, data
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 1001785);
assertEq(IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)), amountOut);
assertEq(
IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapPoolType6() public {
address[11] memory route =
_getRoute(DAI_ADDR, USDC_ADDR, CPOOL);
address[11] memory route = _getRoute(DAI_ADDR, USDC_ADDR, CPOOL);
uint256[5][5] memory swapParams =
_getSwapParams(CPOOL, DAI_ADDR, USDC_ADDR, 2, 1);
_getSwapParams(CPOOL, DAI_ADDR, USDC_ADDR, 2, 1);
// pool.coins(index) reverts, defaulting tokenOut index to 0
swapParams[0][1] = 1;
@@ -224,19 +251,27 @@ contract CurveExecutorTest is Test, Constants {
deal(DAI_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(
amountIn, data);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 999549);
assertEq(IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)), amountOut);
assertEq(
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapPoolType7() public {
address[11] memory route = _getRoute(WETH_ADDR, LDO_ADDR, LDO_POOL);
uint256[5][5] memory swapParams = _getSwapParams(LDO_POOL, WETH_ADDR, LDO_ADDR, 1, 4);
uint256[5][5] memory swapParams =
_getSwapParams(LDO_POOL, WETH_ADDR, LDO_ADDR, 1, 4);
// pool.coins(index) reverts, defaulting tokenOut index to 0
swapParams[0][1] = 1;
@@ -247,20 +282,33 @@ contract CurveExecutorTest is Test, Constants {
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(
amountIn, data);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 2075236672516568049094);
assertEq(IERC20(LDO_ADDR).balanceOf(address(curveExecutorExposed)), amountOut);
assertEq(
IERC20(LDO_ADDR).balanceOf(address(curveExecutorExposed)), amountOut
);
}
function testCurveSwapPoolType8() public {
address[11] memory route = _getRoute(CRV_ADDR, WETH_ADDR, CRV_POOL);
uint256[5][5] memory swapParams = _getSwapParams(CRV_POOL, CRV_ADDR, WETH_ADDR, 1, 4);
// The registry does not have information about the pool.
// We manually set the swap params.
uint256[5][5] memory swapParams;
swapParams[0][0] = 1;
swapParams[0][1] = 0;
swapParams[0][2] = 1;
swapParams[0][3] = 4;
swapParams[0][4] = 2;
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0;
@@ -268,20 +316,253 @@ contract CurveExecutorTest is Test, Constants {
deal(CRV_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route, swapParams, amountIn, minAmountOut, pools, address(curveExecutorExposed)
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(
amountIn, data);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 21806692849);
assertEq(IERC20(WETH_ADDR).balanceOf(address(curveExecutorExposed)), amountOut);
assertEq(
IERC20(WETH_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
/// These tests are taken from the substreams
function testCurveSwapTricrypto2() public {
address[11] memory route =
_getRoute(WETH_ADDR, WBTC_ADDR, TRICRYPTO_USDT_WETH_WBTC);
uint256[5][5] memory swapParams =
_getSwapParams(TRICRYPTO_USDT_WETH_WBTC, WETH_ADDR, WBTC_ADDR, 1, 3);
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(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 2279618);
assertEq(
IERC20(WBTC_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapSUSD() public {
address[11] memory route = _getRoute(USDC_ADDR, SUSD_ADDR, SUSD_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(SUSD_POOL, USDC_ADDR, SUSD_ADDR, 1, 1);
uint256 amountIn = 100 * 10 ** 6;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 100488101605550214590);
assertEq(
IERC20(SUSD_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapFraxUSDC() public {
address[11] memory route =
_getRoute(FRAX_ADDR, USDC_ADDR, FRAX_USDC_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(FRAX_USDC_POOL, FRAX_ADDR, USDC_ADDR, 1, 1);
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(FRAX_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 998096);
assertEq(
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapCryptoSwapNGFactoryPlainPool() public {
address[11] memory route =
_getRoute(USDC_ADDR, USDE_ADDR, USDE_USDC_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(USDE_USDC_POOL, USDC_ADDR, USDE_ADDR, 1, 1);
uint256 amountIn = 100 * 10 ** 6;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 100064812138999986170);
assertEq(
IERC20(USDE_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapMetaPool() public {
address[11] memory route =
_getRoute(DOLA_ADDR, FRAXPYUSD_ADDR, DOLA_FRAXPYUSD_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(DOLA_FRAXPYUSD_POOL, DOLA_ADDR, FRAXPYUSD_ADDR, 1, 1);
uint256 amountIn = 100 * 10 ** 6;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(DOLA_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 99688991);
assertEq(
IERC20(FRAXPYUSD_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapWethXyo() public {
address[11] memory route = _getRoute(XYO_ADDR, WETH_ADDR, WETH_XYO_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(WETH_XYO_POOL, XYO_ADDR, WETH_ADDR, 1, 2);
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(XYO_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 6081816039338);
assertEq(
IERC20(WETH_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapUwuWeth() public {
address[11] memory route = _getRoute(UWU_ADDR, WETH_ADDR, UWU_WETH_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(UWU_WETH_POOL, UWU_ADDR, WETH_ADDR, 1, 2);
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(UWU_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 2873786684675);
assertEq(
IERC20(WETH_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function testCurveSwapStableSwapFactoryPlainPool() public {
address[11] memory route =
_getRoute(CRVUSD_ADDR, USDT_ADDR, CRVUSD_USDT_POOL);
uint256[5][5] memory swapParams =
_getSwapParams(CRVUSD_USDT_POOL, CRVUSD_ADDR, USDT_ADDR, 1, 1);
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0;
address[5] memory pools;
deal(CRVUSD_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data = abi.encode(
route,
swapParams,
amountIn,
minAmountOut,
pools,
address(curveExecutorExposed)
);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 999910);
assertEq(
IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)),
amountOut
);
}
function _getRoute(address tokenIn, address tokenOut, address pool)
internal
@@ -300,43 +581,27 @@ contract CurveExecutorTest is Test, Constants {
uint256 swapType,
uint256 poolType
) internal view returns (uint256[5][5] memory swapParams) {
// Get number of coins in pool and their indices
uint256 coinInIndex;
uint256 coinOutIndex;
uint256 nCoins;
address lastCoinAddress = address(1);
while (lastCoinAddress != address(0)) {
try ICurvePool(pool).coins(nCoins) returns (address coin) {
lastCoinAddress = coin;
nCoins++;
if (coin == tokenIn) {
coinInIndex = nCoins - 1;
}
if (coin == tokenOut) {
coinOutIndex = nCoins - 1;
}
} catch {
lastCoinAddress = address(0);
}
}
uint256 nCoins = metaRegistry.get_n_coins(pool);
(int128 coinInIndex, int128 coinOutIndex, bool isMetapool) =
metaRegistry.get_coin_indices(pool, tokenIn, tokenOut);
swapParams[0][0] = coinInIndex;
swapParams[0][1] = coinOutIndex;
swapParams[0][0] = uint256(int256(coinInIndex));
swapParams[0][1] = uint256(int256(coinOutIndex));
swapParams[0][2] = swapType;
swapParams[0][3] = poolType;
swapParams[0][4] = nCoins;
}
function dealAaveDai() internal {
deal(DAI_ADDR, address(curveExecutorExposed), 100_000 * 10 ** 18);
function dealAaveDai() internal {
deal(DAI_ADDR, address(curveExecutorExposed), 100_000 * 10 ** 18);
ILendingPool aave =
ILendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
vm.startPrank(address(curveExecutorExposed));
IERC20(DAI_ADDR).approve(address(aave), type(uint256).max);
aave.deposit(DAI_ADDR, 100_000 * 10 ** 18, address(curveExecutorExposed), 0);
aave.deposit(
DAI_ADDR, 100_000 * 10 ** 18, address(curveExecutorExposed), 0
);
vm.stopPrank();
}
}