From 8f4a29d506d46fb1b0cae4fe41312a186ad21082 Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Wed, 29 Nov 2023 22:08:48 -0500 Subject: [PATCH 1/6] Refactor tests and add arbitrary call --- ...derReactorIntegrationTest-testExecute.snap | 2 +- ...ionTest-testExecuteWithNativeAsOutput.snap | 2 +- ...rIntegrationTest-testPermitAndExecute.snap | 2 +- lib/universal-router | 1 + src/lib/RelayOrderLib.sol | 12 +- src/reactors/RelayOrderReactor.sol | 28 ++--- .../RelayOrderReactorIntegration.t.sol | 103 ++++++++++++------ 7 files changed, 82 insertions(+), 68 deletions(-) create mode 160000 lib/universal-router diff --git a/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecute.snap b/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecute.snap index 28d61538..87c81da2 100644 --- a/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecute.snap +++ b/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecute.snap @@ -1 +1 @@ -303406 \ No newline at end of file +288585 \ No newline at end of file diff --git a/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecuteWithNativeAsOutput.snap b/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecuteWithNativeAsOutput.snap index 67ad5c65..09fceba3 100644 --- a/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecuteWithNativeAsOutput.snap +++ b/.forge-snapshots/RelayOrderReactorIntegrationTest-testExecuteWithNativeAsOutput.snap @@ -1 +1 @@ -309001 \ No newline at end of file +291179 \ No newline at end of file diff --git a/.forge-snapshots/RelayOrderReactorIntegrationTest-testPermitAndExecute.snap b/.forge-snapshots/RelayOrderReactorIntegrationTest-testPermitAndExecute.snap index 8719ff76..1fff7e94 100644 --- a/.forge-snapshots/RelayOrderReactorIntegrationTest-testPermitAndExecute.snap +++ b/.forge-snapshots/RelayOrderReactorIntegrationTest-testPermitAndExecute.snap @@ -1 +1 @@ -350945 \ No newline at end of file +340631 \ No newline at end of file diff --git a/lib/universal-router b/lib/universal-router new file mode 160000 index 00000000..7d763cb2 --- /dev/null +++ b/lib/universal-router @@ -0,0 +1 @@ +Subproject commit 7d763cb28c88bb3bce5a30ad2356722f10c4d484 diff --git a/src/lib/RelayOrderLib.sol b/src/lib/RelayOrderLib.sol index 519c2be2..8bbee916 100644 --- a/src/lib/RelayOrderLib.sol +++ b/src/lib/RelayOrderLib.sol @@ -5,12 +5,6 @@ import {OrderInfo, OutputToken} from "UniswapX/src/base/ReactorStructs.sol"; import {OrderInfoLib} from "UniswapX/src/lib/OrderInfoLib.sol"; import {InputTokenWithRecipient} from "../base/ReactorStructs.sol"; -enum ActionType { - ApprovePermit2, - Permit2612, - UniversalRouter -} - /// @dev External struct used to specify simple relay orders struct RelayOrder { // generic order information @@ -104,12 +98,12 @@ library RelayOrderLib { function hash(RelayOrder memory order) internal pure returns (bytes32) { return keccak256( abi.encode( - ORDER_TYPE_HASH, - order.info.hash(), + ORDER_TYPE_HASH, + order.info.hash(), order.decayStartTime, order.decayEndTime, order.actions, - hash(order.inputs), + hash(order.inputs), hash(order.outputs) ) ); diff --git a/src/reactors/RelayOrderReactor.sol b/src/reactors/RelayOrderReactor.sol index e13de8b6..8a19b544 100644 --- a/src/reactors/RelayOrderReactor.sol +++ b/src/reactors/RelayOrderReactor.sol @@ -12,7 +12,7 @@ import {ReactorErrors} from "../base/ReactorErrors.sol"; import {InputTokenWithRecipient, ResolvedRelayOrder} from "../base/ReactorStructs.sol"; import {CurrencyLibrary, NATIVE} from "../lib/CurrencyLibrary.sol"; import {Permit2Lib} from "../lib/Permit2Lib.sol"; -import {RelayOrderLib, RelayOrder, ActionType} from "../lib/RelayOrderLib.sol"; +import {RelayOrderLib, RelayOrder} from "../lib/RelayOrderLib.sol"; import {ResolvedRelayOrderLib} from "../lib/ResolvedRelayOrderLib.sol"; import {RelayDecayLib} from "../lib/RelayDecayLib.sol"; @@ -62,30 +62,16 @@ contract RelayOrderReactor is ReactorEvents, ReactorErrors, ReentrancyGuard, IRe function _execute(ResolvedRelayOrder[] memory orders) internal { uint256 ordersLength = orders.length; - // actions are encoded as (ActionType actionType, bytes actionData)[] + // actions are encoded as (address target, uint256 value, bytes data)[] for (uint256 i = 0; i < ordersLength;) { ResolvedRelayOrder memory order = orders[i]; uint256 actionsLength = order.actions.length; for (uint256 j = 0; j < actionsLength;) { - (ActionType actionType, bytes memory actionData) = abi.decode(order.actions[j], (ActionType, bytes)); - if (actionType == ActionType.UniversalRouter) { - /// @dev to use universal router integration, this contract must be recipient of all output tokens - (bool success,) = universalRouter.call(actionData); - if (!success) revert CallFailed(); - } - // Max approve an ERC20 to UniversalRouter from the reactor using Permit2 - else if (actionType == ActionType.ApprovePermit2) { - (address token) = abi.decode(actionData, (address)); - if (token == address(0)) revert InvalidToken(); - if (ERC20(token).allowance(address(this), address(permit2)) == 0) { - ERC20(token).approve(address(permit2), type(uint256).max); - } - permit2.approve(token, universalRouter, type(uint160).max, type(uint48).max); - } - // Catch unsupported action types - else { - revert UnsupportedAction(); - } + (address target, uint256 value, bytes memory data) = + abi.decode(order.actions[j], (address, uint256, bytes)); + + (bool success,) = target.call{value: value}(data); + if (!success) revert CallFailed(); unchecked { j++; } diff --git a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol index 1a52159c..e63ab8a2 100644 --- a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol +++ b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol @@ -14,7 +14,7 @@ import {InputTokenWithRecipient, ResolvedRelayOrder} from "../../../src/base/Rea import {ReactorEvents} from "../../../src/base/ReactorEvents.sol"; import {CurrencyLibrary} from "../../../src/lib/CurrencyLibrary.sol"; import {PermitSignature} from "../util/PermitSignature.sol"; -import {RelayOrderLib, RelayOrder, ActionType} from "../../../src/lib/RelayOrderLib.sol"; +import {RelayOrderLib, RelayOrder} from "../../../src/lib/RelayOrderLib.sol"; import {RelayOrderReactor} from "../../../src/reactors/RelayOrderReactor.sol"; import {PermitExecutor} from "../../../src/sample-executors/PermitExecutor.sol"; import {MethodParameters, Interop} from "../util/Interop.sol"; @@ -49,6 +49,12 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS error InvalidNonce(); error InvalidSigner(); + uint256 swapperInputBalanceStart; + uint256 swapperOutputBalanceStart; + uint256 routerInputBalanceStart; + uint256 routerOutputBalanceStart; + uint256 fillerGasInputBalanceStart; + function setUp() public { swapperPrivateKey = 0xbabe; swapper = vm.addr(swapperPrivateKey); @@ -63,7 +69,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS reactor = RelayOrderReactor(RELAY_ORDER_REACTOR); permitExecutor = new PermitExecutor(address(filler), reactor, address(filler)); - // Swapper max approves permit post + // Swapper max approves permit post for all input tokens vm.startPrank(swapper); DAI.approve(address(PERMIT2), type(uint256).max); USDC.approve(address(PERMIT2), type(uint256).max); @@ -71,15 +77,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS PERMIT2.approve(address(USDC), address(reactor), type(uint160).max, type(uint48).max); vm.stopPrank(); - // reactor max approves permit post - vm.startPrank(address(reactor)); - DAI.approve(address(PERMIT2), type(uint256).max); - USDC.approve(address(PERMIT2), type(uint256).max); - PERMIT2.approve(address(DAI), UNIVERSAL_ROUTER, type(uint160).max, type(uint48).max); - PERMIT2.approve(address(USDC), UNIVERSAL_ROUTER, type(uint160).max, type(uint48).max); - vm.stopPrank(); - - // Transfer 1000 DAI to swapper + // Fund swappers vm.startPrank(WHALE); DAI.transfer(swapper, 1000 * ONE); DAI.transfer(swapper2, 1000 * ONE); @@ -87,6 +85,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS USDC.transfer(swapper2, 1000 * USDC_ONE); vm.stopPrank(); + // initial assumptions assertEq(USDC.balanceOf(address(reactor)), 0, "reactor should have no USDC"); assertEq(DAI.balanceOf(address(reactor)), 0, "reactor should have no DAI"); } @@ -109,7 +108,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS bytes[] memory actions = new bytes[](1); MethodParameters memory methodParameters = readFixture(json, "._UNISWAP_V3_DAI_USDC"); - actions[0] = abi.encode(ActionType.UniversalRouter, methodParameters.data); + actions[0] = abi.encode(UNIVERSAL_ROUTER, methodParameters.value, methodParameters.data); RelayOrder memory order = RelayOrder({ info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper).withDeadline(block.timestamp + 100), @@ -123,17 +122,25 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS SignedOrder memory signedOrder = SignedOrder(abi.encode(order), signOrder(swapperPrivateKey, address(PERMIT2), order)); - uint256 routerDaiBalanceBefore = DAI.balanceOf(UNIVERSAL_ROUTER); + ERC20 tokenIn = DAI; + ERC20 tokenOut = USDC; + _checkpointBalances(swapper, filler, tokenIn, tokenOut, USDC); vm.prank(filler); snapStart("RelayOrderReactorIntegrationTest-testExecute"); reactor.execute{value: methodParameters.value}(signedOrder); snapEnd(); - assertEq(DAI.balanceOf(UNIVERSAL_ROUTER), routerDaiBalanceBefore, "No leftover input in router"); - assertEq(USDC.balanceOf(address(reactor)), 0, "No leftover output in reactor"); - assertGe(USDC.balanceOf(swapper), amountOutMin, "Swapper did not receive enough output"); - assertEq(USDC.balanceOf((filler)), 10 * USDC_ONE, "filler did not receive enough USDC"); + assertEq(tokenIn.balanceOf(UNIVERSAL_ROUTER), routerInputBalanceStart, "No leftover input in router"); + assertEq(tokenOut.balanceOf(UNIVERSAL_ROUTER), routerOutputBalanceStart, "No leftover output in reactor"); + assertEq(tokenOut.balanceOf(address(reactor)), 0, "No leftover output in reactor"); + assertEq(tokenIn.balanceOf(swapper), swapperInputBalanceStart - 100 * ONE, "Swapper input tokens"); + assertGe( + tokenOut.balanceOf(swapper), + swapperOutputBalanceStart + amountOutMin - 10 * USDC_ONE, + "Swapper did not receive enough output" + ); + assertEq(tokenOut.balanceOf((filler)), fillerGasInputBalanceStart + 10 * USDC_ONE, "filler balance"); } function testPermitAndExecute() public { @@ -157,8 +164,6 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS uint256 amountOutMin = 95 * ONE; // sign permit for USDC - uint256 amount = type(uint256).max - 1; // infinite approval to permit2 - uint256 deadline = type(uint256).max - 1; // never expires bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", @@ -168,9 +173,9 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), swapper2, address(PERMIT2), - amount, + type(uint256).max - 1, // infinite approval USDC.nonces(swapper2), - deadline + type(uint256).max - 1 // infinite deadline ) ) ) @@ -181,11 +186,11 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS assertEq(signer, swapper2); bytes memory permitData = - abi.encode(address(USDC), abi.encode(swapper2, address(PERMIT2), amount, deadline, v, r, s)); + abi.encode(address(USDC), abi.encode(swapper2, address(PERMIT2), type(uint256).max - 1, type(uint256).max - 1, v, r, s)); bytes[] memory actions = new bytes[](1); MethodParameters memory methodParameters = readFixture(json, "._UNISWAP_V3_USDC_DAI"); - actions[0] = abi.encode(ActionType.UniversalRouter, methodParameters.data); + actions[0] = abi.encode(UNIVERSAL_ROUTER, methodParameters.value, methodParameters.data); RelayOrder memory order = RelayOrder({ info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper2).withDeadline(block.timestamp + 100), @@ -199,18 +204,27 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS SignedOrder memory signedOrder = SignedOrder(abi.encode(order), signOrder(swapper2PrivateKey, address(PERMIT2), order)); - uint256 routerUSDCBalanceBefore = USDC.balanceOf(UNIVERSAL_ROUTER); + ERC20 tokenIn = USDC; + ERC20 tokenOut = DAI; + // in this case, gas payment will go to executor (msg.sender) + _checkpointBalances(swapper2, address(permitExecutor), tokenIn, tokenOut, USDC); vm.prank(filler); snapStart("RelayOrderReactorIntegrationTest-testPermitAndExecute"); permitExecutor.executeWithPermit{value: methodParameters.value}(signedOrder, permitData); snapEnd(); - assertEq(USDC.balanceOf(UNIVERSAL_ROUTER), routerUSDCBalanceBefore, "No leftover input in router"); - assertEq(DAI.balanceOf(address(reactor)), 0, "No leftover output in reactor"); - assertGe(DAI.balanceOf(swapper2), amountOutMin, "Swapper did not receive enough output"); - // in this case, gas payment will go to sender (executor) - assertEq(USDC.balanceOf(address(permitExecutor)), 10 * USDC_ONE, "executor did not receive enough USDC"); + assertEq(tokenIn.balanceOf(UNIVERSAL_ROUTER), routerInputBalanceStart, "No leftover input in router"); + assertEq(tokenOut.balanceOf(UNIVERSAL_ROUTER), routerOutputBalanceStart, "No leftover output in reactor"); + assertEq(tokenOut.balanceOf(address(reactor)), 0, "No leftover output in reactor"); + // swapper must have spent 100 USDC for the swap and 10 USDC for gas + assertEq( + tokenIn.balanceOf(swapper2), swapperInputBalanceStart - 100 * USDC_ONE - 10 * USDC_ONE, "Swapper input tokens" + ); + assertGe( + tokenOut.balanceOf(swapper2), swapperOutputBalanceStart + amountOutMin, "Swapper did not receive enough output" + ); + assertEq(tokenIn.balanceOf(address(permitExecutor)), fillerGasInputBalanceStart + 10 * USDC_ONE, "executor balance"); } // swapper creates one order containing a universal router swap for 100 DAI -> ETH @@ -231,7 +245,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS bytes[] memory actions = new bytes[](1); MethodParameters memory methodParameters = readFixture(json, "._UNISWAP_V3_DAI_ETH"); - actions[0] = abi.encode(ActionType.UniversalRouter, methodParameters.data); + actions[0] = abi.encode(UNIVERSAL_ROUTER, methodParameters.value, methodParameters.data); RelayOrder memory order = RelayOrder({ info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper).withDeadline(block.timestamp + 100), @@ -246,17 +260,28 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS SignedOrder memory signedOrder = SignedOrder(abi.encode(order), signOrder(swapperPrivateKey, address(PERMIT2), order)); - uint256 routerDaiBalanceBefore = DAI.balanceOf(UNIVERSAL_ROUTER); + ERC20 tokenIn = DAI; + swapperInputBalanceStart = tokenIn.balanceOf(swapper); + swapperOutputBalanceStart = swapper.balance; + routerInputBalanceStart = tokenIn.balanceOf(UNIVERSAL_ROUTER); + routerOutputBalanceStart = UNIVERSAL_ROUTER.balance; + fillerGasInputBalanceStart = USDC.balanceOf(filler); vm.prank(filler); snapStart("RelayOrderReactorIntegrationTest-testExecuteWithNativeAsOutput"); reactor.execute{value: methodParameters.value}(signedOrder); snapEnd(); - assertEq(DAI.balanceOf(UNIVERSAL_ROUTER), routerDaiBalanceBefore, "No leftover input in router"); + assertEq(tokenIn.balanceOf(UNIVERSAL_ROUTER), routerInputBalanceStart, "No leftover input in router"); + assertEq(UNIVERSAL_ROUTER.balance, routerOutputBalanceStart, "No leftover output in reactor"); assertEq(address(reactor).balance, 0, "No leftover output in reactor"); - assertGe(swapper.balance, amountOutMin, "Swapper did not receive enough output"); - assertEq(USDC.balanceOf((filler)), 10 * USDC_ONE, "filler did not receive enough USDC"); + assertEq(tokenIn.balanceOf(swapper), swapperInputBalanceStart - 100 * ONE, "Swapper input tokens"); + assertGe( + swapper.balance, + swapperOutputBalanceStart + amountOutMin - 10 * USDC_ONE, + "Swapper did not receive enough output" + ); + assertEq(USDC.balanceOf(filler), fillerGasInputBalanceStart + 10 * USDC_ONE, "filler balance"); } function testExecuteFailsIfReactorIsNotRecipient() public { @@ -274,7 +299,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS bytes[] memory actions = new bytes[](1); MethodParameters memory methodParameters = readFixture(json, "._UNISWAP_V3_DAI_USDC_RECIPIENT_NOT_REACTOR"); - actions[0] = abi.encode(ActionType.UniversalRouter, methodParameters.data); + actions[0] = abi.encode(UNIVERSAL_ROUTER, methodParameters.value, methodParameters.data); RelayOrder memory order = RelayOrder({ info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper).withDeadline(block.timestamp + 100), @@ -292,4 +317,12 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS vm.expectRevert(CurrencyLibrary.InsufficientBalance.selector); reactor.execute{value: methodParameters.value}(signedOrder); } + + function _checkpointBalances(address _swapper, address _filler, ERC20 tokenIn, ERC20 tokenOut, ERC20 gasInput) internal { + swapperInputBalanceStart = tokenIn.balanceOf(_swapper); + swapperOutputBalanceStart = tokenOut.balanceOf(_swapper); + routerInputBalanceStart = tokenIn.balanceOf(UNIVERSAL_ROUTER); + routerOutputBalanceStart = tokenOut.balanceOf(UNIVERSAL_ROUTER); + fillerGasInputBalanceStart = gasInput.balanceOf(_filler); + } } From 7fa5bcec673ded01f2857d6b9984c76b1bb75110 Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Wed, 29 Nov 2023 22:11:03 -0500 Subject: [PATCH 2/6] forge fmt --- src/reactors/RelayOrderReactor.sol | 1 - .../RelayOrderReactorIntegration.t.sol | 23 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/reactors/RelayOrderReactor.sol b/src/reactors/RelayOrderReactor.sol index 8a19b544..7cb56c60 100644 --- a/src/reactors/RelayOrderReactor.sol +++ b/src/reactors/RelayOrderReactor.sol @@ -69,7 +69,6 @@ contract RelayOrderReactor is ReactorEvents, ReactorErrors, ReentrancyGuard, IRe for (uint256 j = 0; j < actionsLength;) { (address target, uint256 value, bytes memory data) = abi.decode(order.actions[j], (address, uint256, bytes)); - (bool success,) = target.call{value: value}(data); if (!success) revert CallFailed(); unchecked { diff --git a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol index e63ab8a2..ae318a27 100644 --- a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol +++ b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol @@ -175,7 +175,7 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS address(PERMIT2), type(uint256).max - 1, // infinite approval USDC.nonces(swapper2), - type(uint256).max - 1 // infinite deadline + type(uint256).max - 1 // infinite deadline ) ) ) @@ -185,8 +185,9 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS address signer = ecrecover(digest, v, r, s); assertEq(signer, swapper2); - bytes memory permitData = - abi.encode(address(USDC), abi.encode(swapper2, address(PERMIT2), type(uint256).max - 1, type(uint256).max - 1, v, r, s)); + bytes memory permitData = abi.encode( + address(USDC), abi.encode(swapper2, address(PERMIT2), type(uint256).max - 1, type(uint256).max - 1, v, r, s) + ); bytes[] memory actions = new bytes[](1); MethodParameters memory methodParameters = readFixture(json, "._UNISWAP_V3_USDC_DAI"); @@ -219,12 +220,18 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS assertEq(tokenOut.balanceOf(address(reactor)), 0, "No leftover output in reactor"); // swapper must have spent 100 USDC for the swap and 10 USDC for gas assertEq( - tokenIn.balanceOf(swapper2), swapperInputBalanceStart - 100 * USDC_ONE - 10 * USDC_ONE, "Swapper input tokens" + tokenIn.balanceOf(swapper2), + swapperInputBalanceStart - 100 * USDC_ONE - 10 * USDC_ONE, + "Swapper input tokens" ); assertGe( - tokenOut.balanceOf(swapper2), swapperOutputBalanceStart + amountOutMin, "Swapper did not receive enough output" + tokenOut.balanceOf(swapper2), + swapperOutputBalanceStart + amountOutMin, + "Swapper did not receive enough output" + ); + assertEq( + tokenIn.balanceOf(address(permitExecutor)), fillerGasInputBalanceStart + 10 * USDC_ONE, "executor balance" ); - assertEq(tokenIn.balanceOf(address(permitExecutor)), fillerGasInputBalanceStart + 10 * USDC_ONE, "executor balance"); } // swapper creates one order containing a universal router swap for 100 DAI -> ETH @@ -318,7 +325,9 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS reactor.execute{value: methodParameters.value}(signedOrder); } - function _checkpointBalances(address _swapper, address _filler, ERC20 tokenIn, ERC20 tokenOut, ERC20 gasInput) internal { + function _checkpointBalances(address _swapper, address _filler, ERC20 tokenIn, ERC20 tokenOut, ERC20 gasInput) + internal + { swapperInputBalanceStart = tokenIn.balanceOf(_swapper); swapperOutputBalanceStart = tokenOut.balanceOf(_swapper); routerInputBalanceStart = tokenIn.balanceOf(UNIVERSAL_ROUTER); From 23c460deb78db7725016fc46b490753edd90f3d5 Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Thu, 30 Nov 2023 10:51:28 -0500 Subject: [PATCH 3/6] add comment --- lib/universal-router | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/universal-router diff --git a/lib/universal-router b/lib/universal-router deleted file mode 160000 index 7d763cb2..00000000 --- a/lib/universal-router +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7d763cb28c88bb3bce5a30ad2356722f10c4d484 From a1ff78da78d4a8429737d6a54a0738944ff2b4e3 Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Thu, 30 Nov 2023 10:51:35 -0500 Subject: [PATCH 4/6] add comment --- src/reactors/RelayOrderReactor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reactors/RelayOrderReactor.sol b/src/reactors/RelayOrderReactor.sol index 7cb56c60..d10b4ebf 100644 --- a/src/reactors/RelayOrderReactor.sol +++ b/src/reactors/RelayOrderReactor.sol @@ -16,8 +16,8 @@ import {RelayOrderLib, RelayOrder} from "../lib/RelayOrderLib.sol"; import {ResolvedRelayOrderLib} from "../lib/ResolvedRelayOrderLib.sol"; import {RelayDecayLib} from "../lib/RelayDecayLib.sol"; -/// @notice Reactor for relaying calls to UniversalRouter onchain -/// @dev This reactor only supports V2/V3 swaps, do NOT attempt to use other Universal Router commands +/// @notice Reactor for handling the execution of RelayOrders +/// @notice This contract MUST NOT have approvals or priviledged access contract RelayOrderReactor is ReactorEvents, ReactorErrors, ReentrancyGuard, IRelayOrderReactor { using SafeTransferLib for ERC20; using CurrencyLibrary for address; From acf1d76f3b81700237fd140430ba1933e0b74567 Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Thu, 30 Nov 2023 11:02:37 -0500 Subject: [PATCH 5/6] remove outdated approvals --- .../integration/RelayOrderReactorIntegration.t.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol index ae318a27..911286b3 100644 --- a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol +++ b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol @@ -73,8 +73,6 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS vm.startPrank(swapper); DAI.approve(address(PERMIT2), type(uint256).max); USDC.approve(address(PERMIT2), type(uint256).max); - PERMIT2.approve(address(DAI), address(reactor), type(uint160).max, type(uint48).max); - PERMIT2.approve(address(USDC), address(reactor), type(uint160).max, type(uint48).max); vm.stopPrank(); // Fund swappers From 8b9f411fab93d00636dc4c5fb98a465f30cc312a Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Mon, 4 Dec 2023 10:30:55 -0500 Subject: [PATCH 6/6] Add basic allowance checks --- .../integration/RelayOrderReactorIntegration.t.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol index 911286b3..f9092ec4 100644 --- a/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol +++ b/test/foundry-tests/integration/RelayOrderReactorIntegration.t.sol @@ -86,6 +86,11 @@ contract RelayOrderReactorIntegrationTest is GasSnapshot, Test, Interop, PermitS // initial assumptions assertEq(USDC.balanceOf(address(reactor)), 0, "reactor should have no USDC"); assertEq(DAI.balanceOf(address(reactor)), 0, "reactor should have no DAI"); + + (uint160 allowance,,) = PERMIT2.allowance(swapper, address(USDC), address(reactor)); + assertEq(allowance, 0, "reactor must not have allowance for tokens"); + (allowance,,) = PERMIT2.allowance(swapper, address(DAI), address(reactor)); + assertEq(allowance, 0, "reactor must not have approval for tokens"); } // swapper creates one order containing a universal router swap for 100 DAI -> USDC