feat: Refactor Curve Executor not to use the router
We don't need to use all the functionalities of the Curve Router, only the swap type 1 (exchange). By bypassing the router we can save gas on 2 token transfers and with smaller calldata A nice side effect is that the executor is much more readable and understandable now --- don't change below this line --- ENG-4305 Took 2 hours 25 minutes Took 12 seconds
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: BUSL-1.1
|
|
||||||
pragma solidity ^0.8.26;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Curve Router Interface
|
|
||||||
* @notice Interface for interacting with Curve's router contract for token swaps across various Curve pools
|
|
||||||
* @dev This interface allows for executing swaps through Curve's router, which can handle different pool types
|
|
||||||
*/
|
|
||||||
interface ICurveRouter {
|
|
||||||
/**
|
|
||||||
* @notice Executes a token swap through Curve pools
|
|
||||||
* @dev This function handles the routing of tokens through one or more Curve pools
|
|
||||||
* @dev The parameters are encoded in the `CurveRouterParams` struct
|
|
||||||
* @return Amount of output tokens received from the swap
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2,62 +2,45 @@
|
|||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
import "@interfaces/IExecutor.sol";
|
import "@interfaces/IExecutor.sol";
|
||||||
import "@interfaces/ICurveRouter.sol";
|
|
||||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
|
||||||
error CurveExecutor__InvalidAddresses();
|
error CurveExecutor__InvalidAddresses();
|
||||||
|
|
||||||
|
interface CryptoPool {
|
||||||
|
// slither-disable-next-line naming-convention
|
||||||
|
function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy)
|
||||||
|
external
|
||||||
|
payable;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StablePool {
|
||||||
|
// slither-disable-next-line naming-convention
|
||||||
|
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||||
|
external
|
||||||
|
payable;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CryptoPoolETH {
|
||||||
|
// slither-disable-start naming-convention
|
||||||
|
function exchange(
|
||||||
|
uint256 i,
|
||||||
|
uint256 j,
|
||||||
|
uint256 dx,
|
||||||
|
uint256 min_dy,
|
||||||
|
bool use_eth
|
||||||
|
) external payable;
|
||||||
|
// slither-disable-end naming-convention
|
||||||
|
}
|
||||||
|
|
||||||
contract CurveExecutor is IExecutor {
|
contract CurveExecutor is IExecutor {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
ICurveRouter public immutable curveRouter;
|
|
||||||
address public immutable nativeToken;
|
address public immutable nativeToken;
|
||||||
|
|
||||||
/**
|
constructor(address _nativeToken) {
|
||||||
* @dev Struct representing the parameters for a Curve swap.
|
if (_nativeToken == address(0)) {
|
||||||
*
|
|
||||||
* `route` is an array of [initial token, pool or zap, token, pool or zap, token, ...]
|
|
||||||
* The array is iterated until a pool address of 0x00, then the last given token is transferred to `receiver`.
|
|
||||||
*
|
|
||||||
* `swapParams` is a multidimensional array of [i, j, swap_type, pool_type, n_coins] where:
|
|
||||||
* - i is the index of input token
|
|
||||||
* - j is the index of output token
|
|
||||||
*
|
|
||||||
* The swap_type should be:
|
|
||||||
* 1. for `exchange`
|
|
||||||
* 2. for `exchange_underlying`
|
|
||||||
* 3. for underlying exchange via zap: factory stable metapools with lending base pool `exchange_underlying`
|
|
||||||
* and factory crypto-meta pools underlying exchange (`exchange` method in zap)
|
|
||||||
* 4. for coin -> LP token "exchange" (actually `add_liquidity`)
|
|
||||||
* 5. for lending pool underlying coin -> LP token "exchange" (actually `add_liquidity`)
|
|
||||||
* 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`)
|
|
||||||
* 7. for LP token -> lending or fake pool underlying coin "exchange" (actually `remove_liquidity_one_coin`)
|
|
||||||
* 8. for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH, USDe -> sUSDe
|
|
||||||
*
|
|
||||||
* pool_type: 1 - stable, 2 - twocrypto, 3 - tricrypto, 4 - llamma
|
|
||||||
* 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng
|
|
||||||
*
|
|
||||||
* n_coins is the number of coins in the pool.
|
|
||||||
*
|
|
||||||
* `receiver` is the address of the receiver of the final token.
|
|
||||||
*
|
|
||||||
* `needsApproval` is a flag indicating whether the initial token needs approval before the swap.
|
|
||||||
*
|
|
||||||
* For more see https://github.com/curvefi/curve-router-ng/blob/9ab006ca848fc7f1995b6fbbecfecc1e0eb29e2a/contracts/Router.vy
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct CurveSwapParams {
|
|
||||||
address[11] route;
|
|
||||||
uint256[5][5] swapParams;
|
|
||||||
address receiver;
|
|
||||||
bool needsApproval;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(address _curveRouter, address _nativeToken) {
|
|
||||||
if (_curveRouter == address(0) || _nativeToken == address(0)) {
|
|
||||||
revert CurveExecutor__InvalidAddresses();
|
revert CurveExecutor__InvalidAddresses();
|
||||||
}
|
}
|
||||||
curveRouter = ICurveRouter(_curveRouter);
|
|
||||||
nativeToken = _nativeToken;
|
nativeToken = _nativeToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,30 +50,84 @@ contract CurveExecutor is IExecutor {
|
|||||||
payable
|
payable
|
||||||
returns (uint256)
|
returns (uint256)
|
||||||
{
|
{
|
||||||
CurveSwapParams memory params = _decodeData(data);
|
(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
address pool,
|
||||||
|
uint8 poolType,
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
bool tokenApprovalNeeded
|
||||||
|
) = _decodeData(data);
|
||||||
|
|
||||||
if (params.needsApproval) {
|
if (tokenApprovalNeeded && tokenIn != nativeToken) {
|
||||||
// slither-disable-next-line unused-return
|
// slither-disable-next-line unused-return
|
||||||
IERC20(params.route[0]).approve(
|
IERC20(tokenIn).approve(address(pool), type(uint256).max);
|
||||||
address(curveRouter), type(uint256).max
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// slither-disable-next-line uninitialized-local
|
|
||||||
address[5] memory pools;
|
/// Inspired by Curve's router contract: https://github.com/curvefi/curve-router-ng/blob/9ab006ca848fc7f1995b6fbbecfecc1e0eb29e2a/contracts/Router.vy#L44
|
||||||
return curveRouter.exchange{
|
uint256 balanceBefore = _balanceOf(tokenOut);
|
||||||
value: params.route[0] == nativeToken ? amountIn : 0
|
|
||||||
}(params.route, params.swapParams, amountIn, 0, pools, params.receiver);
|
uint256 ethAmount = 0;
|
||||||
|
if (tokenIn == nativeToken) {
|
||||||
|
ethAmount = amountIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poolType == 1 || poolType == 10) {
|
||||||
|
// stable and stable_ng
|
||||||
|
// slither-disable-next-line arbitrary-send-eth
|
||||||
|
StablePool(pool).exchange{value: ethAmount}(i, j, amountIn, 0);
|
||||||
|
} else {
|
||||||
|
// crypto or llamma
|
||||||
|
if (tokenIn == nativeToken || tokenOut == nativeToken) {
|
||||||
|
// slither-disable-next-line arbitrary-send-eth
|
||||||
|
CryptoPoolETH(pool).exchange{value: ethAmount}(
|
||||||
|
uint256(int256(i)), uint256(int256(j)), amountIn, 0, true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
CryptoPool(pool).exchange(
|
||||||
|
uint256(int256(i)), uint256(int256(j)), amountIn, 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 balanceAfter = _balanceOf(tokenOut);
|
||||||
|
return balanceAfter - balanceBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _decodeData(bytes calldata data)
|
function _decodeData(bytes calldata data)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
returns (CurveSwapParams memory params)
|
returns (
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
address pool,
|
||||||
|
uint8 poolType,
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
bool tokenApprovalNeeded
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return abi.decode(data, (CurveSwapParams));
|
tokenIn = address(bytes20(data[0:20]));
|
||||||
|
tokenOut = address(bytes20(data[20:40]));
|
||||||
|
pool = address(bytes20(data[40:60]));
|
||||||
|
poolType = uint8(data[60]);
|
||||||
|
i = int128(uint128(uint8(data[61])));
|
||||||
|
j = int128(uint128(uint8(data[62])));
|
||||||
|
tokenApprovalNeeded = data[63] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
receive() external payable {
|
receive() external payable {
|
||||||
require(msg.sender.code.length != 0);
|
require(msg.sender.code.length != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _balanceOf(address token)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
balance = token == nativeToken
|
||||||
|
? address(this).balance
|
||||||
|
: IERC20(token).balanceOf(address(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,9 +130,6 @@ 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;
|
|
||||||
|
|
||||||
// Curve meta registry
|
// Curve meta registry
|
||||||
address CURVE_META_REGISTRY = 0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC;
|
address CURVE_META_REGISTRY = 0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC;
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
// 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 {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_POOL;
|
|
||||||
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);
|
|
||||||
curveRouter.exchange(
|
|
||||||
route, swapParams, amountIn, minAmountOut, pools, address(this)
|
|
||||||
);
|
|
||||||
vm.stopPrank();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,14 +35,20 @@ interface IAaveLendingPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contract CurveExecutorExposed is CurveExecutor {
|
contract CurveExecutorExposed is CurveExecutor {
|
||||||
constructor(address _curveRouter, address _nativeToken)
|
constructor(address _nativeToken) CurveExecutor(_nativeToken) {}
|
||||||
CurveExecutor(_curveRouter, _nativeToken)
|
|
||||||
{}
|
|
||||||
|
|
||||||
function decodeParams(bytes calldata data)
|
function decodeData(bytes calldata data)
|
||||||
external
|
external
|
||||||
pure
|
pure
|
||||||
returns (CurveSwapParams memory params)
|
returns (
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
address pool,
|
||||||
|
uint8 poolType,
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
bool tokenApprovalNeeded
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return _decodeData(data);
|
return _decodeData(data);
|
||||||
}
|
}
|
||||||
@@ -57,86 +63,78 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
function setUp() public {
|
function setUp() public {
|
||||||
uint256 forkBlock = 22031795;
|
uint256 forkBlock = 22031795;
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
curveExecutorExposed = new CurveExecutorExposed(CURVE_ROUTER, ETH_ADDR);
|
curveExecutorExposed = new CurveExecutorExposed(ETH_ADDR);
|
||||||
metaRegistry = MetaRegistry(CURVE_META_REGISTRY);
|
metaRegistry = MetaRegistry(CURVE_META_REGISTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeParams() public view {
|
function testDecodeParams() public view {
|
||||||
address[11] memory route =
|
bytes memory data = abi.encodePacked(
|
||||||
_getRoute(WETH_ADDR, USDC_ADDR, TRICRYPTO_POOL);
|
WETH_ADDR,
|
||||||
|
USDC_ADDR,
|
||||||
|
TRICRYPTO_POOL,
|
||||||
|
uint8(3),
|
||||||
|
uint8(2),
|
||||||
|
uint8(0),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// The meta registry does not have information about the pool.
|
(
|
||||||
// We manually set the swap params.
|
address tokenIn,
|
||||||
uint256[5][5] memory swapParams;
|
address tokenOut,
|
||||||
swapParams[0][0] = 2; // tokenIn Index
|
address pool,
|
||||||
swapParams[0][1] = 0; // tokenOut Index
|
uint8 poolType,
|
||||||
swapParams[0][2] = 1; // swap type
|
int128 i,
|
||||||
swapParams[0][3] = 3; // pool type
|
int128 j,
|
||||||
swapParams[0][4] = 3; // n_coins
|
bool tokenApprovalNeeded
|
||||||
|
) = curveExecutorExposed.decodeData(data);
|
||||||
|
|
||||||
bytes memory data = abi.encode(route, swapParams, address(this), true);
|
assertEq(tokenIn, WETH_ADDR);
|
||||||
|
assertEq(tokenOut, USDC_ADDR);
|
||||||
CurveExecutor.CurveSwapParams memory params =
|
assertEq(pool, TRICRYPTO_POOL);
|
||||||
curveExecutorExposed.decodeParams(data);
|
assertEq(poolType, 3);
|
||||||
|
assertEq(i, 2);
|
||||||
assertEq(params.route[0], WETH_ADDR);
|
assertEq(j, 0);
|
||||||
assertEq(params.route[1], TRICRYPTO_POOL);
|
assertEq(tokenApprovalNeeded, true);
|
||||||
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);
|
|
||||||
assertEq(params.swapParams[0][4], 3);
|
|
||||||
assertEq(params.receiver, address(this));
|
|
||||||
assertEq(params.needsApproval, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testTriPool() public {
|
function testTriPool() public {
|
||||||
// Swapping DAI -> USDC on TriPool 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7
|
// Swapping DAI -> USDC on TriPool 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7
|
||||||
address[11] memory route = _getRoute(DAI_ADDR, USDC_ADDR, TRIPOOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(TRIPOOL, DAI_ADDR, USDC_ADDR, 1, 1);
|
|
||||||
|
|
||||||
uint256 amountIn = 1 ether;
|
uint256 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(DAI_ADDR, address(curveExecutorExposed), amountIn);
|
deal(DAI_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data = abi.encode(route, swapParams, address(this), true);
|
|
||||||
|
bytes memory data = _getData(DAI_ADDR, USDC_ADDR, TRIPOOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
assertEq(amountOut, 999796);
|
assertEq(amountOut, 999797);
|
||||||
assertEq(IERC20(USDC_ADDR).balanceOf(address(this)), amountOut);
|
assertEq(
|
||||||
|
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
||||||
|
amountOut
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testStEthPool() public {
|
function testStEthPool() public {
|
||||||
// Swapping ETH -> stETH on StEthPool 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022
|
// Swapping ETH -> stETH on StEthPool 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022
|
||||||
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);
|
|
||||||
|
|
||||||
uint256 amountIn = 1 ether;
|
uint256 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(address(curveExecutorExposed), amountIn);
|
deal(address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data = abi.encode(route, swapParams, address(this), false);
|
|
||||||
|
bytes memory data = _getData(ETH_ADDR, STETH_ADDR, STETH_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
assertTrue(amountOut == 1001072414418410898);
|
assertEq(amountOut, 1001072414418410897);
|
||||||
assertEq(IERC20(STETH_ADDR).balanceOf(address(this)), amountOut - 1); //// Gets 1 wei less than amountOut
|
assertEq(
|
||||||
|
IERC20(STETH_ADDR).balanceOf(address(curveExecutorExposed)),
|
||||||
|
amountOut
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testTricrypto2Pool() public {
|
function testTricrypto2Pool() public {
|
||||||
// Swapping WETH -> WBTC on Tricrypto2Pool 0xD51a44d3FaE010294C616388b506AcdA1bfAAE46
|
// Swapping WETH -> WBTC on Tricrypto2Pool 0xD51a44d3FaE010294C616388b506AcdA1bfAAE46
|
||||||
address[11] memory route =
|
|
||||||
_getRoute(WETH_ADDR, WBTC_ADDR, TRICRYPTO2_POOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(TRICRYPTO2_POOL, WETH_ADDR, WBTC_ADDR, 1, 3);
|
|
||||||
|
|
||||||
uint256 amountIn = 1 ether;
|
uint256 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(WETH_ADDR, WBTC_ADDR, TRICRYPTO2_POOL, 3);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -149,15 +147,10 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testSUSDPool() public {
|
function testSUSDPool() public {
|
||||||
// Swapping USDC -> SUSD on SUSDPool 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD
|
// Swapping USDC -> SUSD on SUSDPool 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD
|
||||||
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 amountIn = 100 * 10 ** 6;
|
||||||
|
|
||||||
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
|
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(USDC_ADDR, SUSD_ADDR, SUSD_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -170,20 +163,14 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testFraxUsdcPool() public {
|
function testFraxUsdcPool() public {
|
||||||
// Swapping FRAX -> USDC on FraxUsdcPool 0xDcEF968d416a41Cdac0ED8702fAC8128A64241A2
|
// Swapping FRAX -> USDC on FraxUsdcPool 0xDcEF968d416a41Cdac0ED8702fAC8128A64241A2
|
||||||
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 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(FRAX_ADDR, address(curveExecutorExposed), amountIn);
|
deal(FRAX_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(FRAX_ADDR, USDC_ADDR, FRAX_USDC_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
assertEq(amountOut, 998096);
|
assertEq(amountOut, 998097);
|
||||||
assertEq(
|
assertEq(
|
||||||
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
||||||
amountOut
|
amountOut
|
||||||
@@ -192,16 +179,10 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testUsdeUsdcPool() public {
|
function testUsdeUsdcPool() public {
|
||||||
// Swapping USDC -> USDE on a CryptoSwapNG, deployed by factory 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf (plain pool)
|
// Swapping USDC -> USDE on a CryptoSwapNG, deployed by factory 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf (plain pool)
|
||||||
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 amountIn = 100 * 10 ** 6;
|
||||||
|
|
||||||
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
|
deal(USDC_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(USDC_ADDR, USDE_ADDR, USDE_USDC_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -214,20 +195,15 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testDolaFraxPyusdPool() public {
|
function testDolaFraxPyusdPool() public {
|
||||||
// Swapping DOLA -> FRAXPYUSD on a CryptoSwapNG, deployed by factory 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf (meta pool)
|
// Swapping DOLA -> FRAXPYUSD on a CryptoSwapNG, deployed by factory 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf (meta pool)
|
||||||
address[11] memory route =
|
|
||||||
_getRoute(DOLA_ADDR, FRAXPYUSD_POOL, DOLA_FRAXPYUSD_POOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(DOLA_FRAXPYUSD_POOL, DOLA_ADDR, FRAXPYUSD_POOL, 1, 1);
|
|
||||||
|
|
||||||
uint256 amountIn = 100 * 10 ** 6;
|
uint256 amountIn = 100 * 10 ** 6;
|
||||||
|
|
||||||
deal(DOLA_ADDR, address(curveExecutorExposed), amountIn);
|
deal(DOLA_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
|
|
||||||
bytes memory data =
|
bytes memory data =
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
_getData(DOLA_ADDR, FRAXPYUSD_POOL, DOLA_FRAXPYUSD_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
assertEq(amountOut, 99688991);
|
assertEq(amountOut, 99688992);
|
||||||
assertEq(
|
assertEq(
|
||||||
IERC20(FRAXPYUSD_POOL).balanceOf(address(curveExecutorExposed)),
|
IERC20(FRAXPYUSD_POOL).balanceOf(address(curveExecutorExposed)),
|
||||||
amountOut
|
amountOut
|
||||||
@@ -236,15 +212,11 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testCryptoPoolWithETH() public {
|
function testCryptoPoolWithETH() public {
|
||||||
// Swapping XYO -> ETH on a CryptoPool, deployed by factory 0xF18056Bbd320E96A48e3Fbf8bC061322531aac99
|
// Swapping XYO -> ETH on a CryptoPool, deployed by factory 0xF18056Bbd320E96A48e3Fbf8bC061322531aac99
|
||||||
address[11] memory route = _getRoute(XYO_ADDR, ETH_ADDR, ETH_XYO_POOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(ETH_XYO_POOL, XYO_ADDR, ETH_ADDR, 1, 2);
|
|
||||||
|
|
||||||
uint256 amountIn = 1 ether;
|
uint256 amountIn = 1 ether;
|
||||||
uint256 initialBalance = address(curveExecutorExposed).balance; // this address already has some ETH assigned to it
|
uint256 initialBalance = address(curveExecutorExposed).balance; // this address already has some ETH assigned to it
|
||||||
deal(XYO_ADDR, address(curveExecutorExposed), amountIn);
|
deal(XYO_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(XYO_ADDR, ETH_ADDR, ETH_XYO_POOL, 2);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -256,16 +228,10 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testCryptoPool() public {
|
function testCryptoPool() public {
|
||||||
// Swapping BSGG -> USDT on a CryptoPool, deployed by factory 0xF18056Bbd320E96A48e3Fbf8bC061322531aac99
|
// Swapping BSGG -> USDT on a CryptoPool, deployed by factory 0xF18056Bbd320E96A48e3Fbf8bC061322531aac99
|
||||||
address[11] memory route =
|
|
||||||
_getRoute(BSGG_ADDR, USDT_ADDR, BSGG_USDT_POOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(BSGG_USDT_POOL, BSGG_ADDR, USDT_ADDR, 1, 2);
|
|
||||||
|
|
||||||
uint256 amountIn = 1000 ether;
|
uint256 amountIn = 1000 ether;
|
||||||
|
|
||||||
deal(BSGG_ADDR, address(curveExecutorExposed), amountIn);
|
deal(BSGG_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(BSGG_ADDR, USDT_ADDR, BSGG_USDT_POOL, 2);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -278,33 +244,26 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testTricryptoPool() public {
|
function testTricryptoPool() public {
|
||||||
// Swapping WETH -> USDC on a Tricrypto pool, deployed by factory 0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963
|
// Swapping WETH -> USDC on a Tricrypto pool, deployed by factory 0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963
|
||||||
address[11] memory route =
|
|
||||||
_getRoute(WETH_ADDR, USDC_ADDR, TRICRYPTO_POOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(TRICRYPTO_POOL, WETH_ADDR, USDC_ADDR, 1, 2);
|
|
||||||
|
|
||||||
uint256 amountIn = 1 ether;
|
uint256 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
deal(WETH_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data = abi.encode(route, swapParams, address(this), true);
|
|
||||||
|
bytes memory data = _getData(WETH_ADDR, USDC_ADDR, TRICRYPTO_POOL, 2);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
assertEq(amountOut, 1861130973);
|
assertEq(amountOut, 1861130974);
|
||||||
assertEq(IERC20(USDC_ADDR).balanceOf(address(this)), amountOut);
|
assertEq(
|
||||||
|
IERC20(USDC_ADDR).balanceOf(address(curveExecutorExposed)),
|
||||||
|
amountOut
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testTwoCryptoPool() public {
|
function testTwoCryptoPool() public {
|
||||||
// Swapping UWU -> WETH on a Twocrypto pool, deployed by factory 0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f
|
// Swapping UWU -> WETH on a Twocrypto pool, deployed by factory 0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f
|
||||||
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 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(UWU_ADDR, address(curveExecutorExposed), amountIn);
|
deal(UWU_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
bytes memory data =
|
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
bytes memory data = _getData(UWU_ADDR, WETH_ADDR, UWU_WETH_POOL, 2);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -317,16 +276,11 @@ 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)
|
||||||
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 amountIn = 1 ether;
|
||||||
|
|
||||||
deal(CRVUSD_ADDR, address(curveExecutorExposed), amountIn);
|
deal(CRVUSD_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
|
|
||||||
bytes memory data =
|
bytes memory data =
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
_getData(CRVUSD_ADDR, USDT_ADDR, CRVUSD_USDT_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
@@ -339,52 +293,47 @@ contract CurveExecutorTest is Test, Constants {
|
|||||||
|
|
||||||
function testMetaPool() public {
|
function testMetaPool() public {
|
||||||
// Swapping WTAO -> WSTTAO on a MetaPool deployed by factory 0xB9fC157394Af804a3578134A6585C0dc9cc990d4 (plain pool)
|
// Swapping WTAO -> WSTTAO on a MetaPool deployed by factory 0xB9fC157394Af804a3578134A6585C0dc9cc990d4 (plain pool)
|
||||||
address[11] memory route =
|
|
||||||
_getRoute(WTAO_ADDR, WSTTAO_ADDR, WSTTAO_WTAO_POOL);
|
|
||||||
uint256[5][5] memory swapParams =
|
|
||||||
_getSwapParams(WSTTAO_WTAO_POOL, WTAO_ADDR, WSTTAO_ADDR, 1, 1);
|
|
||||||
|
|
||||||
uint256 amountIn = 100 * 10 ** 9; // 9 decimals
|
uint256 amountIn = 100 * 10 ** 9; // 9 decimals
|
||||||
|
|
||||||
deal(WTAO_ADDR, address(curveExecutorExposed), amountIn);
|
deal(WTAO_ADDR, address(curveExecutorExposed), amountIn);
|
||||||
|
|
||||||
bytes memory data =
|
bytes memory data =
|
||||||
abi.encode(route, swapParams, address(curveExecutorExposed), true);
|
_getData(WTAO_ADDR, WSTTAO_ADDR, WSTTAO_WTAO_POOL, 1);
|
||||||
|
|
||||||
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
|
||||||
|
|
||||||
assertEq(amountOut, 32797923609);
|
assertEq(amountOut, 32797923610);
|
||||||
assertEq(
|
assertEq(
|
||||||
IERC20(WSTTAO_ADDR).balanceOf(address(curveExecutorExposed)),
|
IERC20(WSTTAO_ADDR).balanceOf(address(curveExecutorExposed)),
|
||||||
amountOut
|
amountOut
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getRoute(address tokenIn, address tokenOut, address pool)
|
function _getData(
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (address[11] memory route)
|
|
||||||
{
|
|
||||||
route[0] = tokenIn;
|
|
||||||
route[2] = tokenOut;
|
|
||||||
route[1] = pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getSwapParams(
|
|
||||||
address pool,
|
|
||||||
address tokenIn,
|
address tokenIn,
|
||||||
address tokenOut,
|
address tokenOut,
|
||||||
uint256 swapType,
|
address pool,
|
||||||
uint256 poolType
|
uint8 poolType
|
||||||
) internal view returns (uint256[5][5] memory swapParams) {
|
) internal view returns (bytes memory data) {
|
||||||
uint256 nCoins = metaRegistry.get_n_coins(pool);
|
(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,) =
|
(int128 coinInIndex, int128 coinOutIndex,) =
|
||||||
metaRegistry.get_coin_indices(pool, tokenIn, tokenOut);
|
metaRegistry.get_coin_indices(pool, tokenIn, tokenOut);
|
||||||
|
return (coinInIndex, 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 {
|
function dealAaveDai() internal {
|
||||||
|
|||||||
Reference in New Issue
Block a user