From 3752c155ea89d4d2d91be98f2c5fa79264a45999 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 9 Jul 2025 12:06:15 -0400 Subject: [PATCH] feat: Handle native ETH outputs - In this case, the ETH needs to be manually transferred to the reactor. - Native ETH input is not supported for UniswapX Source: https://support.uniswap.org/hc/en-us/articles/17544708791821-Are-there-network-costs-for-UniswapX --- foundry/src/uniswap_x/UniswapXFiller.sol | 36 ++++++++++----------- foundry/test/uniswap_x/UniswapXFiller.t.sol | 6 ++-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/foundry/src/uniswap_x/UniswapXFiller.sol b/foundry/src/uniswap_x/UniswapXFiller.sol index bc5a3f9..2daaf40 100644 --- a/foundry/src/uniswap_x/UniswapXFiller.sol +++ b/foundry/src/uniswap_x/UniswapXFiller.sol @@ -20,6 +20,7 @@ contract UniswapXFiller is AccessControl, IReactorCallback { // UniswapX V2DutchOrder Reactor IReactor public immutable reactor; address public immutable tychoRouter; + address public immutable nativeAddress; // keccak256("NAME_OF_ROLE") : save gas on deployment bytes32 public constant REACTOR_ROLE = @@ -31,7 +32,11 @@ contract UniswapXFiller is AccessControl, IReactorCallback { address indexed token, uint256 amount, address indexed receiver ); - constructor(address _tychoRouter, address _reactor) { + constructor( + address _tychoRouter, + address _reactor, + address _native_address + ) { if (_tychoRouter == address(0)) revert UniswapXFiller__AddressZero(); if (_reactor == address(0)) revert UniswapXFiller__AddressZero(); @@ -39,6 +44,7 @@ contract UniswapXFiller is AccessControl, IReactorCallback { _grantRole(REACTOR_ROLE, address(_reactor)); tychoRouter = _tychoRouter; reactor = IReactor(_reactor); + nativeAddress = _native_address; } function execute(SignedOrder calldata order, bytes calldata callbackData) @@ -67,26 +73,14 @@ contract UniswapXFiller is AccessControl, IReactorCallback { // The TychoRouter will take the input tokens from the filler if (tokenInApprovalNeeded) { - // TODO only if ERC20 + // Native ETH input is not supported by UniswapX IERC20(order.input.token).forceApprove( tychoRouter, order.input.maxAmount ); - } else { - // The filler will manually transfer into the TychoRouter - // Note: We are using the balance of our filler instead of the order - // input amount to avoid having to do a decay calculation in the filler. - IERC20 inputToken = IERC20(order.input.token); - inputToken.transfer( - tychoRouter, inputToken.balanceOf(address(this)) - ); } - // TODO properly handle native in and out tokens - uint256 ethValue = 0; - // slither-disable-next-line low-level-calls - (bool success, bytes memory result) = - tychoRouter.call{value: ethValue}(tychoCalldata); + (bool success, bytes memory result) = tychoRouter.call(tychoCalldata); if (!success) { revert( @@ -101,9 +95,15 @@ contract UniswapXFiller is AccessControl, IReactorCallback { if (tokenOutApprovalNeeded) { // Multiple outputs are possible when taking fees - but token itself should // not change. - // TODO only if ERC20 - IERC20 token = IERC20(order.outputs[0].token); - token.forceApprove(address(reactor), type(uint256).max); + OutputToken memory output = order.outputs[0]; + if (output.token != nativeAddress) { + IERC20 token = IERC20(output.token); + token.forceApprove(address(reactor), type(uint256).max); + } else { + // With native ETH - the filler is responsible for transferring back + // to the reactor. + Address.sendValue(payable(address(reactor)), output.amount); + } } } diff --git a/foundry/test/uniswap_x/UniswapXFiller.t.sol b/foundry/test/uniswap_x/UniswapXFiller.t.sol index 49f9e90..9ac004c 100644 --- a/foundry/test/uniswap_x/UniswapXFiller.t.sol +++ b/foundry/test/uniswap_x/UniswapXFiller.t.sol @@ -23,7 +23,7 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { function fillerSetup() public { vm.startPrank(ADMIN); - filler = new UniswapXFiller(tychoRouterAddr, REACTOR); + filler = new UniswapXFiller(tychoRouterAddr, REACTOR, address(0)); fillerAddr = address(filler); filler.grantRole(keccak256("EXECUTOR_ROLE"), EXECUTOR); vm.stopPrank(); @@ -31,12 +31,12 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { function testTychoAddressZeroTychoRouter() public { vm.expectRevert(UniswapXFiller__AddressZero.selector); - filler = new UniswapXFiller(address(0), REACTOR); + filler = new UniswapXFiller(address(0), REACTOR, address(0)); } function testTychoAddressZeroReactor() public { vm.expectRevert(UniswapXFiller__AddressZero.selector); - filler = new UniswapXFiller(tychoRouterAddr, address(0)); + filler = new UniswapXFiller(tychoRouterAddr, address(0), address(0)); } function testCallback() public {