Files
tycho-execution/foundry/test/uniswap_x/UniswapXFiller.t.sol
Diana Carvalho a82d0f2885 refactor: forge fmt (after foundry update)
Took 5 minutes


Took 8 seconds
2025-10-16 16:49:59 +01:00

302 lines
14 KiB
Solidity

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import "@src/uniswap_x/UniswapXFiller.sol";
import "../TychoRouterTestSetup.sol";
contract UniswapXFillerTest is Test, TychoRouterTestSetup {
address EXECUTOR = address(0xCe79b081c0c924cb67848723ed3057234d10FC6b);
address REACTOR = address(0x00000011F84B9aa48e5f8aA8B9897600006289Be);
UniswapXFiller filler;
address fillerAddr;
event CallbackVerifierSet(address indexed callbackVerifier);
event Withdrawal(
address indexed token, uint256 amount, address indexed receiver
);
function getForkBlock() public pure override returns (uint256) {
return 22880493;
}
function fillerSetup() public {
vm.startPrank(ADMIN);
filler = new UniswapXFiller(tychoRouterAddr, REACTOR, address(0));
fillerAddr = address(filler);
filler.grantRole(keccak256("EXECUTOR_ROLE"), EXECUTOR);
vm.stopPrank();
}
function testTychoAddressZeroTychoRouter() public {
vm.expectRevert(UniswapXFiller__AddressZero.selector);
filler = new UniswapXFiller(address(0), REACTOR, address(0));
}
function testTychoAddressZeroReactor() public {
vm.expectRevert(UniswapXFiller__AddressZero.selector);
filler = new UniswapXFiller(tychoRouterAddr, address(0), address(0));
}
function testCallback() public {
fillerSetup();
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData = abi.encodePacked(
WETH_ADDR,
WETH_DAI_POOL,
address(filler),
zeroForOne,
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
encodeSingleSwap(address(usv2Executor), protocolData);
bytes memory tychoRouterData = abi.encodeWithSelector(
tychoRouter.singleSwap.selector,
amountIn,
WETH_ADDR,
DAI_ADDR,
2008817438608734439722,
false,
false,
address(filler),
true,
swap
);
bytes memory callbackData =
abi.encodePacked(true, true, tychoRouterData);
deal(WETH_ADDR, address(filler), amountIn);
ResolvedOrder[] memory orders = new ResolvedOrder[](1);
OutputToken[] memory outputs = new OutputToken[](1);
outputs[0] = OutputToken({
token: address(DAI_ADDR),
amount: 1847751195973566072891,
recipient: BOB
});
// Irrelevant fields for this test - we only need token output
// info for the sake of testing.
orders[0] = ResolvedOrder({
info: OrderInfo({
reactor: address(0),
swapper: address(0),
nonce: 0,
deadline: 0,
additionalValidationContract: address(0),
additionalValidationData: ""
}),
input: InputToken({
token: address(WETH_ADDR), amount: amountIn, maxAmount: amountIn
}),
outputs: outputs,
sig: "",
hash: ""
});
vm.startPrank(REACTOR);
filler.reactorCallback(orders, callbackData);
vm.stopPrank();
// Check that the funds are in the filler at the end of the function call
uint256 finalBalance = IERC20(DAI_ADDR).balanceOf(address(filler));
assertGe(finalBalance, amountOut);
// Check that the proper approval was set
vm.startPrank(REACTOR);
IERC20(DAI_ADDR).transferFrom(address(filler), BOB, amountOut);
vm.stopPrank();
assertGe(IERC20(DAI_ADDR).balanceOf(BOB), amountOut);
}
function testExecuteIntegration() public {
fillerSetup();
// Set to time with no more penalty for not being exclusive filler
vm.warp(1752050415);
deal(
DAI_ADDR,
address(0xD213e6F6dCB2DBaC03FA28b893F6dA1BD822e852),
2000 ether
);
uint256 amountIn = 2000000000000000000000;
vm.startPrank(address(0xD213e6F6dCB2DBaC03FA28b893F6dA1BD822e852));
// Approve Permit2
IERC20(DAI_ADDR)
.approve(
address(0x000000000022D473030F116dDEE9F6B43aC78BA3), amountIn
);
vm.stopPrank();
// Tx 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d
// Calldata generated using rust test `test_sequential_swap_usx`
SignedOrder memory order = SignedOrder({
order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e8520468320351debb1ddbfb032a239d699e3d54e3ce2b6e1037cd836a784c80b60100000000000000000000000000000000000000000000000000000000686e2bf9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000076f9f4870000000000000000000000000000000000000000000000000000000076566300000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e85200000000000000000000000000000000000000000000000000000000686e2aee00000000000000000000000000000000000000000000000000000000686e2b2a000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000007727b5f40000000000000000000000000000000000000000000000000000000000000041a2d261cd4c8930428260f18b55e3036024bac68d58cb2ee6161e6395b0984b827104158713d44ddc4e14d852b48d93d95a4e60b8d5be1ef431c1e82d2f76a4111b00000000000000000000000000000000000000000000000000000000000000",
sig: hex"f4cc5734820e4ee08519045c83a25b75687756053b3d6c0fda2141380dfa6ef17b40f64d9279f237e96982c6ba53a202e01a4358fd66e027c9bdf200d5626f441c"
});
bytes memory callbackData =
loadCallDataFromFile("test_sequential_swap_usx");
vm.startPrank(EXECUTOR);
filler.execute(order, callbackData);
vm.stopPrank();
}
function testExecute() public {
fillerSetup();
// Set to time with no more penalty for not being exclusive filler
vm.warp(1752050415);
// tx: 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d
// DAI ──> USDT
SignedOrder memory order = SignedOrder({
order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e8520468320351debb1ddbfb032a239d699e3d54e3ce2b6e1037cd836a784c80b60100000000000000000000000000000000000000000000000000000000686e2bf9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000076f9f4870000000000000000000000000000000000000000000000000000000076566300000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e85200000000000000000000000000000000000000000000000000000000686e2aee00000000000000000000000000000000000000000000000000000000686e2b2a000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000007727b5f40000000000000000000000000000000000000000000000000000000000000041a2d261cd4c8930428260f18b55e3036024bac68d58cb2ee6161e6395b0984b827104158713d44ddc4e14d852b48d93d95a4e60b8d5be1ef431c1e82d2f76a4111b00000000000000000000000000000000000000000000000000000000000000",
sig: hex"f4cc5734820e4ee08519045c83a25b75687756053b3d6c0fda2141380dfa6ef17b40f64d9279f237e96982c6ba53a202e01a4358fd66e027c9bdf200d5626f441c"
});
uint256 amountIn = 2000000000000000000000;
bool zeroForOne = true;
uint24 fee = 100;
bytes memory protocolData = abi.encodePacked(
DAI_ADDR,
USDT_ADDR,
fee,
fillerAddr,
DAI_USDT_USV3,
zeroForOne,
RestrictTransferFrom.TransferType.TransferFrom
);
bytes memory swap =
encodeSingleSwap(address(usv3Executor), protocolData);
bytes memory tychoRouterData = abi.encodeWithSelector(
tychoRouter.singleSwap.selector,
amountIn,
DAI_ADDR,
USDT_ADDR,
1,
false,
false,
fillerAddr,
true,
swap
);
bytes memory callbackData = abi.encodePacked(
true, // tokenIn approval needed
true, // tokenOut approval needed
tychoRouterData
);
vm.startPrank(address(filler));
IERC20(WBTC_ADDR).approve(tychoRouterAddr, amountIn);
vm.stopPrank();
vm.startPrank(EXECUTOR);
filler.execute(order, callbackData);
vm.stopPrank();
}
function testWithdrawNative() public {
fillerSetup();
vm.startPrank(ADMIN);
// Send 100 ether to filler
assertEq(fillerAddr.balance, 0);
assertEq(ADMIN.balance, 0);
vm.deal(fillerAddr, 100 ether);
vm.expectEmit();
emit Withdrawal(address(0), 100 ether, ADMIN);
filler.withdrawNative(ADMIN);
assertEq(fillerAddr.balance, 0);
assertEq(ADMIN.balance, 100 ether);
vm.stopPrank();
}
function testWithdrawNativeAddressZero() public {
fillerSetup();
vm.deal(fillerAddr, 100 ether);
vm.startPrank(ADMIN);
vm.expectRevert(UniswapXFiller__AddressZero.selector);
filler.withdrawNative(address(0));
vm.stopPrank();
}
function testWithdrawNativeMissingRole() public {
fillerSetup();
vm.deal(fillerAddr, 100 ether);
// Not role ADMIN
vm.startPrank(BOB);
vm.expectRevert();
filler.withdrawNative(ADMIN);
vm.stopPrank();
}
function testWithdrawERC20Tokens() public {
fillerSetup();
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = IERC20(WETH_ADDR);
tokens[1] = IERC20(USDC_ADDR);
for (uint256 i = 0; i < tokens.length; i++) {
deal(address(tokens[i]), fillerAddr, 100 ether);
}
vm.startPrank(ADMIN);
filler.withdraw(tokens, ADMIN);
// Check balances after withdrawing
for (uint256 i = 0; i < tokens.length; i++) {
// slither-disable-next-line calls-loop
assertEq(tokens[i].balanceOf(fillerAddr), 0);
// slither-disable-next-line calls-loop
assertEq(tokens[i].balanceOf(ADMIN), 100 ether);
}
vm.stopPrank();
}
function testWithdrawERC20TokensAddressZero() public {
fillerSetup();
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = IERC20(WETH_ADDR);
tokens[1] = IERC20(USDC_ADDR);
for (uint256 i = 0; i < tokens.length; i++) {
deal(address(tokens[i]), fillerAddr, 100 ether);
}
vm.startPrank(ADMIN);
vm.expectRevert(UniswapXFiller__AddressZero.selector);
filler.withdraw(tokens, address(0));
vm.stopPrank();
}
function testWithdrawERC20TokensAddressMissingRole() public {
fillerSetup();
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = IERC20(WETH_ADDR);
tokens[1] = IERC20(USDC_ADDR);
for (uint256 i = 0; i < tokens.length; i++) {
deal(address(tokens[i]), fillerAddr, 100 ether);
}
// Not role ADMIN
vm.startPrank(BOB);
vm.expectRevert();
filler.withdraw(tokens, ADMIN);
vm.stopPrank();
}
}