CVE-2022-48216 (published 2023-01-04T16:15Z)

Uniswap Universal Router before 1.1.0 mishandles reentrancy. This would have allowed theft of funds.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-667
cwe_name: Improper Locking
description: The product does not properly acquire or release a lock on a resource, leading to unexpected resource state changes and behaviors.
extended_description: 
	   Locking is a type of synchronization behavior that ensures that multiple independently-operating processes or threads do not interfere with each other when accessing the same resource. All processes/threads are expected to follow the same steps for locking. If these steps are not followed precisely - or if no locking is done at all - then another process/thread could modify the shared resource in a way that is not visible or predictable to the original process.  This can lead to data or memory corruption, denial of service, etc.
         

Fix information:
repo_url: https://github.com/Uniswap/universal-router
commit_details: repo_url: https://github.com/Uniswap/universal-router
author: Alice
author_date: 2022-11-28 18:42:02-03:00
msg: [to consider] dirty reentrancy lock (#189)

* Gas costs for clean reentrancy lock

* Gas costs for direct reentrancy lock

* Reentrancy test

* bytecode increase
repository: repo_url: https://github.com/Uniswap/universal-router
repo_name: Uniswap/universal-router
description: Uniswap's Universal Router for NFT and ERC20 swapping
repo_language: TypeScript
owner: Uniswap
file_changes: filename: UniversalRouter.sol
change_type: ModificationType.MODIFY
diff: @@ -7,8 +7,9 @@ import {RouterParameters, RouterImmutables} from './base/RouterImmutables.sol';
 import {Constants} from './libraries/Constants.sol';
 import {Commands} from './libraries/Commands.sol';
 import {IUniversalRouter} from './interfaces/IUniversalRouter.sol';
+import {ReentrancyLock} from './base/ReentrancyLock.sol';
 
-contract UniversalRouter is RouterImmutables, IUniversalRouter, Dispatcher, RewardsCollector {
+contract UniversalRouter is RouterImmutables, IUniversalRouter, Dispatcher, RewardsCollector, ReentrancyLock {
     modifier checkDeadline(uint256 deadline) {
         if (block.timestamp > deadline) revert TransactionDeadlinePassed();
         _;
@@ -26,7 +27,7 @@ contract UniversalRouter is RouterImmutables, IUniversalRouter, Dispatcher, Rewa
     }
 
     /// @inheritdoc IUniversalRouter
-    function execute(bytes calldata commands, bytes[] calldata inputs) public payable {
+    function execute(bytes calldata commands, bytes[] calldata inputs) public payable isNotLocked {
         bool success;
         bytes memory output;
         uint256 numCommands = commands.length;

programming_language: TypeScript
filename: ReentrancyLock.sol
change_type: ModificationType.ADD
diff: @@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+contract ReentrancyLock {
+    error ContractLocked();
+
+    uint256 private isLocked = 1;
+
+    modifier isNotLocked() {
+        if (isLocked != 1) revert ContractLocked();
+        isLocked = 2;
+        _;
+        isLocked = 1;
+    }
+}

programming_language: JavaScript
filename: ReenteringProtocol.sol
change_type: ModificationType.ADD
diff: @@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.15;
+
+contract ReenteringProtocol {
+    error NotAllowedReenter();
+
+    function callAndReenter(address universalRouter, bytes calldata data) public payable {
+        (bool success,) = universalRouter.call(data);
+        if (!success) revert NotAllowedReenter();
+    }
+}

programming_language: None
filename: UniversalRouter.test.ts
change_type: ModificationType.MODIFY
diff: @@ -2,6 +2,7 @@ import { UniversalRouter, Permit2, ERC20, MockLooksRareRewardsDistributor, ERC72
 import { BigNumber, BigNumberish } from 'ethers'
 import { Pair } from '@uniswap/v2-sdk'
 import { expect } from './shared/expect'
+import { abi as ROUTER_ABI } from '../../artifacts/contracts/UniversalRouter.sol/UniversalRouter.json'
 import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json'
 import NFTX_ZAP_ABI from './shared/abis/NFTXZap.json'
 import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter'
@@ -16,6 +17,7 @@ import {
   SOURCE_MSG_SENDER,
   MAX_UINT160,
   MAX_UINT,
+  ETH_ADDRESS,
 } from './shared/constants'
 import {
   seaportOrders,
@@ -34,6 +36,7 @@ import hre from 'hardhat'
 
 const { ethers } = hre
 const nftxZapInterface = new ethers.utils.Interface(NFTX_ZAP_ABI)
+const routerInterface = new ethers.utils.Interface(ROUTER_ABI)
 
 describe('UniversalRouter', () => {
   let alice: SignerWithAddress
@@ -131,6 +134,37 @@ describe('UniversalRouter', () => {
       await expect(router['execute(bytes,bytes[])'](commands, inputs)).to.be.revertedWith('InvalidBips()')
     })
 
+    it('reverts if a malicious contract tries to reenter', async () => {
+      const reentrantProtocol = await (await ethers.getContractFactory('ReenteringProtocol')).deploy()
+
+      router = (
+        await deployUniversalRouter(
+          permit2,
+          mockLooksRareRewardsDistributor.address,
+          mockLooksRareToken.address,
+          reentrantProtocol.address
+        )
+      ).connect(alice) as UniversalRouter
+
+      planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, alice.address, 0])
+      let { commands, inputs } = planner
+
+      const sweepCalldata = routerInterface.encodeFunctionData('execute(bytes,bytes[])', [commands, inputs])
+      const reentrantCalldata = reentrantProtocol.interface.encodeFunctionData('callAndReenter', [
+        router.address,
+        sweepCalldata,
+      ])
+
+      planner = new RoutePlanner()
+      planner.addCommand(CommandType.NFTX, [0, reentrantCalldata])
+      ;({ commands, inputs } = planner)
+
+      const notAllowedReenterSelector = '0xb418cb98'
+      await expect(router['execute(bytes,bytes[])'](commands, inputs)).to.be.revertedWith(
+        `ExecutionFailed(0, "` + notAllowedReenterSelector + `")`
+      )
+    })
+
     describe('partial fills', async () => {
       let nftxValue: BigNumber
       let numCovens: number

programming_language: TypeScript
filename: CheckOwnership.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,27 +3,27 @@
 exports[`Check Ownership Gas gas: checks ownership after a seaport trade, one NFT 1`] = `
 Object {
   "calldataByteLength": 2212,
-  "gasUsed": 203708,
+  "gasUsed": 206020,
 }
 `;
 
 exports[`Check Ownership Gas gas: checks ownership after a seaport trade, two NFTs 1`] = `
 Object {
   "calldataByteLength": 5028,
-  "gasUsed": 339196,
+  "gasUsed": 341508,
 }
 `;
 
 exports[`Check Ownership Gas gas: does not check ownership after a seaport trade, one NFT 1`] = `
 Object {
   "calldataByteLength": 2052,
-  "gasUsed": 200173,
+  "gasUsed": 202485,
 }
 `;
 
 exports[`Check Ownership Gas gas: just ownership check 1`] = `
 Object {
   "calldataByteLength": 356,
-  "gasUsed": 30615,
+  "gasUsed": 32927,
 }
 `;

programming_language: TypeScript
filename: CryptoPunk.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,6 @@
 exports[`Cryptopunks purchases 1 cryptopunk gas 1`] = `
 Object {
   "calldataByteLength": 356,
-  "gasUsed": 105779,
+  "gasUsed": 109868,
 }
 `;

programming_language: Markdown
filename: Foundation.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,6 @@
 exports[`Foundation Gas Tests Buy a mental worlds NFT from Foundation gas: token id 32 of mental worlds 1`] = `
 Object {
   "calldataByteLength": 612,
-  "gasUsed": 179658,
+  "gasUsed": 183748,
 }
 `;

programming_language: Markdown
filename: LooksRare.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,13 +3,13 @@
 exports[`LooksRare Gas Tests ERC-721 Purchase gas: buy 1 ERC-721 on looks rare 1`] = `
 Object {
   "calldataByteLength": 1316,
-  "gasUsed": 246469,
+  "gasUsed": 248781,
 }
 `;
 
 exports[`LooksRare Gas Tests ERC-1155 Purchase gas: buy 1 ERC-1155 on looks rare 1`] = `
 Object {
   "calldataByteLength": 1348,
-  "gasUsed": 266494,
+  "gasUsed": 268806,
 }
 `;

programming_language: TypeScript
filename: NFT20.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,6 @@
 exports[`NFT20 Buy 3 alphabetties from NFT20 gas: purchases token ids 129, 193, 278 of Alphabetties 1`] = `
 Object {
   "calldataByteLength": 836,
-  "gasUsed": 391360,
+  "gasUsed": 393672,
 }
 `;

programming_language: Markdown
filename: NFTX.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,6 @@
 exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = `
 Object {
   "calldataByteLength": 740,
-  "gasUsed": 490835,
+  "gasUsed": 494924,
 }
 `;

programming_language: TypeScript
filename: Payments.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,48 +3,48 @@
 exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = `
 Object {
   "calldataByteLength": 356,
-  "gasUsed": 37194,
+  "gasUsed": 39506,
 }
 `;
 
 exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = `
 Object {
   "calldataByteLength": 516,
-  "gasUsed": 66436,
+  "gasUsed": 68748,
 }
 `;
 
 exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = `
 Object {
   "calldataByteLength": 356,
-  "gasUsed": 36168,
+  "gasUsed": 38480,
 }
 `;
 
 exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = `
 Object {
   "calldataByteLength": 356,
-  "gasUsed": 31759,
+  "gasUsed": 34071,
 }
 `;
 
 exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = `
 Object {
   "calldataByteLength": 324,
-  "gasUsed": 44647,
+  "gasUsed": 46959,
 }
 `;
 
 exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = `
 Object {
   "calldataByteLength": 644,
-  "gasUsed": 52003,
+  "gasUsed": 54315,
 }
 `;
 
 exports[`Payments Gas Tests Individual Command Tests gas: WRAP_ETH 1`] = `
 Object {
   "calldataByteLength": 324,
-  "gasUsed": 53491,
+  "gasUsed": 57580,
 }
 `;

programming_language: TypeScript
filename: Router.gas.test.ts.snap
change_type: ModificationType.DELETE
diff: @@ -1,17 +0,0 @@
-// Just Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Router Gas Tests gas: bytecode size 1`] = `17011`;
-
-exports[`Router Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = `
-Object {
-  "calldataByteLength": 2500,
-  "gasUsed": 301927,
-}
-`;
-
-exports[`Router Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = `
-Object {
-  "calldataByteLength": 2052,
-  "gasUsed": 200173,
-}
-`;

programming_language: unknown
filename: Seaport.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,13 +3,13 @@
 exports[`Seaport Gas Tests gas: fulfillAdvancedOrder 1`] = `
 Object {
   "calldataByteLength": 2052,
-  "gasUsed": 200173,
+  "gasUsed": 202485,
 }
 `;
 
 exports[`Seaport Gas Tests gas: fulfillAvailableAdvancedOrders 1 orders 1`] = `
 Object {
   "calldataByteLength": 4708,
-  "gasUsed": 332120,
+  "gasUsed": 334432,
 }
 `;

programming_language: TypeScript
filename: Sudoswap.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,6 @@
 exports[`Sudoswap Gas Tests Buy 3 sudolets from sudoswap gas: purchases token ids 80, 35, 93 of Sudolets 1`] = `
 Object {
   "calldataByteLength": 836,
-  "gasUsed": 190323,
+  "gasUsed": 192635,
 }
 `;

programming_language: Markdown
filename: Uniswap.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,77 +3,77 @@
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = `
 Object {
   "calldataByteLength": 836,
-  "gasUsed": 197181,
+  "gasUsed": 199493,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = `
 Object {
   "calldataByteLength": 836,
-  "gasUsed": 189735,
+  "gasUsed": 192047,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = `
 Object {
   "calldataByteLength": 1540,
-  "gasUsed": 306351,
+  "gasUsed": 308663,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = `
 Object {
   "calldataByteLength": 1220,
-  "gasUsed": 314308,
+  "gasUsed": 316620,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch 1`] = `
 Object {
   "calldataByteLength": 1284,
-  "gasUsed": 316090,
+  "gasUsed": 318402,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit 1`] = `
 Object {
   "calldataByteLength": 900,
-  "gasUsed": 310075,
+  "gasUsed": 312387,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = `
 Object {
   "calldataByteLength": 996,
-  "gasUsed": 180414,
+  "gasUsed": 182726,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = `
 Object {
   "calldataByteLength": 996,
-  "gasUsed": 180189,
+  "gasUsed": 182501,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = `
 Object {
   "calldataByteLength": 964,
-  "gasUsed": 197847,
+  "gasUsed": 200159,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = `
 Object {
   "calldataByteLength": 964,
-  "gasUsed": 188177,
+  "gasUsed": 190489,
 }
 `;
 
 exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = `
 Object {
   "calldataByteLength": 1124,
-  "gasUsed": 214422,
+  "gasUsed": 218512,
 }
 `;
 
@@ -115,98 +115,98 @@ Object {
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = `
 Object {
   "calldataByteLength": 836,
-  "gasUsed": 128473,
+  "gasUsed": 130785,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 516,
-  "gasUsed": 107645,
+  "gasUsed": 109957,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = `
 Object {
   "calldataByteLength": 580,
-  "gasUsed": 246066,
+  "gasUsed": 248378,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops, no deadline 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 245808,
+  "gasUsed": 248126,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 178544,
+  "gasUsed": 180856,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops, MSG_SENDER flag 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 178544,
+  "gasUsed": 180856,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 516,
-  "gasUsed": 107030,
+  "gasUsed": 109342,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = `
 Object {
   "calldataByteLength": 580,
-  "gasUsed": 250553,
+  "gasUsed": 252865,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 180512,
+  "gasUsed": 182824,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 644,
-  "gasUsed": 124271,
+  "gasUsed": 126583,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 804,
-  "gasUsed": 129272,
+  "gasUsed": 131584,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = `
 Object {
   "calldataByteLength": 964,
-  "gasUsed": 144977,
+  "gasUsed": 147289,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 644,
-  "gasUsed": 107952,
+  "gasUsed": 110264,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 772,
-  "gasUsed": 138445,
+  "gasUsed": 142535,
 }
 `;
 
@@ -255,69 +255,69 @@ Object {
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 516,
-  "gasUsed": 116041,
+  "gasUsed": 118353,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 277424,
+  "gasUsed": 279736,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 192408,
+  "gasUsed": 194720,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = `
 Object {
   "calldataByteLength": 516,
-  "gasUsed": 117315,
+  "gasUsed": 119627,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 421870,
+  "gasUsed": 424182,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = `
 Object {
   "calldataByteLength": 548,
-  "gasUsed": 314568,
+  "gasUsed": 316880,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = `
 Object {
   "calldataByteLength": 644,
-  "gasUsed": 132715,
+  "gasUsed": 135027,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = `
 Object {
   "calldataByteLength": 644,
-  "gasUsed": 134061,
+  "gasUsed": 136373,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = `
 Object {
   "calldataByteLength": 644,
-  "gasUsed": 245799,
+  "gasUsed": 248111,
 }
 `;
 
 exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = `
 Object {
   "calldataByteLength": 772,
-  "gasUsed": 140449,
+  "gasUsed": 144539,
 }
 `;

programming_language: Scala
filename: UniversalRouter.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -1,17 +1,17 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17017`;
+exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17322`;
 
 exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = `
 Object {
   "calldataByteLength": 2500,
-  "gasUsed": 301863,
+  "gasUsed": 304175,
 }
 `;
 
 exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = `
 Object {
   "calldataByteLength": 2052,
-  "gasUsed": 200173,
+  "gasUsed": 202485,
 }
 `;

programming_language: TypeScript
filename: UniversalVSSwapRouter.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -7,32 +7,32 @@ Object {
 }
 `;
 
-exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1172093`;
+exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1179047`;
 
-exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1209450`;
+exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1216404`;
 
 exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`;
 
-exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3262025`;
+exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3285205`;
 
-exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3428830`;
+exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3452010`;
 
 exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `278117`;
 
-exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4355287`;
+exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4390057`;
 
-exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4576897`;
+exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4611667`;
 
 exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`;
 
-exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `538455`;
+exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `540773`;
 
-exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `538773`;
+exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `541091`;
 
 exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`;
 
-exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `312301`;
+exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `314619`;
 
-exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `312237`;
+exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `314555`;
 
 exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`;

programming_language: TypeScript
filename: X2Y2.gas.test.ts.snap
change_type: ModificationType.MODIFY
diff: @@ -3,13 +3,13 @@
 exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = `
 Object {
   "calldataByteLength": 2212,
-  "gasUsed": 212190,
+  "gasUsed": 214502,
 }
 `;
 
 exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = `
 Object {
   "calldataByteLength": 2276,
-  "gasUsed": 213385,
+  "gasUsed": 215697,
 }
 `;

programming_language: TypeScript
filename: deployUniversalRouter.ts
change_type: ModificationType.MODIFY
diff: @@ -14,13 +14,14 @@ import {
 export async function deployRouter(
   permit2: Permit2,
   mockLooksRareRewardsDistributor?: string,
-  mockLooksRareToken?: string
+  mockLooksRareToken?: string,
+  mockReentrantProtocol?: string
 ): Promise<UniversalRouter> {
   const routerParameters = {
     permit2: permit2.address,
     weth9: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
     seaport: '0x00000000006c3852cbEf3e08E8dF289169EdE581',
-    nftxZap: '0x0fc584529a2AEfA997697FAfAcbA5831faC0c22d',
+    nftxZap: mockReentrantProtocol ?? '0x0fc584529a2AEfA997697FAfAcbA5831faC0c22d',
     x2y2: '0x74312363e45DCaBA76c59ec49a7Aa8A65a67EeD3',
     foundation: '0xcDA72070E455bb31C7690a170224Ce43623d0B6f',
     sudoswap: '0x2B2e8cDA09bBA9660dCA5cB6233787738Ad68329',

programming_language: TypeScript