Curve pools use a different address from ETH (native token) - Pass Chain into the SwapEncoderBuilder and SwapEncoder - Add native_token_curve_address and native_token_address to CurveSwapEncoder - Added integration test for this curve case --- don't change below this line --- ENG-4306 Took 1 hour 4 minutes Took 11 seconds
328 lines
11 KiB
Solidity
328 lines
11 KiB
Solidity
// 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";
|
|
|
|
interface ICurvePool {
|
|
function coins(uint256 i) external view returns (address);
|
|
}
|
|
|
|
// Curve pool registry
|
|
// This is the registry that contains the information about the pool
|
|
// The naming convention is different because it is in vyper
|
|
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);
|
|
}
|
|
|
|
contract CurveExecutorExposed is CurveExecutor {
|
|
constructor(address _nativeToken) CurveExecutor(_nativeToken) {}
|
|
|
|
function decodeData(bytes calldata data)
|
|
external
|
|
pure
|
|
returns (
|
|
address tokenIn,
|
|
address tokenOut,
|
|
address pool,
|
|
uint8 poolType,
|
|
int128 i,
|
|
int128 j,
|
|
bool tokenApprovalNeeded
|
|
)
|
|
{
|
|
return _decodeData(data);
|
|
}
|
|
}
|
|
|
|
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(ETH_ADDR_FOR_CURVE);
|
|
metaRegistry = MetaRegistry(CURVE_META_REGISTRY);
|
|
}
|
|
|
|
function testDecodeParams() public view {
|
|
bytes memory data = abi.encodePacked(
|
|
WETH_ADDR,
|
|
USDC_ADDR,
|
|
TRICRYPTO_POOL,
|
|
uint8(3),
|
|
uint8(2),
|
|
uint8(0),
|
|
true
|
|
);
|
|
|
|
(
|
|
address tokenIn,
|
|
address tokenOut,
|
|
address pool,
|
|
uint8 poolType,
|
|
int128 i,
|
|
int128 j,
|
|
bool tokenApprovalNeeded
|
|
) = curveExecutorExposed.decodeData(data);
|
|
|
|
assertEq(tokenIn, WETH_ADDR);
|
|
assertEq(tokenOut, USDC_ADDR);
|
|
assertEq(pool, TRICRYPTO_POOL);
|
|
assertEq(poolType, 3);
|
|
assertEq(i, 2);
|
|
assertEq(j, 0);
|
|
assertEq(tokenApprovalNeeded, true);
|
|
}
|
|
|
|
function testTriPool() public {
|
|
// Swapping DAI -> USDC on TriPool 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7
|
|
uint256 amountIn = 1 ether;
|
|
deal(DAI_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(DAI_ADDR, USDC_ADDR, TRIPOOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 999797);
|
|
assertEq(
|
|
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testStEthPool() public {
|
|
// Swapping ETH -> stETH on StEthPool 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022
|
|
uint256 amountIn = 1 ether;
|
|
deal(address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data =
|
|
_getData(ETH_ADDR_FOR_CURVE, STETH_ADDR, STETH_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 1001072414418410897);
|
|
assertEq(
|
|
IERC20(STETH_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testTricrypto2Pool() public {
|
|
// Swapping WETH -> WBTC on Tricrypto2Pool 0xD51a44d3FaE010294C616388b506AcdA1bfAAE46
|
|
uint256 amountIn = 1 ether;
|
|
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(WETH_ADDR, WBTC_ADDR, TRICRYPTO2_POOL, 3);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 2279618);
|
|
assertEq(
|
|
IERC20(WBTC_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testSUSDPool() public {
|
|
// Swapping USDC -> SUSD on SUSDPool 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD
|
|
uint256 amountIn = 100 * 10 ** 6;
|
|
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(USDC_ADDR, SUSD_ADDR, SUSD_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 100488101605550214590);
|
|
assertEq(
|
|
IERC20(SUSD_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testFraxUsdcPool() public {
|
|
// Swapping FRAX -> USDC on FraxUsdcPool 0xDcEF968d416a41Cdac0ED8702fAC8128A64241A2
|
|
uint256 amountIn = 1 ether;
|
|
deal(FRAX_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(FRAX_ADDR, USDC_ADDR, FRAX_USDC_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 998097);
|
|
assertEq(
|
|
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testUsdeUsdcPool() public {
|
|
// Swapping USDC -> USDE on a CryptoSwapNG, deployed by factory 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf (plain pool)
|
|
uint256 amountIn = 100 * 10 ** 6;
|
|
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(USDC_ADDR, USDE_ADDR, USDE_USDC_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 100064812138999986170);
|
|
assertEq(
|
|
IERC20(USDE_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testDolaFraxPyusdPool() public {
|
|
// Swapping DOLA -> FRAXPYUSD on a CryptoSwapNG, deployed by factory 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf (meta pool)
|
|
uint256 amountIn = 100 * 10 ** 6;
|
|
deal(DOLA_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data =
|
|
_getData(DOLA_ADDR, FRAXPYUSD_POOL, DOLA_FRAXPYUSD_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 99688992);
|
|
assertEq(
|
|
IERC20(FRAXPYUSD_POOL).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testCryptoPoolWithETH() public {
|
|
// Swapping XYO -> ETH on a CryptoPool, deployed by factory 0xF18056Bbd320E96A48e3Fbf8bC061322531aac99
|
|
uint256 amountIn = 1 ether;
|
|
uint256 initialBalance = address(curveExecutorExposed).balance; // this address already has some ETH assigned to it
|
|
deal(XYO_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data =
|
|
_getData(XYO_ADDR, ETH_ADDR_FOR_CURVE, ETH_XYO_POOL, 2);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 6081816039338);
|
|
assertEq(
|
|
address(curveExecutorExposed).balance, initialBalance + amountOut
|
|
);
|
|
}
|
|
|
|
function testCryptoPool() public {
|
|
// Swapping BSGG -> USDT on a CryptoPool, deployed by factory 0xF18056Bbd320E96A48e3Fbf8bC061322531aac99
|
|
uint256 amountIn = 1000 ether;
|
|
deal(BSGG_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(BSGG_ADDR, USDT_ADDR, BSGG_USDT_POOL, 2);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 23429);
|
|
assertEq(
|
|
IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testTricryptoPool() public {
|
|
// Swapping WETH -> USDC on a Tricrypto pool, deployed by factory 0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963
|
|
uint256 amountIn = 1 ether;
|
|
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(WETH_ADDR, USDC_ADDR, TRICRYPTO_POOL, 2);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 1861130974);
|
|
assertEq(
|
|
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testTwoCryptoPool() public {
|
|
// Swapping UWU -> WETH on a Twocrypto pool, deployed by factory 0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f
|
|
uint256 amountIn = 1 ether;
|
|
deal(UWU_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data = _getData(UWU_ADDR, WETH_ADDR, UWU_WETH_POOL, 2);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 2873786684675);
|
|
assertEq(
|
|
IERC20(WETH_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
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);
|
|
|
|
bytes memory data =
|
|
_getData(CRVUSD_ADDR, USDT_ADDR, CRVUSD_USDT_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 999910);
|
|
assertEq(
|
|
IERC20(USDT_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function testMetaPool() public {
|
|
// Swapping WTAO -> WSTTAO on a MetaPool deployed by factory 0xB9fC157394Af804a3578134A6585C0dc9cc990d4 (plain pool)
|
|
uint256 amountIn = 100 * 10 ** 9; // 9 decimals
|
|
deal(WTAO_ADDR, address(curveExecutorExposed), amountIn);
|
|
|
|
bytes memory data =
|
|
_getData(WTAO_ADDR, WSTTAO_ADDR, WSTTAO_WTAO_POOL, 1);
|
|
|
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
|
|
|
assertEq(amountOut, 32797923610);
|
|
assertEq(
|
|
IERC20(WSTTAO_ADDR).balanceOf(address(curveExecutorExposed)),
|
|
amountOut
|
|
);
|
|
}
|
|
|
|
function _getData(
|
|
address tokenIn,
|
|
address tokenOut,
|
|
address pool,
|
|
uint8 poolType
|
|
) internal view returns (bytes memory data) {
|
|
(int128 i, int128 j) = _getIndexes(tokenIn, tokenOut, pool);
|
|
data = abi.encodePacked(
|
|
tokenIn,
|
|
tokenOut,
|
|
pool,
|
|
poolType,
|
|
uint8(uint256(uint128(i))),
|
|
uint8(uint256(uint128(j))),
|
|
true
|
|
);
|
|
}
|
|
|
|
function _getIndexes(address tokenIn, address tokenOut, address pool)
|
|
internal
|
|
view
|
|
returns (int128, int128)
|
|
{
|
|
(int128 coinInIndex, int128 coinOutIndex,) =
|
|
metaRegistry.get_coin_indices(pool, tokenIn, tokenOut);
|
|
return (coinInIndex, coinOutIndex);
|
|
}
|
|
}
|