166 lines
5.6 KiB
Solidity
166 lines
5.6 KiB
Solidity
// SPDX-License-Identifier: UNLICENSED
|
|
pragma solidity ^0.8.26;
|
|
|
|
import {
|
|
IERC20
|
|
} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
|
import {
|
|
SafeERC20
|
|
} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import {RestrictTransferFrom} from "../../src/RestrictTransferFrom.sol";
|
|
import {
|
|
LiquidityPartyExecutor,
|
|
IPartyPool
|
|
} from "../../src/executors/LiquidityPartyExecutor.sol";
|
|
import {Constants} from "../Constants.sol";
|
|
import {Permit2TestHelper} from "../Permit2TestHelper.sol";
|
|
import {TestUtils} from "../TestUtils.sol";
|
|
import {LiquidityPartyExecutorExposed} from "./LiquidityParty.t.sol";
|
|
|
|
contract LiquidityPartyExecutorExposed is LiquidityPartyExecutor {
|
|
constructor(address _permit2) LiquidityPartyExecutor(_permit2) {}
|
|
|
|
function decodeParams(bytes calldata data)
|
|
external
|
|
pure
|
|
returns (
|
|
IPartyPool pool,
|
|
address tokenIn,
|
|
uint8 indexIn,
|
|
uint8 indexOut,
|
|
TransferType transferType,
|
|
address receiver
|
|
)
|
|
{
|
|
return _decodeData(data);
|
|
}
|
|
}
|
|
|
|
contract LiquidityPartyExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
|
using SafeERC20 for IERC20;
|
|
|
|
LiquidityPartyExecutorExposed private executor;
|
|
IPartyPool private constant POOL =
|
|
IPartyPool(0x2A804e94500AE379ee0CcC423a67B07cc0aF548C);
|
|
IERC20 private constant INPUT_TOKEN =
|
|
IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // WETH
|
|
uint8 private constant INPUT_INDEX = 3;
|
|
uint256 private constant AMOUNT_IN = 30428379889; // 30 gwei, 0.1% of the pool's WETH
|
|
IERC20 private constant OUTPUT_TOKEN =
|
|
IERC20(0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE); // SHIB
|
|
uint8 private constant OUTPUT_INDEX = 9;
|
|
uint256 private constant EXPECTED_AMOUNT_OUT = 11480220066406156603; // about 115 SHIB
|
|
uint256 private constant FORK_BLOCK = 23978797;
|
|
|
|
function setUp() public {
|
|
vm.createSelectFork(vm.rpcUrl("mainnet"), FORK_BLOCK);
|
|
executor = new LiquidityPartyExecutorExposed(PERMIT2_ADDRESS);
|
|
}
|
|
|
|
function testDecodeParams() public view {
|
|
bytes memory params = abi.encodePacked(
|
|
POOL,
|
|
INPUT_TOKEN,
|
|
INPUT_INDEX,
|
|
OUTPUT_INDEX,
|
|
ALICE,
|
|
RestrictTransferFrom.TransferType.Transfer
|
|
);
|
|
|
|
(
|
|
IPartyPool pool,
|
|
address tokenIn,
|
|
uint8 indexIn,
|
|
uint8 indexOut,
|
|
RestrictTransferFrom.TransferType transferType,
|
|
address receiver
|
|
) = executor.decodeParams(params);
|
|
|
|
assertEq(address(pool), address(POOL));
|
|
assertEq(address(tokenIn), address(INPUT_TOKEN));
|
|
assertEq(indexIn, INPUT_INDEX);
|
|
assertEq(indexOut, OUTPUT_INDEX);
|
|
assertEq(
|
|
uint8(transferType),
|
|
uint8(RestrictTransferFrom.TransferType.Transfer)
|
|
);
|
|
assertEq(receiver, ALICE);
|
|
}
|
|
|
|
function testDecodeParamsInvalidDataLength() public {
|
|
bytes memory invalidParams =
|
|
abi.encodePacked(WETH_ADDR, address(2), address(3));
|
|
vm.expectRevert();
|
|
executor.decodeParams(invalidParams);
|
|
}
|
|
|
|
function testSwapWithTransfer() public {
|
|
bytes memory protocolData = abi.encodePacked(
|
|
POOL,
|
|
INPUT_TOKEN,
|
|
INPUT_INDEX,
|
|
OUTPUT_INDEX,
|
|
BOB,
|
|
RestrictTransferFrom.TransferType.Transfer
|
|
);
|
|
|
|
deal(address(INPUT_TOKEN), address(executor), AMOUNT_IN);
|
|
uint256 amountOut = executor.swap(AMOUNT_IN, protocolData);
|
|
|
|
assertEq(amountOut, EXPECTED_AMOUNT_OUT);
|
|
assertGe(OUTPUT_TOKEN.balanceOf(BOB), EXPECTED_AMOUNT_OUT);
|
|
}
|
|
|
|
function testSwapNoTransfer() public {
|
|
bytes memory protocolData = abi.encodePacked(
|
|
POOL,
|
|
INPUT_TOKEN,
|
|
INPUT_INDEX,
|
|
OUTPUT_INDEX,
|
|
BOB,
|
|
RestrictTransferFrom.TransferType.None
|
|
);
|
|
|
|
deal(address(INPUT_TOKEN), address(this), AMOUNT_IN);
|
|
/// forge-lint: disable-next-line(erc20-unchecked-transfer)
|
|
INPUT_TOKEN.transfer(address(POOL), AMOUNT_IN);
|
|
uint256 amountOut = executor.swap(AMOUNT_IN, protocolData);
|
|
|
|
assertEq(amountOut, EXPECTED_AMOUNT_OUT);
|
|
assertGe(OUTPUT_TOKEN.balanceOf(BOB), EXPECTED_AMOUNT_OUT);
|
|
}
|
|
|
|
function testSwapIntegration() public {
|
|
bytes memory protocolData =
|
|
loadCallDataFromFile("test_encode_liquidityparty");
|
|
deal(address(INPUT_TOKEN), address(executor), AMOUNT_IN);
|
|
uint256 amountOut = executor.swap(AMOUNT_IN, protocolData);
|
|
|
|
uint256 finalBalance = OUTPUT_TOKEN.balanceOf(BOB);
|
|
assertEq(amountOut, EXPECTED_AMOUNT_OUT);
|
|
assertGe(finalBalance, amountOut);
|
|
}
|
|
|
|
function testSwapFailureKilledPool() public {
|
|
// Killed pools should not even appear as protocol components, but we test an attempted swap anyway.
|
|
// This address is a pool that was killed (permanent redeem-only mode)
|
|
address killedPool = address(0xC0A908477FFeff658699182bEB5EcaF1D46B3ddB);
|
|
bytes memory protocolData = abi.encodePacked(
|
|
killedPool,
|
|
INPUT_TOKEN,
|
|
INPUT_INDEX,
|
|
OUTPUT_INDEX,
|
|
BOB,
|
|
RestrictTransferFrom.TransferType.None
|
|
);
|
|
|
|
deal(address(INPUT_TOKEN), address(executor), AMOUNT_IN);
|
|
vm.expectRevert();
|
|
executor.swap(AMOUNT_IN, protocolData);
|
|
}
|
|
|
|
function testExportContract() public {
|
|
exportRuntimeBytecode(address(executor), "LiquidityParty");
|
|
}
|
|
}
|