mirror of
https://github.com/lux-amm/v4-periphery.git
synced 2026-10-30 20:54:39 +00:00
214 lines
8.1 KiB
Solidity
214 lines
8.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.24;
|
|
|
|
import "forge-std/Test.sol";
|
|
import {PoolModifyLiquidityTest} from "@luxamm/v4-core/src/test/PoolModifyLiquidityTest.sol";
|
|
import {MockV4Router} from "../mocks/MockV4Router.sol";
|
|
import {Plan, Planner} from "../shared/Planner.sol";
|
|
import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol";
|
|
import {IHooks} from "@luxamm/v4-core/src/interfaces/IHooks.sol";
|
|
import {PathKey} from "../../src/libraries/PathKey.sol";
|
|
import {Actions} from "../../src/libraries/Actions.sol";
|
|
|
|
import {Currency, CurrencyLibrary} from "@luxamm/v4-core/src/types/Currency.sol";
|
|
import {PoolKey} from "@luxamm/v4-core/src/types/PoolKey.sol";
|
|
import {Deployers} from "@luxamm/v4-core/test/utils/Deployers.sol";
|
|
import {IERC20} from "forge-std/interfaces/IERC20.sol";
|
|
import {LiquidityOperations} from "./LiquidityOperations.sol";
|
|
import {IV4Router} from "../../src/interfaces/IV4Router.sol";
|
|
import {ActionConstants} from "../../src/libraries/ActionConstants.sol";
|
|
import {ModifyLiquidityParams, SwapParams} from "@luxamm/v4-core/src/types/PoolOperation.sol";
|
|
|
|
/// @notice A shared test contract that wraps the v4-core deployers contract and exposes basic helpers for swapping with the router.
|
|
contract RoutingTestHelpers is Test, Deployers {
|
|
PoolModifyLiquidityTest positionManager;
|
|
MockV4Router router;
|
|
|
|
uint256 MAX_SETTLE_AMOUNT = type(uint256).max;
|
|
uint256 MIN_TAKE_AMOUNT = 0;
|
|
|
|
// nativeKey is already defined in Deployers.sol
|
|
PoolKey key0;
|
|
PoolKey key1;
|
|
PoolKey key2;
|
|
|
|
// currency0 and currency1 are defined in Deployers.sol
|
|
Currency currency2;
|
|
Currency currency3;
|
|
|
|
Currency[] tokenPath;
|
|
Plan plan;
|
|
|
|
function setupRouterCurrenciesAndPoolsWithLiquidity() public {
|
|
deployFreshManager();
|
|
|
|
router = new MockV4Router(manager);
|
|
positionManager = new PoolModifyLiquidityTest(manager);
|
|
|
|
MockERC20[] memory tokens = deployTokensMintAndApprove(4);
|
|
|
|
currency0 = Currency.wrap(address(tokens[0]));
|
|
currency1 = Currency.wrap(address(tokens[1]));
|
|
currency2 = Currency.wrap(address(tokens[2]));
|
|
currency3 = Currency.wrap(address(tokens[3]));
|
|
|
|
nativeKey = createNativePoolWithLiquidity(currency0, address(0));
|
|
key0 = createPoolWithLiquidity(currency0, currency1, address(0));
|
|
key1 = createPoolWithLiquidity(currency1, currency2, address(0));
|
|
key2 = createPoolWithLiquidity(currency2, currency3, address(0));
|
|
}
|
|
|
|
function deployTokensMintAndApprove(uint8 count) internal returns (MockERC20[] memory) {
|
|
MockERC20[] memory tokens = deployTokens(count, 2 ** 128);
|
|
for (uint256 i = 0; i < count; i++) {
|
|
tokens[i].approve(address(router), type(uint256).max);
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
function createPoolWithLiquidity(Currency currencyA, Currency currencyB, address hookAddr)
|
|
internal
|
|
returns (PoolKey memory _key)
|
|
{
|
|
if (Currency.unwrap(currencyA) > Currency.unwrap(currencyB)) {
|
|
(currencyA, currencyB) = (currencyB, currencyA);
|
|
}
|
|
_key = PoolKey(currencyA, currencyB, 3000, 60, IHooks(hookAddr));
|
|
|
|
manager.initialize(_key, SQRT_PRICE_1_1);
|
|
MockERC20(Currency.unwrap(currencyA)).approve(address(positionManager), type(uint256).max);
|
|
MockERC20(Currency.unwrap(currencyB)).approve(address(positionManager), type(uint256).max);
|
|
positionManager.modifyLiquidity(_key, ModifyLiquidityParams(-887220, 887220, 200 ether, 0), "0x");
|
|
}
|
|
|
|
function createNativePoolWithLiquidity(Currency currency, address hookAddr) internal returns (PoolKey memory _key) {
|
|
_key = PoolKey(CurrencyLibrary.ADDRESS_ZERO, currency, 3000, 60, IHooks(hookAddr));
|
|
|
|
manager.initialize(_key, SQRT_PRICE_1_1);
|
|
MockERC20(Currency.unwrap(currency)).approve(address(positionManager), type(uint256).max);
|
|
positionManager.modifyLiquidity{value: 200 ether}(
|
|
_key, ModifyLiquidityParams(-887220, 887220, 200 ether, 0), "0x"
|
|
);
|
|
}
|
|
|
|
function _getExactInputParams(Currency[] memory _tokenPath, uint256 amountIn)
|
|
internal
|
|
pure
|
|
returns (IV4Router.ExactInputParams memory params)
|
|
{
|
|
PathKey[] memory path = new PathKey[](_tokenPath.length - 1);
|
|
for (uint256 i = 0; i < _tokenPath.length - 1; i++) {
|
|
path[i] = PathKey(_tokenPath[i + 1], 3000, 60, IHooks(address(0)), bytes(""));
|
|
}
|
|
|
|
params.currencyIn = _tokenPath[0];
|
|
params.path = path;
|
|
params.minHopPriceX36 = new uint256[](0);
|
|
params.amountIn = uint128(amountIn);
|
|
params.amountOutMinimum = 0;
|
|
}
|
|
|
|
function _getExactInputParams(Currency[] memory _tokenPath, uint256[] memory minHopPriceX36, uint256 amountIn)
|
|
internal
|
|
pure
|
|
returns (IV4Router.ExactInputParams memory params)
|
|
{
|
|
params = _getExactInputParams(_tokenPath, amountIn);
|
|
params.minHopPriceX36 = minHopPriceX36;
|
|
return params;
|
|
}
|
|
|
|
function _getExactOutputParams(Currency[] memory _tokenPath, uint256 amountOut)
|
|
internal
|
|
pure
|
|
returns (IV4Router.ExactOutputParams memory params)
|
|
{
|
|
PathKey[] memory path = new PathKey[](_tokenPath.length - 1);
|
|
for (uint256 i = _tokenPath.length - 1; i > 0; i--) {
|
|
path[i - 1] = PathKey(_tokenPath[i - 1], 3000, 60, IHooks(address(0)), bytes(""));
|
|
}
|
|
|
|
params.currencyOut = _tokenPath[_tokenPath.length - 1];
|
|
params.path = path;
|
|
params.minHopPriceX36 = new uint256[](0);
|
|
params.amountOut = uint128(amountOut);
|
|
params.amountInMaximum = type(uint128).max;
|
|
}
|
|
|
|
function _getExactOutputParams(Currency[] memory _tokenPath, uint256[] memory minHopPriceX36, uint256 amountOut)
|
|
internal
|
|
pure
|
|
returns (IV4Router.ExactOutputParams memory params)
|
|
{
|
|
params = _getExactOutputParams(_tokenPath, amountOut);
|
|
params.minHopPriceX36 = minHopPriceX36;
|
|
return params;
|
|
}
|
|
|
|
function _finalizeAndExecuteSwap(
|
|
Currency inputCurrency,
|
|
Currency outputCurrency,
|
|
uint256 amountIn,
|
|
address takeRecipient
|
|
)
|
|
internal
|
|
returns (
|
|
uint256 inputBalanceBefore,
|
|
uint256 outputBalanceBefore,
|
|
uint256 inputBalanceAfter,
|
|
uint256 outputBalanceAfter
|
|
)
|
|
{
|
|
inputBalanceBefore = inputCurrency.balanceOfSelf();
|
|
outputBalanceBefore = outputCurrency.balanceOfSelf();
|
|
|
|
bytes memory data = plan.finalizeSwap(inputCurrency, outputCurrency, takeRecipient);
|
|
|
|
uint256 value = (inputCurrency.isAddressZero()) ? amountIn : 0;
|
|
|
|
// otherwise just execute as normal
|
|
router.executeActions{value: value}(data);
|
|
|
|
inputBalanceAfter = inputCurrency.balanceOfSelf();
|
|
outputBalanceAfter = outputCurrency.balanceOfSelf();
|
|
}
|
|
|
|
function _finalizeAndExecuteSwap(Currency inputCurrency, Currency outputCurrency, uint256 amountIn)
|
|
internal
|
|
returns (
|
|
uint256 inputBalanceBefore,
|
|
uint256 outputBalanceBefore,
|
|
uint256 inputBalanceAfter,
|
|
uint256 outputBalanceAfter
|
|
)
|
|
{
|
|
return _finalizeAndExecuteSwap(inputCurrency, outputCurrency, amountIn, ActionConstants.MSG_SENDER);
|
|
}
|
|
|
|
function _finalizeAndExecuteNativeInputExactOutputSwap(
|
|
Currency inputCurrency,
|
|
Currency outputCurrency,
|
|
uint256 expectedAmountIn
|
|
)
|
|
internal
|
|
returns (
|
|
uint256 inputBalanceBefore,
|
|
uint256 outputBalanceBefore,
|
|
uint256 inputBalanceAfter,
|
|
uint256 outputBalanceAfter
|
|
)
|
|
{
|
|
inputBalanceBefore = inputCurrency.balanceOfSelf();
|
|
outputBalanceBefore = outputCurrency.balanceOfSelf();
|
|
|
|
bytes memory data = plan.finalizeSwap(inputCurrency, outputCurrency, ActionConstants.MSG_SENDER);
|
|
|
|
// send too much ETH to mimic slippage
|
|
uint256 value = expectedAmountIn + 0.1 ether;
|
|
router.executeActionsAndSweepExcessETH{value: value}(data);
|
|
|
|
inputBalanceAfter = inputCurrency.balanceOfSelf();
|
|
outputBalanceAfter = outputCurrency.balanceOfSelf();
|
|
}
|
|
}
|