refactor: Separate test files for split/sequential/single
- Add more test cases for sequential swap and single swap to match those of split swap (fee, slippage), to catch errors more easily if someone makes a small code change to either the single or sequential methods - Excluded USV3 and USV4 tests on sequential and single swap, since these tests were more to test USV3 and USV4 executor functionality than the high level sswap methods - they should already be sufficiently tested. - Remove `testSplitSwapSimple` and `testSplitSwapSingleUSV3` since this is already tested by several other high-level methods (see single USV3 and single USV4 tests). We should prioritize integration-testing public methods over private methods.
This commit is contained in:
committed by
Diana Carvalho
parent
1dad4afb6b
commit
c4455dcabb
307
foundry/test/TychoRouterSingleSwap.t.sol
Normal file
307
foundry/test/TychoRouterSingleSwap.t.sol
Normal file
@@ -0,0 +1,307 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {TychoRouter} from "@src/TychoRouter.sol";
|
||||
import "./TychoRouterTestSetup.sol";
|
||||
import "./executors/UniswapV4Utils.sol";
|
||||
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||
|
||||
contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
|
||||
bytes32 public constant FEE_SETTER_ROLE =
|
||||
0xe6ad9a47fbda1dc18de1eb5eeb7d935e5e81b4748f3cfc61e233e64f88182060;
|
||||
|
||||
function testSingleSwapPermit2() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2 using Permit2
|
||||
// 1 WETH -> DAI
|
||||
// (USV2)
|
||||
vm.startPrank(ALICE);
|
||||
|
||||
uint256 amountIn = 1 ether;
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
(
|
||||
IAllowanceTransfer.PermitSingle memory permitSingle,
|
||||
bytes memory signature
|
||||
) = handlePermit2Approval(WETH_ADDR, amountIn);
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
tychoRouter.singleSwapPermit2(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
2659881924818443699786,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
permitSingle,
|
||||
signature,
|
||||
swap
|
||||
);
|
||||
|
||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
assertEq(daiBalance, 2659881924818443699787);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 0);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSingleSwapNoPermit2() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
|
||||
// Checks amount out at the end
|
||||
uint256 amountIn = 1 ether;
|
||||
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
vm.startPrank(ALICE);
|
||||
// Approve the tokenIn to be transferred to the router
|
||||
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
uint256 minAmountOut = 2600 * 1e18;
|
||||
uint256 amountOut = tychoRouter.singleSwap(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
minAmountOut,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
swap
|
||||
);
|
||||
|
||||
uint256 expectedAmount = 2659881924818443699787;
|
||||
assertEq(amountOut, expectedAmount);
|
||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
assertEq(daiBalance, expectedAmount);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 0);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSingleSwapUndefinedMinAmount() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
|
||||
// Checks amount out at the end
|
||||
uint256 amountIn = 1 ether;
|
||||
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector);
|
||||
tychoRouter.singleSwap(
|
||||
amountIn, WETH_ADDR, DAI_ADDR, 0, false, false, ALICE, swap
|
||||
);
|
||||
}
|
||||
|
||||
function testSingleSwapInsufficientApproval() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
|
||||
// Checks amount out at the end
|
||||
uint256 amountIn = 1 ether;
|
||||
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1);
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
uint256 minAmountOut = 2600 * 1e18;
|
||||
vm.expectRevert();
|
||||
tychoRouter.singleSwap(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
minAmountOut,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
swap
|
||||
);
|
||||
}
|
||||
|
||||
function testSingleSwapNegativeSlippageFailure() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
|
||||
// Checks amount out at the end
|
||||
uint256 amountIn = 1 ether;
|
||||
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
vm.startPrank(ALICE);
|
||||
// Approve the tokenIn to be transferred to the router
|
||||
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
uint256 minAmountOut = 5600 * 1e18;
|
||||
|
||||
vm.expectRevert(
|
||||
abi.encodeWithSelector(
|
||||
TychoRouter__NegativeSlippage.selector,
|
||||
2659881924818443699787, // actual amountOut
|
||||
minAmountOut
|
||||
)
|
||||
);
|
||||
tychoRouter.singleSwap(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
minAmountOut,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
swap
|
||||
);
|
||||
}
|
||||
|
||||
function testSingleSwapFee() public {
|
||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
|
||||
// Takes 1% fee at the end
|
||||
|
||||
vm.startPrank(FEE_SETTER);
|
||||
tychoRouter.setFee(100);
|
||||
tychoRouter.setFeeReceiver(FEE_RECEIVER);
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 amountIn = 1 ether;
|
||||
|
||||
deal(WETH_ADDR, ALICE, amountIn);
|
||||
vm.startPrank(ALICE);
|
||||
// Approve the tokenIn to be transferred to the router
|
||||
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
uint256 minAmountOut = 2600 * 1e18;
|
||||
uint256 amountOut = tychoRouter.singleSwap(
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
minAmountOut,
|
||||
false,
|
||||
false,
|
||||
ALICE,
|
||||
swap
|
||||
);
|
||||
|
||||
uint256 expectedAmount = 2633283105570259262790;
|
||||
assertEq(amountOut, expectedAmount);
|
||||
uint256 usdcBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
assertEq(usdcBalance, expectedAmount);
|
||||
assertEq(IERC20(DAI_ADDR).balanceOf(FEE_RECEIVER), 26598819248184436997);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSingleSwapWrapETH() public {
|
||||
uint256 amountIn = 1 ether;
|
||||
deal(ALICE, amountIn);
|
||||
vm.startPrank(ALICE);
|
||||
|
||||
IAllowanceTransfer.PermitSingle memory emptyPermitSingle =
|
||||
IAllowanceTransfer.PermitSingle({
|
||||
details: IAllowanceTransfer.PermitDetails({
|
||||
token: address(0),
|
||||
amount: 0,
|
||||
expiration: 0,
|
||||
nonce: 0
|
||||
}),
|
||||
spender: address(0),
|
||||
sigDeadline: 0
|
||||
});
|
||||
|
||||
bytes memory protocolData = encodeUniswapV2Swap(
|
||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
uint256 amountOut = tychoRouter.singleSwapPermit2{value: amountIn}(
|
||||
amountIn,
|
||||
address(0),
|
||||
DAI_ADDR,
|
||||
1000_000000,
|
||||
true,
|
||||
false,
|
||||
ALICE,
|
||||
emptyPermitSingle,
|
||||
"",
|
||||
swap
|
||||
);
|
||||
uint256 expectedAmount = 2659881924818443699787;
|
||||
assertEq(amountOut, expectedAmount);
|
||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
assertEq(daiBalance, expectedAmount);
|
||||
assertEq(ALICE.balance, 0);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSingleSwapUnwrapETH() public {
|
||||
// DAI -> WETH with unwrapping to ETH
|
||||
uint256 amountIn = 3000 ether;
|
||||
deal(DAI_ADDR, ALICE, amountIn);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
(
|
||||
IAllowanceTransfer.PermitSingle memory permitSingle,
|
||||
bytes memory signature
|
||||
) = handlePermit2Approval(DAI_ADDR, amountIn);
|
||||
|
||||
bytes memory protocolData =
|
||||
encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
uint256 amountOut = tychoRouter.singleSwapPermit2(
|
||||
amountIn,
|
||||
DAI_ADDR,
|
||||
address(0),
|
||||
1000_000000,
|
||||
false,
|
||||
true,
|
||||
ALICE,
|
||||
permitSingle,
|
||||
signature,
|
||||
swap
|
||||
);
|
||||
|
||||
uint256 expectedAmount = 1120007305574805922;
|
||||
assertEq(amountOut, expectedAmount);
|
||||
assertEq(ALICE.balance, expectedAmount);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user