From 6cf84c8c8889661c160606a064ef03a64547f20f Mon Sep 17 00:00:00 2001 From: Daniel Gretzke Date: Sat, 15 Jun 2024 00:59:50 +0200 Subject: [PATCH 01/13] Prepare for v4 work (#346) * update hardhat * remove symlinks by using hardhat-foundry use hardhat-foundry package to use foundry remappings to compile. Symlinks are no longer necessary After upgrade HH412 error was thrown caused by the existing symlinks ref: https://hardhat.org/hardhat-runner/docs/errors#HH412 https://github.com/NomicFoundation/hardhat/issues/3623 * update compiler version to ^0.8.24 supporting the cancun upgrades * fix ci * fix lock file * Update yarn.lock * regenerate gas snapshots * install foundry in ci * Use mainnet permit2 (#347) * solc upgrade to 0.8.26 * use mainnet permit2 work started * fix uniswap tests * Remove block from resetFork * Refactor to fetch fee tiers * remove NFT protocols for V4 router (#348) * first pass * fix forge builds * fix reentrancy test * Add check to receive * remove .only rip * add todo for tests that need wrtiting * update readme and planner --------- Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com> Co-authored-by: Alice Henshaw --- .github/workflows/forge.yml | 5 +- .github/workflows/lint.yml | 5 +- .github/workflows/test.yml | 10 +- .gitignore | 1 + .prettierignore | 1 + README.md | 44 +- contracts/UniversalRouter.sol | 31 +- contracts/base/Callbacks.sol | 25 +- contracts/base/Dispatcher.sol | 534 ++---- contracts/base/LockAndMsgSender.sol | 2 +- contracts/base/RewardsCollector.sol | 25 - contracts/base/RouterImmutables.sol | 16 +- contracts/deploy/UnsupportedProtocol.sol | 2 +- contracts/interfaces/IRewardsCollector.sol | 13 - contracts/interfaces/IUniversalRouter.sol | 11 +- .../external/ICryptoPunksMarket.sol | 11 - contracts/libraries/Commands.sol | 24 +- contracts/libraries/Constants.sol | 2 +- contracts/modules/NFTImmutables.sol | 73 - contracts/modules/Payments.sol | 38 +- contracts/modules/PaymentsImmutables.sol | 18 +- contracts/modules/Permit2Payments.sol | 2 +- .../modules/uniswap/UniswapImmutables.sol | 2 +- contracts/modules/uniswap/v2/V2SwapRouter.sol | 2 +- contracts/modules/uniswap/v3/V3SwapRouter.sol | 2 +- contracts/test/ExampleModule.sol | 2 +- contracts/test/ImportsForTypechain.sol | 9 - .../test/MockLooksRareRewardsDistributor.sol | 18 - contracts/test/ReenteringProtocol.sol | 11 - contracts/test/ReenteringWETH.sol | 23 + contracts/test/TestCustomErrors.sol | 2 +- foundry.toml | 9 +- hardhat.config.ts | 7 +- lib/permit2 | 2 +- package.json | 5 +- permit2 | 1 - script/DeployUniversalRouter.s.sol | 37 +- script/deployParameters/DeployArbitrum.s.sol | 14 - .../DeployArbitrumGoerli.s.sol | 14 - script/deployParameters/DeployAvalanche.s.sol | 14 - script/deployParameters/DeployBSC.s.sol | 14 - script/deployParameters/DeployBase.s.sol | 14 - .../deployParameters/DeployBaseGoerli.s.sol | 14 - script/deployParameters/DeployBlast.s.sol | 14 - script/deployParameters/DeployCelo.s.sol | 14 - .../DeployCeloAlfajores.s.sol | 14 - script/deployParameters/DeployGoerli.s.sol | 14 - script/deployParameters/DeployMainnet.s.sol | 14 - script/deployParameters/DeployOptimism.s.sol | 14 - .../DeployOptimismGoerli.s.sol | 14 - script/deployParameters/DeployPolygon.s.sol | 14 - .../DeployPolygonMumbai.s.sol | 14 - script/deployParameters/DeploySepolia.s.sol | 14 - solmate | 1 - test/foundry-tests/UniswapV2.t.sol | 23 +- test/foundry-tests/UniversalRouter.t.sol | 67 +- test/foundry-tests/mock/MockERC1155.sol | 13 - test/foundry-tests/mock/MockERC20.sol | 2 +- test/integration-tests/CheckOwnership.test.ts | 181 +- test/integration-tests/Cryptopunks.test.ts | 49 - test/integration-tests/Element.test.ts | 67 - test/integration-tests/Foundation.test.ts | 54 - test/integration-tests/LooksRareV2.test.ts | 113 -- test/integration-tests/NFT20.test.ts | 63 - test/integration-tests/NFTX.test.ts | 148 -- test/integration-tests/Seaport.test.ts | 313 ---- test/integration-tests/Sudoswap.test.ts | 156 -- test/integration-tests/Uniswap.test.ts | 35 +- .../integration-tests/UniversalRouter.test.ts | 265 +-- test/integration-tests/X2Y2.test.ts | 104 -- .../gas-tests/CheckOwnership.gas.test.ts | 101 +- .../gas-tests/CryptoPunk.gas.test.ts | 39 - .../gas-tests/Element.gas.test.ts | 54 - .../gas-tests/Foundation.gas.test.ts | 51 - .../gas-tests/LooksRareV2.gas.test.ts | 98 - .../gas-tests/NFT20.gas.test.ts | 53 - .../gas-tests/NFTX.gas.test.ts | 49 - .../gas-tests/Payments.gas.test.ts | 16 +- .../gas-tests/SeaportV1_4.gas.test.ts | 107 -- .../gas-tests/SeaportV1_5.gas.test.ts | 60 - .../gas-tests/Sudoswap.gas.test.ts | 130 -- .../gas-tests/Uniswap.gas.test.ts | 35 +- .../gas-tests/UniversalRouter.gas.test.ts | 95 +- .../UniversalVSSwapRouter.gas.test.ts | 64 +- .../gas-tests/X2Y2.gas.test.ts | 91 - .../CheckOwnership.gas.test.ts.snap | 30 +- .../CryptoPunk.gas.test.ts 2.snap | 8 - .../__snapshots__/CryptoPunk.gas.test.ts.snap | 8 - .../__snapshots__/Element.gas.test.ts.snap | 8 - .../__snapshots__/Foundation.gas.test.ts.snap | 8 - .../LooksRareV2.gas.test.ts.snap | 15 - .../__snapshots__/NFT20.gas.test.ts.snap | 8 - .../__snapshots__/NFTX.gas.test.ts.snap | 8 - .../__snapshots__/Payments.gas.test.ts.snap | 21 +- .../SeaportV1_4.gas.test.ts.snap | 15 - .../SeaportV1_5.gas.test.ts.snap | 15 - .../__snapshots__/Sudoswap.gas.test.ts.snap | 22 - .../__snapshots__/Uniswap.gas.test.ts.snap | 92 +- .../UniversalRouter.gas.test.ts.snap | 23 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 32 +- .../__snapshots__/X2Y2.gas.test.ts.snap | 15 - .../shared/abis/Cryptopunks.json | 595 ------- .../shared/abis/Element.json | 1022 ----------- .../shared/abis/Foundation.json | 1581 ----------------- .../shared/abis/LooksRareV2.json | 777 -------- test/integration-tests/shared/abis/NFT20.json | 242 --- .../shared/abis/NFTXZap.json | 250 --- .../shared/abis/Seaport.json | 1143 ------------ .../shared/abis/Sudoswap.json | 1109 ------------ test/integration-tests/shared/abis/X2Y2.json | 1086 ----------- test/integration-tests/shared/constants.ts | 27 +- .../shared/deployUniversalRouter.ts | 46 +- .../shared/mainnetForkHelpers.ts | 34 +- .../shared/orders/Element.json | 30 - .../shared/orders/LooksRareV2.json | 74 - .../shared/orders/SeaportV1_4.json | 151 -- .../shared/orders/SeaportV1_5.json | 158 -- .../integration-tests/shared/orders/X2Y2.json | 14 - test/integration-tests/shared/planner.ts | 53 +- .../shared/protocolHelpers/element.ts | 123 -- .../shared/protocolHelpers/looksRareV2.ts | 120 -- .../shared/protocolHelpers/permit2.ts | 11 +- .../shared/protocolHelpers/seaport.ts | 120 -- .../shared/protocolHelpers/x2y2.ts | 17 - .../shared/swapRouter02Helpers.ts | 41 +- yarn.lock | 515 ++---- 126 files changed, 613 insertions(+), 13000 deletions(-) delete mode 100644 contracts/base/RewardsCollector.sol delete mode 100644 contracts/interfaces/IRewardsCollector.sol delete mode 100644 contracts/interfaces/external/ICryptoPunksMarket.sol delete mode 100644 contracts/modules/NFTImmutables.sol delete mode 100644 contracts/test/ImportsForTypechain.sol delete mode 100644 contracts/test/MockLooksRareRewardsDistributor.sol delete mode 100644 contracts/test/ReenteringProtocol.sol create mode 100644 contracts/test/ReenteringWETH.sol delete mode 120000 permit2 delete mode 120000 solmate delete mode 100644 test/foundry-tests/mock/MockERC1155.sol delete mode 100644 test/integration-tests/Cryptopunks.test.ts delete mode 100644 test/integration-tests/Element.test.ts delete mode 100644 test/integration-tests/Foundation.test.ts delete mode 100644 test/integration-tests/LooksRareV2.test.ts delete mode 100644 test/integration-tests/NFT20.test.ts delete mode 100644 test/integration-tests/NFTX.test.ts delete mode 100644 test/integration-tests/Seaport.test.ts delete mode 100644 test/integration-tests/Sudoswap.test.ts delete mode 100644 test/integration-tests/X2Y2.test.ts delete mode 100644 test/integration-tests/gas-tests/CryptoPunk.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/Element.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/Foundation.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/LooksRareV2.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/NFT20.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/NFTX.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/SeaportV1_4.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/SeaportV1_5.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/Sudoswap.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/X2Y2.gas.test.ts delete mode 100644 test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts 2.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap delete mode 100644 test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap delete mode 100644 test/integration-tests/shared/abis/Cryptopunks.json delete mode 100644 test/integration-tests/shared/abis/Element.json delete mode 100644 test/integration-tests/shared/abis/Foundation.json delete mode 100644 test/integration-tests/shared/abis/LooksRareV2.json delete mode 100644 test/integration-tests/shared/abis/NFT20.json delete mode 100644 test/integration-tests/shared/abis/NFTXZap.json delete mode 100644 test/integration-tests/shared/abis/Seaport.json delete mode 100644 test/integration-tests/shared/abis/Sudoswap.json delete mode 100644 test/integration-tests/shared/abis/X2Y2.json delete mode 100644 test/integration-tests/shared/orders/Element.json delete mode 100644 test/integration-tests/shared/orders/LooksRareV2.json delete mode 100644 test/integration-tests/shared/orders/SeaportV1_4.json delete mode 100644 test/integration-tests/shared/orders/SeaportV1_5.json delete mode 100644 test/integration-tests/shared/orders/X2Y2.json delete mode 100644 test/integration-tests/shared/protocolHelpers/element.ts delete mode 100644 test/integration-tests/shared/protocolHelpers/looksRareV2.ts delete mode 100644 test/integration-tests/shared/protocolHelpers/seaport.ts delete mode 100644 test/integration-tests/shared/protocolHelpers/x2y2.ts diff --git a/.github/workflows/forge.yml b/.github/workflows/forge.yml index 68d15a52..7deb0df6 100644 --- a/.github/workflows/forge.yml +++ b/.github/workflows/forge.yml @@ -24,9 +24,12 @@ jobs: # Must Insall node modules for forge to reference in remappings - uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x registry-url: https://registry.npmjs.org + - name: Install Yarn + run: npm install -g yarn + - id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 40cfab0f..1573cf2f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,9 +19,12 @@ jobs: - name: Set up node uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x registry-url: https://registry.npmjs.org + - name: Install Yarn + run: npm install -g yarn + - name: Install dependencies run: yarn install --frozen-lockfile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e1d8c60b..2cdb27f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,12 @@ jobs: - uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x registry-url: https://registry.npmjs.org + - name: Install Yarn + run: npm install -g yarn + - id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" @@ -34,6 +37,11 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + # This is required separately from yarn test because it generates the typechain definitions - name: Compile run: yarn compile diff --git a/.gitignore b/.gitignore index 4e424b06..8ec726a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Compiler files cache/ +cache_hardhat/ out/ # Ignores development broadcast logs diff --git a/.prettierignore b/.prettierignore index e8dbe480..2b352d05 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,4 @@ out openzeppelin-contracts permit2 solmate +cache_hardhat \ No newline at end of file diff --git a/README.md b/README.md index 9cd337df..9d785747 100644 --- a/README.md +++ b/README.md @@ -84,49 +84,12 @@ Each command is a `bytes1` containing the following 8 bits: ├──────┼───────────────────────────────┤ │ 0x0d │ PERMIT2_TRANSFER_FROM_BATCH │ ├──────┼───────────────────────────────┤ - │ 0x0e │ ------- │ - ├──────┼───────────────────────────────┤ - │ 0x0f │ ------- │ - ├──────┼───────────────────────────────┤ - │ 0x10 │ SEAPORT_V1_5 │ - ├──────┼───────────────────────────────┤ - │ 0x11 │ LOOKS_RARE_721 │ - ├──────┼───────────────────────────────┤ - │ 0x12 │ NFTX │ - ├──────┼───────────────────────────────┤ - │ 0x13 │ CRYPTOPUNKS │ - ├──────┼───────────────────────────────┤ - │ 0x14 │ LOOKS_RARE_1155 │ - ├──────┼───────────────────────────────┤ - │ 0x15 │ OWNER_CHECK_721 │ - ├──────┼───────────────────────────────┤ - │ 0x16 │ OWNER_CHECK_1155 │ - ├──────┼───────────────────────────────┤ - │ 0x17 │ SWEEP_ERC721 │ - ├──────┼───────────────────────────────┤ - │ 0x18 │ X2Y2_721 │ - ├──────┼───────────────────────────────┤ - │ 0x19 │ SUDOSWAP │ - ├──────┼───────────────────────────────┤ - │ 0x1a │ NFT20 │ - ├──────┼───────────────────────────────┤ - │ 0x1b │ X2Y2_1155 │ - ├──────┼───────────────────────────────┤ - │ 0x1c │ FOUNDATION │ - ├──────┼───────────────────────────────┤ - │ 0x1d │ SWEEP_ERC1155 │ - ├──────┼───────────────────────────────┤ - │ 0x1e │ ELEMENT_MARKET │ - ├──────┼───────────────────────────────┤ - │ 0x1f │ ------- │ - ├──────┼───────────────────────────────┤ - │ 0x20 │ SEAPORT_V1_4 │ + │ 0x0e-│ ------- │ + │ 0x20 │ │ ├──────┼───────────────────────────────┤ │ 0x21 │ EXECUTE_SUB_PLAN │ ├──────┼───────────────────────────────┤ - │ 0x22 │ APPROVE_ERC20 │ - ├──────┼───────────────────────────────┤ - │ 0x23-│ ------- │ + │ 0x22-│ ------- │ │ 0x3f │ │ └──────┴───────────────────────────────┘ ``` @@ -180,7 +143,6 @@ INFURA_API_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' ```console yarn install -yarn symlink yarn compile yarn test ``` diff --git a/contracts/UniversalRouter.sol b/contracts/UniversalRouter.sol index c54d50ec..d8212e9e 100644 --- a/contracts/UniversalRouter.sol +++ b/contracts/UniversalRouter.sol @@ -1,17 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; // Command implementations import {Dispatcher} from './base/Dispatcher.sol'; -import {RewardsCollector} from './base/RewardsCollector.sol'; import {RouterParameters} from './base/RouterImmutables.sol'; import {PaymentsImmutables, PaymentsParameters} from './modules/PaymentsImmutables.sol'; -import {NFTImmutables, NFTParameters} from './modules/NFTImmutables.sol'; import {UniswapImmutables, UniswapParameters} from './modules/uniswap/UniswapImmutables.sol'; import {Commands} from './libraries/Commands.sol'; import {IUniversalRouter} from './interfaces/IUniversalRouter.sol'; -contract UniversalRouter is IUniversalRouter, Dispatcher, RewardsCollector { +contract UniversalRouter is IUniversalRouter, Dispatcher { modifier checkDeadline(uint256 deadline) { if (block.timestamp > deadline) revert TransactionDeadlinePassed(); _; @@ -21,24 +19,7 @@ contract UniversalRouter is IUniversalRouter, Dispatcher, RewardsCollector { UniswapImmutables( UniswapParameters(params.v2Factory, params.v3Factory, params.pairInitCodeHash, params.poolInitCodeHash) ) - PaymentsImmutables(PaymentsParameters(params.permit2, params.weth9, params.openseaConduit, params.sudoswap)) - NFTImmutables( - NFTParameters( - params.seaportV1_5, - params.seaportV1_4, - params.nftxZap, - params.x2y2, - params.foundation, - params.sudoswap, - params.elementMarket, - params.nft20Zap, - params.cryptopunks, - params.looksRareV2, - params.routerRewardsDistributor, - params.looksRareRewardsDistributor, - params.looksRareToken - ) - ) + PaymentsImmutables(PaymentsParameters(params.permit2, params.weth9)) {} /// @inheritdoc IUniversalRouter @@ -79,6 +60,8 @@ contract UniversalRouter is IUniversalRouter, Dispatcher, RewardsCollector { return command & Commands.FLAG_ALLOW_REVERT == 0; } - /// @notice To receive ETH from WETH and NFT protocols - receive() external payable {} + /// @notice To receive ETH from WETH + receive() external payable { + if (msg.sender != address(WETH9)) revert InvalidEthSender(); + } } diff --git a/contracts/base/Callbacks.sol b/contracts/base/Callbacks.sol index fa097b59..f2360c25 100644 --- a/contracts/base/Callbacks.sol +++ b/contracts/base/Callbacks.sol @@ -1,32 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; -import {IERC721Receiver} from '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; -import {IERC1155Receiver} from '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; /// @title ERC Callback Support /// @notice Implements various functions introduced by a variety of ERCs for security reasons. /// All are called by external contracts to ensure that this contract safely supports the ERC in question. -contract Callbacks is IERC721Receiver, IERC1155Receiver { - function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { - return this.onERC721Received.selector; - } - - function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) - external - pure - returns (bytes4) - { - return this.onERC1155BatchReceived.selector; - } - +contract Callbacks { function supportsInterface(bytes4 interfaceId) external pure returns (bool) { - return interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId - || interfaceId == type(IERC165).interfaceId; + return interfaceId == type(IERC165).interfaceId; } } diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 5c1f5f1c..8f80c589 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -1,30 +1,23 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {V2SwapRouter} from '../modules/uniswap/v2/V2SwapRouter.sol'; import {V3SwapRouter} from '../modules/uniswap/v3/V3SwapRouter.sol'; import {BytesLib} from '../modules/uniswap/v3/BytesLib.sol'; import {Payments} from '../modules/Payments.sol'; import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol'; -import {NFTImmutables} from '../modules/NFTImmutables.sol'; import {Callbacks} from '../base/Callbacks.sol'; import {Commands} from '../libraries/Commands.sol'; import {LockAndMsgSender} from './LockAndMsgSender.sol'; -import {ERC721} from 'solmate/src/tokens/ERC721.sol'; -import {ERC1155} from 'solmate/src/tokens/ERC1155.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; -import {ICryptoPunksMarket} from '../interfaces/external/ICryptoPunksMarket.sol'; /// @title Decodes and Executes Commands /// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command -abstract contract Dispatcher is NFTImmutables, Payments, V2SwapRouter, V3SwapRouter, Callbacks, LockAndMsgSender { +abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, Callbacks, LockAndMsgSender { using BytesLib for bytes; error InvalidCommandType(uint256 commandType); - error BuyPunkFailed(); - error InvalidOwnerERC721(); - error InvalidOwnerERC1155(); error BalanceTooLow(); /// @notice Decodes and executes the given command with the given inputs @@ -38,319 +31,182 @@ abstract contract Dispatcher is NFTImmutables, Payments, V2SwapRouter, V3SwapRou success = true; + // 0x00 <= command < 0x20 if (command < Commands.FOURTH_IF_BOUNDARY) { - if (command < Commands.SECOND_IF_BOUNDARY) { - // 0x00 <= command < 0x08 - if (command < Commands.FIRST_IF_BOUNDARY) { - if (command == Commands.V3_SWAP_EXACT_IN) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountIn; - uint256 amountOutMin; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountIn := calldataload(add(inputs.offset, 0x20)) - amountOutMin := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? lockedBy : address(this); - v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); - } else if (command == Commands.V3_SWAP_EXACT_OUT) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountOut; - uint256 amountInMax; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountOut := calldataload(add(inputs.offset, 0x20)) - amountInMax := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? lockedBy : address(this); - v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); - } else if (command == Commands.PERMIT2_TRANSFER_FROM) { - // equivalent: abi.decode(inputs, (address, address, uint160)) - address token; - address recipient; - uint160 amount; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - amount := calldataload(add(inputs.offset, 0x40)) - } - permit2TransferFrom(token, lockedBy, map(recipient), amount); - } else if (command == Commands.PERMIT2_PERMIT_BATCH) { - (IAllowanceTransfer.PermitBatch memory permitBatch,) = - abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); - bytes calldata data = inputs.toBytes(1); - PERMIT2.permit(lockedBy, permitBatch, data); - } else if (command == Commands.SWEEP) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint160 amountMin; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - amountMin := calldataload(add(inputs.offset, 0x40)) - } - Payments.sweep(token, map(recipient), amountMin); - } else if (command == Commands.TRANSFER) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint256 value; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - value := calldataload(add(inputs.offset, 0x40)) - } - Payments.pay(token, map(recipient), value); - } else if (command == Commands.PAY_PORTION) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint256 bips; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - bips := calldataload(add(inputs.offset, 0x40)) - } - Payments.payPortion(token, map(recipient), bips); - } else { - // placeholder area for command 0x07 - revert InvalidCommandType(command); + // 0x00 <= command < 0x08 + if (command < Commands.FIRST_IF_BOUNDARY) { + if (command == Commands.V3_SWAP_EXACT_IN) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountIn; + uint256 amountOutMin; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountIn := calldataload(add(inputs.offset, 0x20)) + amountOutMin := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) } - // 0x08 <= command < 0x10 - } else { - if (command == Commands.V2_SWAP_EXACT_IN) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountIn; - uint256 amountOutMin; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountIn := calldataload(add(inputs.offset, 0x20)) - amountOutMin := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? lockedBy : address(this); - v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); - } else if (command == Commands.V2_SWAP_EXACT_OUT) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountOut; - uint256 amountInMax; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountOut := calldataload(add(inputs.offset, 0x20)) - amountInMax := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? lockedBy : address(this); - v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); - } else if (command == Commands.PERMIT2_PERMIT) { - // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes)) - IAllowanceTransfer.PermitSingle calldata permitSingle; - assembly { - permitSingle := inputs.offset - } - bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) - PERMIT2.permit(lockedBy, permitSingle, data); - } else if (command == Commands.WRAP_ETH) { - // equivalent: abi.decode(inputs, (address, uint256)) - address recipient; - uint256 amountMin; - assembly { - recipient := calldataload(inputs.offset) - amountMin := calldataload(add(inputs.offset, 0x20)) - } - Payments.wrapETH(map(recipient), amountMin); - } else if (command == Commands.UNWRAP_WETH) { - // equivalent: abi.decode(inputs, (address, uint256)) - address recipient; - uint256 amountMin; - assembly { - recipient := calldataload(inputs.offset) - amountMin := calldataload(add(inputs.offset, 0x20)) - } - Payments.unwrapWETH9(map(recipient), amountMin); - } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { - (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = - abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); - permit2TransferFrom(batchDetails, lockedBy); - } else if (command == Commands.BALANCE_CHECK_ERC20) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address owner; - address token; - uint256 minBalance; - assembly { - owner := calldataload(inputs.offset) - token := calldataload(add(inputs.offset, 0x20)) - minBalance := calldataload(add(inputs.offset, 0x40)) - } - success = (ERC20(token).balanceOf(owner) >= minBalance); - if (!success) output = abi.encodePacked(BalanceTooLow.selector); - } else { - // placeholder area for command 0x0f - revert InvalidCommandType(command); + bytes calldata path = inputs.toBytes(3); + address payer = payerIsUser ? lockedBy : address(this); + v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); + } else if (command == Commands.V3_SWAP_EXACT_OUT) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountOut; + uint256 amountInMax; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountOut := calldataload(add(inputs.offset, 0x20)) + amountInMax := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) + } + bytes calldata path = inputs.toBytes(3); + address payer = payerIsUser ? lockedBy : address(this); + v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); + } else if (command == Commands.PERMIT2_TRANSFER_FROM) { + // equivalent: abi.decode(inputs, (address, address, uint160)) + address token; + address recipient; + uint160 amount; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + amount := calldataload(add(inputs.offset, 0x40)) + } + permit2TransferFrom(token, lockedBy, map(recipient), amount); + } else if (command == Commands.PERMIT2_PERMIT_BATCH) { + (IAllowanceTransfer.PermitBatch memory permitBatch,) = + abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); + bytes calldata data = inputs.toBytes(1); + PERMIT2.permit(lockedBy, permitBatch, data); + } else if (command == Commands.SWEEP) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address token; + address recipient; + uint160 amountMin; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + amountMin := calldataload(add(inputs.offset, 0x40)) + } + Payments.sweep(token, map(recipient), amountMin); + } else if (command == Commands.TRANSFER) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address token; + address recipient; + uint256 value; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + value := calldataload(add(inputs.offset, 0x40)) } + Payments.pay(token, map(recipient), value); + } else if (command == Commands.PAY_PORTION) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address token; + address recipient; + uint256 bips; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + bips := calldataload(add(inputs.offset, 0x40)) + } + Payments.payPortion(token, map(recipient), bips); + } else { + // placeholder area for command 0x07 + revert InvalidCommandType(command); } - // 0x10 <= command + // 0x08 <= command < 0x10 } else { - // 0x10 <= command < 0x18 - if (command < Commands.THIRD_IF_BOUNDARY) { - if (command == Commands.SEAPORT_V1_5) { - /// @dev Seaport 1.4 and 1.5 allow for orders to be created by contracts. - /// These orders pass control to the contract offerers during fufillment, - /// allowing them to perform any number of destructive actions as a holder of the NFT. - /// Integrators should be aware that in some scenarios: e.g. purchasing an NFT that allows the holder - /// to claim another NFT, the contract offerer can "steal" the claim during order fufillment. - /// For some such purchases, an OWNER_CHECK command can be prepended to ensure that all tokens have the desired owner at the end of the transaction. - /// This is also outlined in the Seaport documentation: https://github.com/ProjectOpenSea/seaport/blob/main/docs/SeaportDocumentation.md - (uint256 value, bytes calldata data) = getValueAndData(inputs); - (success, output) = SEAPORT_V1_5.call{value: value}(data); - } else if (command == Commands.LOOKS_RARE_V2) { - // equivalent: abi.decode(inputs, (uint256, bytes)) - uint256 value; - assembly { - value := calldataload(inputs.offset) - } - bytes calldata data = inputs.toBytes(1); - (success, output) = LOOKS_RARE_V2.call{value: value}(data); - } else if (command == Commands.NFTX) { - // equivalent: abi.decode(inputs, (uint256, bytes)) - (uint256 value, bytes calldata data) = getValueAndData(inputs); - (success, output) = NFTX_ZAP.call{value: value}(data); - } else if (command == Commands.CRYPTOPUNKS) { - // equivalent: abi.decode(inputs, (uint256, address, uint256)) - uint256 punkId; - address recipient; - uint256 value; - assembly { - punkId := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - value := calldataload(add(inputs.offset, 0x40)) - } - (success, output) = CRYPTOPUNKS.call{value: value}( - abi.encodeWithSelector(ICryptoPunksMarket.buyPunk.selector, punkId) - ); - if (success) ICryptoPunksMarket(CRYPTOPUNKS).transferPunk(map(recipient), punkId); - else output = abi.encodePacked(BuyPunkFailed.selector); - } else if (command == Commands.OWNER_CHECK_721) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address owner; - address token; - uint256 id; - assembly { - owner := calldataload(inputs.offset) - token := calldataload(add(inputs.offset, 0x20)) - id := calldataload(add(inputs.offset, 0x40)) - } - success = (ERC721(token).ownerOf(id) == owner); - if (!success) output = abi.encodePacked(InvalidOwnerERC721.selector); - } else if (command == Commands.OWNER_CHECK_1155) { - // equivalent: abi.decode(inputs, (address, address, uint256, uint256)) - address owner; - address token; - uint256 id; - uint256 minBalance; - assembly { - owner := calldataload(inputs.offset) - token := calldataload(add(inputs.offset, 0x20)) - id := calldataload(add(inputs.offset, 0x40)) - minBalance := calldataload(add(inputs.offset, 0x60)) - } - success = (ERC1155(token).balanceOf(owner, id) >= minBalance); - if (!success) output = abi.encodePacked(InvalidOwnerERC1155.selector); - } else if (command == Commands.SWEEP_ERC721) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint256 id; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - id := calldataload(add(inputs.offset, 0x40)) - } - Payments.sweepERC721(token, map(recipient), id); + if (command == Commands.V2_SWAP_EXACT_IN) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountIn; + uint256 amountOutMin; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountIn := calldataload(add(inputs.offset, 0x20)) + amountOutMin := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) } - // 0x18 <= command < 0x1f - } else { - if (command == Commands.X2Y2_721) { - (success, output) = callAndTransfer721(inputs, X2Y2); - } else if (command == Commands.SUDOSWAP) { - // equivalent: abi.decode(inputs, (uint256, bytes)) - (uint256 value, bytes calldata data) = getValueAndData(inputs); - (success, output) = SUDOSWAP.call{value: value}(data); - } else if (command == Commands.NFT20) { - // equivalent: abi.decode(inputs, (uint256, bytes)) - (uint256 value, bytes calldata data) = getValueAndData(inputs); - (success, output) = NFT20_ZAP.call{value: value}(data); - } else if (command == Commands.X2Y2_1155) { - (success, output) = callAndTransfer1155(inputs, X2Y2); - } else if (command == Commands.FOUNDATION) { - (success, output) = callAndTransfer721(inputs, FOUNDATION); - } else if (command == Commands.SWEEP_ERC1155) { - // equivalent: abi.decode(inputs, (address, address, uint256, uint256)) - address token; - address recipient; - uint256 id; - uint256 amount; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - id := calldataload(add(inputs.offset, 0x40)) - amount := calldataload(add(inputs.offset, 0x60)) - } - Payments.sweepERC1155(token, map(recipient), id, amount); - } else if (command == Commands.ELEMENT_MARKET) { - // equivalent: abi.decode(inputs, (uint256, bytes)) - (uint256 value, bytes calldata data) = getValueAndData(inputs); - (success, output) = ELEMENT_MARKET.call{value: value}(data); - } else { - // placeholder for command 0x1f - revert InvalidCommandType(command); + address[] calldata path = inputs.toAddressArray(3); + address payer = payerIsUser ? lockedBy : address(this); + v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); + } else if (command == Commands.V2_SWAP_EXACT_OUT) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountOut; + uint256 amountInMax; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountOut := calldataload(add(inputs.offset, 0x20)) + amountInMax := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) } + address[] calldata path = inputs.toAddressArray(3); + address payer = payerIsUser ? lockedBy : address(this); + v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); + } else if (command == Commands.PERMIT2_PERMIT) { + // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes)) + IAllowanceTransfer.PermitSingle calldata permitSingle; + assembly { + permitSingle := inputs.offset + } + bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) + PERMIT2.permit(lockedBy, permitSingle, data); + } else if (command == Commands.WRAP_ETH) { + // equivalent: abi.decode(inputs, (address, uint256)) + address recipient; + uint256 amountMin; + assembly { + recipient := calldataload(inputs.offset) + amountMin := calldataload(add(inputs.offset, 0x20)) + } + Payments.wrapETH(map(recipient), amountMin); + } else if (command == Commands.UNWRAP_WETH) { + // equivalent: abi.decode(inputs, (address, uint256)) + address recipient; + uint256 amountMin; + assembly { + recipient := calldataload(inputs.offset) + amountMin := calldataload(add(inputs.offset, 0x20)) + } + Payments.unwrapWETH9(map(recipient), amountMin); + } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { + (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = + abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); + permit2TransferFrom(batchDetails, lockedBy); + } else if (command == Commands.BALANCE_CHECK_ERC20) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address owner; + address token; + uint256 minBalance; + assembly { + owner := calldataload(inputs.offset) + token := calldataload(add(inputs.offset, 0x20)) + minBalance := calldataload(add(inputs.offset, 0x40)) + } + success = (ERC20(token).balanceOf(owner) >= minBalance); + if (!success) output = abi.encodePacked(BalanceTooLow.selector); + } else { + // placeholder area for command 0x0f + revert InvalidCommandType(command); } } - // 0x20 <= command } else { - if (command == Commands.SEAPORT_V1_4) { - /// @dev Seaport 1.4 and 1.5 allow for orders to be created by contracts. - /// These orders pass control to the contract offerers during fufillment, - /// allowing them to perform any number of destructive actions as a holder of the NFT. - /// Integrators should be aware that in some scenarios: e.g. purchasing an NFT that allows the holder - /// to claim another NFT, the contract offerer can "steal" the claim during order fufillment. - /// For some such purchases, an OWNER_CHECK command can be prepended to ensure that all tokens have the desired owner at the end of the transaction. - /// This is also outlined in the Seaport documentation: https://github.com/ProjectOpenSea/seaport/blob/main/docs/SeaportDocumentation.md - (uint256 value, bytes calldata data) = getValueAndData(inputs); - (success, output) = SEAPORT_V1_4.call{value: value}(data); - } else if (command == Commands.EXECUTE_SUB_PLAN) { + if (command == Commands.EXECUTE_SUB_PLAN) { bytes calldata _commands = inputs.toBytes(0); bytes[] calldata _inputs = inputs.toBytesArray(1); (success, output) = (address(this)).call(abi.encodeWithSelector(Dispatcher.execute.selector, _commands, _inputs)); - } else if (command == Commands.APPROVE_ERC20) { - ERC20 token; - PaymentsImmutables.Spenders spender; - assembly { - token := calldataload(inputs.offset) - spender := calldataload(add(inputs.offset, 0x20)) - } - Payments.approveERC20(token, spender); } else { // placeholder area for commands 0x23-0x3f revert InvalidCommandType(command); @@ -362,66 +218,4 @@ abstract contract Dispatcher is NFTImmutables, Payments, V2SwapRouter, V3SwapRou /// @param commands A set of concatenated commands, each 1 byte in length /// @param inputs An array of byte strings containing abi encoded inputs for each command function execute(bytes calldata commands, bytes[] calldata inputs) external payable virtual; - - /// @notice Performs a call to purchase an ERC721, then transfers the ERC721 to a specified recipient - /// @param inputs The inputs for the protocol and ERC721 transfer, encoded - /// @param protocol The protocol to pass the calldata to - /// @return success True on success of the command, false on failure - /// @return output The outputs or error messages, if any, from the command - function callAndTransfer721(bytes calldata inputs, address protocol) - internal - returns (bool success, bytes memory output) - { - // equivalent: abi.decode(inputs, (uint256, bytes, address, address, uint256)) - (uint256 value, bytes calldata data) = getValueAndData(inputs); - address recipient; - address token; - uint256 id; - assembly { - // 0x00 and 0x20 offsets are value and data, above - recipient := calldataload(add(inputs.offset, 0x40)) - token := calldataload(add(inputs.offset, 0x60)) - id := calldataload(add(inputs.offset, 0x80)) - } - (success, output) = protocol.call{value: value}(data); - if (success) ERC721(token).safeTransferFrom(address(this), map(recipient), id); - } - - /// @notice Performs a call to purchase an ERC1155, then transfers the ERC1155 to a specified recipient - /// @param inputs The inputs for the protocol and ERC1155 transfer, encoded - /// @param protocol The protocol to pass the calldata to - /// @return success True on success of the command, false on failure - /// @return output The outputs or error messages, if any, from the command - function callAndTransfer1155(bytes calldata inputs, address protocol) - internal - returns (bool success, bytes memory output) - { - // equivalent: abi.decode(inputs, (uint256, bytes, address, address, uint256, uint256)) - (uint256 value, bytes calldata data) = getValueAndData(inputs); - address recipient; - address token; - uint256 id; - uint256 amount; - assembly { - // 0x00 and 0x20 offsets are value and data, above - recipient := calldataload(add(inputs.offset, 0x40)) - token := calldataload(add(inputs.offset, 0x60)) - id := calldataload(add(inputs.offset, 0x80)) - amount := calldataload(add(inputs.offset, 0xa0)) - } - (success, output) = protocol.call{value: value}(data); - if (success) ERC1155(token).safeTransferFrom(address(this), map(recipient), id, amount, new bytes(0)); - } - - /// @notice Helper function to extract `value` and `data` parameters from input bytes string - /// @dev The helper assumes that `value` is the first parameter, and `data` is the second - /// @param inputs The bytes string beginning with value and data parameters - /// @return value The 256 bit integer value - /// @return data The data bytes string - function getValueAndData(bytes calldata inputs) internal pure returns (uint256 value, bytes calldata data) { - assembly { - value := calldataload(inputs.offset) - } - data = inputs.toBytes(1); - } } diff --git a/contracts/base/LockAndMsgSender.sol b/contracts/base/LockAndMsgSender.sol index 60e8bb00..472c4fe3 100644 --- a/contracts/base/LockAndMsgSender.sol +++ b/contracts/base/LockAndMsgSender.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {Constants} from '../libraries/Constants.sol'; diff --git a/contracts/base/RewardsCollector.sol b/contracts/base/RewardsCollector.sol deleted file mode 100644 index ded36577..00000000 --- a/contracts/base/RewardsCollector.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.15; - -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; -import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol'; -import {NFTImmutables} from '../modules/NFTImmutables.sol'; -import {IRewardsCollector} from '../interfaces/IRewardsCollector.sol'; - -abstract contract RewardsCollector is IRewardsCollector, NFTImmutables { - using SafeTransferLib for ERC20; - - event RewardsSent(uint256 amount); - - error UnableToClaim(); - - /// @inheritdoc IRewardsCollector - function collectRewards(bytes calldata looksRareClaim) external { - (bool success,) = LOOKS_RARE_REWARDS_DISTRIBUTOR.call(looksRareClaim); - if (!success) revert UnableToClaim(); - - uint256 balance = LOOKS_RARE_TOKEN.balanceOf(address(this)); - LOOKS_RARE_TOKEN.transfer(ROUTER_REWARDS_DISTRIBUTOR, balance); - emit RewardsSent(balance); - } -} diff --git a/contracts/base/RouterImmutables.sol b/contracts/base/RouterImmutables.sol index 391ce6a6..3a13b107 100644 --- a/contracts/base/RouterImmutables.sol +++ b/contracts/base/RouterImmutables.sol @@ -1,23 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; struct RouterParameters { address permit2; address weth9; - address seaportV1_5; - address seaportV1_4; - address openseaConduit; - address nftxZap; - address x2y2; - address foundation; - address sudoswap; - address elementMarket; - address nft20Zap; - address cryptopunks; - address looksRareV2; - address routerRewardsDistributor; - address looksRareRewardsDistributor; - address looksRareToken; address v2Factory; address v3Factory; bytes32 pairInitCodeHash; diff --git a/contracts/deploy/UnsupportedProtocol.sol b/contracts/deploy/UnsupportedProtocol.sol index f55e9a83..562641be 100644 --- a/contracts/deploy/UnsupportedProtocol.sol +++ b/contracts/deploy/UnsupportedProtocol.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; /// @title Dummy contract that always reverts /// @notice Used as a placeholder to ensure reverts on attempted calls to protocols unsupported on a given chain diff --git a/contracts/interfaces/IRewardsCollector.sol b/contracts/interfaces/IRewardsCollector.sol deleted file mode 100644 index e7bd52b6..00000000 --- a/contracts/interfaces/IRewardsCollector.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.15; - -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; - -/// @title LooksRare Rewards Collector -/// @notice Implements a permissionless call to fetch LooksRare rewards earned by Universal Router users -/// and transfers them to an external rewards distributor contract -interface IRewardsCollector { - /// @notice Fetches users' LooksRare rewards and sends them to the distributor contract - /// @param looksRareClaim The data required by LooksRare to claim reward tokens - function collectRewards(bytes calldata looksRareClaim) external; -} diff --git a/contracts/interfaces/IUniversalRouter.sol b/contracts/interfaces/IUniversalRouter.sol index df9fed38..f05a7f08 100644 --- a/contracts/interfaces/IUniversalRouter.sol +++ b/contracts/interfaces/IUniversalRouter.sol @@ -1,11 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; -import {IERC721Receiver} from '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; -import {IERC1155Receiver} from '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol'; -import {IRewardsCollector} from './IRewardsCollector.sol'; - -interface IUniversalRouter is IRewardsCollector, IERC721Receiver, IERC1155Receiver { +interface IUniversalRouter { /// @notice Thrown when a required command has failed error ExecutionFailed(uint256 commandIndex, bytes message); @@ -18,6 +14,9 @@ interface IUniversalRouter is IRewardsCollector, IERC721Receiver, IERC1155Receiv /// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided error LengthMismatch(); + // @notice Thrown when an address that isnt WETH tries to send ETH to the router without calldata + error InvalidEthSender(); + /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired. /// @param commands A set of concatenated commands, each 1 byte in length /// @param inputs An array of byte strings containing abi encoded inputs for each command diff --git a/contracts/interfaces/external/ICryptoPunksMarket.sol b/contracts/interfaces/external/ICryptoPunksMarket.sol deleted file mode 100644 index 24e39709..00000000 --- a/contracts/interfaces/external/ICryptoPunksMarket.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.4; - -/// @title Interface for CryptoPunksMarket -interface ICryptoPunksMarket { - /// @notice Buy a cryptopunk - function buyPunk(uint256 punkIndex) external payable; - - /// @notice Transfer a cryptopunk to another address - function transferPunk(address to, uint256 punkIndex) external; -} diff --git a/contracts/libraries/Commands.sol b/contracts/libraries/Commands.sol index c3bba22a..0ba72d82 100644 --- a/contracts/libraries/Commands.sol +++ b/contracts/libraries/Commands.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; /// @title Commands /// @notice Command Flags used to decode commands @@ -38,37 +38,15 @@ library Commands { // The following constant defines one of the boundaries where the if blocks split commands uint256 constant SECOND_IF_BOUNDARY = 0x10; - // Command Types where 0x10<=value<0x18, executed in the third nested-if block - uint256 constant SEAPORT_V1_5 = 0x10; - uint256 constant LOOKS_RARE_V2 = 0x11; - uint256 constant NFTX = 0x12; - uint256 constant CRYPTOPUNKS = 0x13; - // 0x14; - uint256 constant OWNER_CHECK_721 = 0x15; - uint256 constant OWNER_CHECK_1155 = 0x16; - uint256 constant SWEEP_ERC721 = 0x17; - // The commands are executed in nested if blocks to minimise gas consumption // The following constant defines one of the boundaries where the if blocks split commands uint256 constant THIRD_IF_BOUNDARY = 0x18; - // Command Types where 0x18<=value<=0x1f, executed in the final nested-if block - uint256 constant X2Y2_721 = 0x18; - uint256 constant SUDOSWAP = 0x19; - uint256 constant NFT20 = 0x1a; - uint256 constant X2Y2_1155 = 0x1b; - uint256 constant FOUNDATION = 0x1c; - uint256 constant SWEEP_ERC1155 = 0x1d; - uint256 constant ELEMENT_MARKET = 0x1e; - // COMMAND_PLACEHOLDER = 0x1f; - // The commands are executed in nested if blocks to minimise gas consumption // The following constant defines one of the boundaries where the if blocks split commands uint256 constant FOURTH_IF_BOUNDARY = 0x20; // Command Types where 0x20<=value - uint256 constant SEAPORT_V1_4 = 0x20; uint256 constant EXECUTE_SUB_PLAN = 0x21; - uint256 constant APPROVE_ERC20 = 0x22; // COMMAND_PLACEHOLDER for 0x23 to 0x3f (all unused) } diff --git a/contracts/libraries/Constants.sol b/contracts/libraries/Constants.sol index c264376d..ed14f109 100644 --- a/contracts/libraries/Constants.sol +++ b/contracts/libraries/Constants.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {IWETH9} from '../interfaces/external/IWETH9.sol'; diff --git a/contracts/modules/NFTImmutables.sol b/contracts/modules/NFTImmutables.sol deleted file mode 100644 index c44613ed..00000000 --- a/contracts/modules/NFTImmutables.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; - -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; - -struct NFTParameters { - address seaportV1_5; - address seaportV1_4; - address nftxZap; - address x2y2; - address foundation; - address sudoswap; - address elementMarket; - address nft20Zap; - address cryptopunks; - address looksRareV2; - address routerRewardsDistributor; - address looksRareRewardsDistributor; - address looksRareToken; -} - -contract NFTImmutables { - /// @dev Seaport 1.5 address - address internal immutable SEAPORT_V1_5; - - /// @dev Seaport 1.4 address - address internal immutable SEAPORT_V1_4; - - /// @dev The address of NFTX zap contract for interfacing with vaults - address internal immutable NFTX_ZAP; - - /// @dev The address of X2Y2 - address internal immutable X2Y2; - - // @dev The address of Foundation - address internal immutable FOUNDATION; - - // @dev The address of Element Market - address internal immutable ELEMENT_MARKET; - - // @dev the address of NFT20's zap contract - address internal immutable NFT20_ZAP; - - // @dev the address of Larva Lab's cryptopunks marketplace - address internal immutable CRYPTOPUNKS; - - /// @dev The address of LooksRareV2 - address internal immutable LOOKS_RARE_V2; - - /// @dev The address of LooksRare token - ERC20 internal immutable LOOKS_RARE_TOKEN; - - /// @dev The address of LooksRare rewards distributor - address internal immutable LOOKS_RARE_REWARDS_DISTRIBUTOR; - - /// @dev The address of router rewards distributor - address internal immutable ROUTER_REWARDS_DISTRIBUTOR; - - constructor(NFTParameters memory params) { - SEAPORT_V1_5 = params.seaportV1_5; - SEAPORT_V1_4 = params.seaportV1_4; - NFTX_ZAP = params.nftxZap; - X2Y2 = params.x2y2; - FOUNDATION = params.foundation; - ELEMENT_MARKET = params.elementMarket; - NFT20_ZAP = params.nft20Zap; - CRYPTOPUNKS = params.cryptopunks; - LOOKS_RARE_V2 = params.looksRareV2; - LOOKS_RARE_TOKEN = ERC20(params.looksRareToken); - LOOKS_RARE_REWARDS_DISTRIBUTOR = params.looksRareRewardsDistributor; - ROUTER_REWARDS_DISTRIBUTOR = params.routerRewardsDistributor; - } -} diff --git a/contracts/modules/Payments.sol b/contracts/modules/Payments.sol index a752582a..2c4fb485 100644 --- a/contracts/modules/Payments.sol +++ b/contracts/modules/Payments.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {Constants} from '../libraries/Constants.sol'; import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol'; import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; -import {ERC721} from 'solmate/src/tokens/ERC721.sol'; -import {ERC1155} from 'solmate/src/tokens/ERC1155.sol'; /// @title Payments contract /// @notice Performs various operations around the payment of ETH and tokens @@ -37,21 +35,6 @@ abstract contract Payments is PaymentsImmutables { } } - /// @notice Approves a protocol to spend ERC20s in the router - /// @param token The token to approve - /// @param spender Which protocol to approve - function approveERC20(ERC20 token, Spenders spender) internal { - // check spender is one of our approved spenders - address spenderAddress; - /// @dev use 0 = Opensea Conduit for both Seaport v1.4 and v1.5 - if (spender == Spenders.OSConduit) spenderAddress = OPENSEA_CONDUIT; - else if (spender == Spenders.Sudoswap) spenderAddress = SUDOSWAP; - else revert InvalidSpender(); - - // set approval - token.safeApprove(spenderAddress, type(uint256).max); - } - /// @notice Pays a proportion of the contract's ETH or ERC20 to a recipient /// @param token The token to pay (can be ETH using Constants.ETH) /// @param recipient The address that will receive payment @@ -86,25 +69,6 @@ abstract contract Payments is PaymentsImmutables { } } - /// @notice Sweeps an ERC721 to a recipient from the contract - /// @param token The ERC721 token to sweep - /// @param recipient The address that will receive payment - /// @param id The ID of the ERC721 to sweep - function sweepERC721(address token, address recipient, uint256 id) internal { - ERC721(token).safeTransferFrom(address(this), recipient, id); - } - - /// @notice Sweeps all of the contract's ERC1155 to an address - /// @param token The ERC1155 token to sweep - /// @param recipient The address that will receive payment - /// @param id The ID of the ERC1155 to sweep - /// @param amountMinimum The minimum desired amount - function sweepERC1155(address token, address recipient, uint256 id, uint256 amountMinimum) internal { - uint256 balance = ERC1155(token).balanceOf(address(this), id); - if (balance < amountMinimum) revert InsufficientToken(); - ERC1155(token).safeTransferFrom(address(this), recipient, id, balance, bytes('')); - } - /// @notice Wraps an amount of ETH into WETH /// @param recipient The recipient of the WETH /// @param amount The amount to wrap (can be CONTRACT_BALANCE) diff --git a/contracts/modules/PaymentsImmutables.sol b/contracts/modules/PaymentsImmutables.sol index 713cb59c..0e00c30d 100644 --- a/contracts/modules/PaymentsImmutables.sol +++ b/contracts/modules/PaymentsImmutables.sol @@ -1,14 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {IWETH9} from '../interfaces/external/IWETH9.sol'; -import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; +import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol'; struct PaymentsParameters { address permit2; address weth9; - address openseaConduit; - address sudoswap; } contract PaymentsImmutables { @@ -16,13 +14,7 @@ contract PaymentsImmutables { IWETH9 internal immutable WETH9; /// @dev Permit2 address - IAllowanceTransfer internal immutable PERMIT2; - - /// @dev The address of OpenSea's conduit used in both Seaport 1.4 and Seaport 1.5 - address internal immutable OPENSEA_CONDUIT; - - // @dev The address of Sudoswap's router - address internal immutable SUDOSWAP; + IPermit2 internal immutable PERMIT2; enum Spenders { OSConduit, @@ -31,8 +23,6 @@ contract PaymentsImmutables { constructor(PaymentsParameters memory params) { WETH9 = IWETH9(params.weth9); - PERMIT2 = IAllowanceTransfer(params.permit2); - OPENSEA_CONDUIT = params.openseaConduit; - SUDOSWAP = params.sudoswap; + PERMIT2 = IPermit2(params.permit2); } } diff --git a/contracts/modules/Permit2Payments.sol b/contracts/modules/Permit2Payments.sol index bcfca986..49f70b01 100644 --- a/contracts/modules/Permit2Payments.sol +++ b/contracts/modules/Permit2Payments.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; import {SafeCast160} from 'permit2/src/libraries/SafeCast160.sol'; diff --git a/contracts/modules/uniswap/UniswapImmutables.sol b/contracts/modules/uniswap/UniswapImmutables.sol index 1cdcd037..ea5630c1 100644 --- a/contracts/modules/uniswap/UniswapImmutables.sol +++ b/contracts/modules/uniswap/UniswapImmutables.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; struct UniswapParameters { address v2Factory; diff --git a/contracts/modules/uniswap/v2/V2SwapRouter.sol b/contracts/modules/uniswap/v2/V2SwapRouter.sol index ee2aff42..61a1128f 100644 --- a/contracts/modules/uniswap/v2/V2SwapRouter.sol +++ b/contracts/modules/uniswap/v2/V2SwapRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; import {UniswapV2Library} from './UniswapV2Library.sol'; diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 7708f3ab..0384a1db 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {V3Path} from './V3Path.sol'; import {BytesLib} from './BytesLib.sol'; diff --git a/contracts/test/ExampleModule.sol b/contracts/test/ExampleModule.sol index 2542ba5a..0d7b78ab 100644 --- a/contracts/test/ExampleModule.sol +++ b/contracts/test/ExampleModule.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; contract ExampleModule { event ExampleModuleEvent(string message); diff --git a/contracts/test/ImportsForTypechain.sol b/contracts/test/ImportsForTypechain.sol deleted file mode 100644 index fc2049e6..00000000 --- a/contracts/test/ImportsForTypechain.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; - -import {ERC1155} from 'solmate/src/tokens/ERC1155.sol'; -import {Permit2} from 'permit2/src/Permit2.sol'; - -// this contract only exists to pull ERC1155 and Permit2 into the hardhat build pipeline -// so that typechain artifacts are generated for it -abstract contract ImportsForTypechain is ERC1155, Permit2 {} diff --git a/contracts/test/MockLooksRareRewardsDistributor.sol b/contracts/test/MockLooksRareRewardsDistributor.sol deleted file mode 100644 index 6313d579..00000000 --- a/contracts/test/MockLooksRareRewardsDistributor.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.15; - -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; - -contract MockLooksRareRewardsDistributor { - address public immutable routerRewardsDistributor; - ERC20 immutable looksRareToken; - - constructor(address _routerRewardsDistributor, address _looksRareToken) { - routerRewardsDistributor = _routerRewardsDistributor; - looksRareToken = ERC20(_looksRareToken); - } - - fallback() external { - looksRareToken.transfer(routerRewardsDistributor, looksRareToken.balanceOf(address(this))); - } -} diff --git a/contracts/test/ReenteringProtocol.sol b/contracts/test/ReenteringProtocol.sol deleted file mode 100644 index 97772e7f..00000000 --- a/contracts/test/ReenteringProtocol.sol +++ /dev/null @@ -1,11 +0,0 @@ -// 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(); - } -} diff --git a/contracts/test/ReenteringWETH.sol b/contracts/test/ReenteringWETH.sol new file mode 100644 index 00000000..699f77a4 --- /dev/null +++ b/contracts/test/ReenteringWETH.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.15; + +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; + +contract ReenteringWETH is ERC20 { + error NotAllowedReenter(); + + address universalRouter; + bytes data; + + constructor() ERC20('ReenteringWETH', 'RW', 18) {} + + function setParameters(address _universalRouter, bytes memory _data) external { + universalRouter = _universalRouter; + data = _data; + } + + function deposit() public payable { + (bool success,) = universalRouter.call(data); + if (!success) revert NotAllowedReenter(); + } +} diff --git a/contracts/test/TestCustomErrors.sol b/contracts/test/TestCustomErrors.sol index f8e63647..b901cca0 100644 --- a/contracts/test/TestCustomErrors.sol +++ b/contracts/test/TestCustomErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; contract TestCustomErrors { // adding so that hardhat knows this custom signature selector for external contracts diff --git a/foundry.toml b/foundry.toml index 6d524f16..57d8d57c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,10 +3,17 @@ src = 'contracts' out = 'out' libs = ['lib'] via_ir = true -solc_version = '0.8.17' +solc_version = '0.8.26' +evm_version = "cancun" + optimizer_runs = 1_000_000 fs_permissions = [{ access = "read", path = "./script/deployParameters/"}] +remappings = [ + "solmate/=lib/solmate/", + "permit2/=lib/permit2/", +] + [fmt] line_length = 120 quote_style = 'single' diff --git a/hardhat.config.ts b/hardhat.config.ts index c9bb79dd..a43562b4 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,14 +1,15 @@ import 'hardhat-typechain' import '@nomiclabs/hardhat-ethers' import '@nomicfoundation/hardhat-chai-matchers' +import '@nomicfoundation/hardhat-foundry' import dotenv from 'dotenv' dotenv.config() const DEFAULT_COMPILER_SETTINGS = { - version: '0.8.17', + version: '0.8.26', settings: { viaIR: true, - evmVersion: 'istanbul', + evmVersion: 'cancun', optimizer: { enabled: true, runs: 1_000_000, @@ -29,7 +30,7 @@ export default { chainId: 1, forking: { url: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, - blockNumber: 15360000, + blockNumber: 20010000, }, }, mainnet: { diff --git a/lib/permit2 b/lib/permit2 index a7cd1869..cc56ad0f 160000 --- a/lib/permit2 +++ b/lib/permit2 @@ -1 +1 @@ -Subproject commit a7cd186948b44f9096a35035226d7d70b9e24eaf +Subproject commit cc56ad0f3439c502c246fc5cfcc3db92bb8b7219 diff --git a/package.json b/package.json index 8661b8b2..75eae10f 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "devDependencies": { "@nomicfoundation/hardhat-chai-matchers": "1.0.4", "@nomiclabs/hardhat-ethers": "^2.2.2", + "@nomicfoundation/hardhat-foundry": "1.1.2", "@typechain/ethers-v5": "^4.0.0", "@types/chai": "^4.2.6", "@types/mocha": "^5.2.7", @@ -49,7 +50,7 @@ "bignumber.js": "^9.0.0", "chai": "^4.3.4", "ethers": "^5.7.2", - "hardhat": "2.12.2", + "hardhat": "2.22.5", "hardhat-typechain": "^0.3.5", "mocha-chai-jest-snapshot": "^1.1.0", "prettier": "^2.0.5", @@ -64,8 +65,6 @@ "test": "hardhat test", "test:gas": "UPDATE_SNAPSHOT=1 yarn test --grep gas", "test:all": "UPDATE_SNAPSHOT=1 yarn test", - "presymlink": "rm -rf ./solmate && rm -rf ./permit2", - "symlink": "ln -s ./lib/solmate ./solmate && ln -s ./lib/permit2 ./permit2", "prettier:fix": "prettier --write '**/*.ts' && prettier --write '**/*.json'", "lint:fix": "yarn prettier:fix && forge fmt", "prettier": "prettier --check '**/*.ts' && forge fmt --check" diff --git a/permit2 b/permit2 deleted file mode 120000 index 1b21770e..00000000 --- a/permit2 +++ /dev/null @@ -1 +0,0 @@ -./lib/permit2 \ No newline at end of file diff --git a/script/DeployUniversalRouter.s.sol b/script/DeployUniversalRouter.s.sol index bc17cb1a..fea277d7 100644 --- a/script/DeployUniversalRouter.s.sol +++ b/script/DeployUniversalRouter.s.sol @@ -6,7 +6,6 @@ import 'forge-std/Script.sol'; import {RouterParameters} from 'contracts/base/RouterImmutables.sol'; import {UnsupportedProtocol} from 'contracts/deploy/UnsupportedProtocol.sol'; import {UniversalRouter} from 'contracts/UniversalRouter.sol'; -import {Permit2} from 'permit2/src/Permit2.sol'; bytes32 constant SALT = bytes32(uint256(0x00000000000000000000000000000000000000005eb67581652632000a6cbedf)); @@ -17,6 +16,8 @@ abstract contract DeployUniversalRouter is Script { address constant UNSUPPORTED_PROTOCOL = address(0); bytes32 constant BYTES32_ZERO = bytes32(0); + error Permit2NotDeployed(); + // set values for params and unsupported function setUp() public virtual; @@ -24,11 +25,7 @@ abstract contract DeployUniversalRouter is Script { vm.startBroadcast(); // deploy permit2 if it isnt yet deployed - if (params.permit2 == address(0)) { - address permit2 = address(new Permit2{salt: SALT}()); - params.permit2 = permit2; - console2.log('Permit2 Deployed:', address(permit2)); - } + if (params.permit2 == address(0)) revert Permit2NotDeployed(); // only deploy unsupported if this chain doesn't already have one if (unsupported == address(0)) { @@ -39,20 +36,6 @@ abstract contract DeployUniversalRouter is Script { params = RouterParameters({ permit2: mapUnsupported(params.permit2), weth9: mapUnsupported(params.weth9), - seaportV1_5: mapUnsupported(params.seaportV1_5), - seaportV1_4: mapUnsupported(params.seaportV1_4), - openseaConduit: mapUnsupported(params.openseaConduit), - nftxZap: mapUnsupported(params.nftxZap), - x2y2: mapUnsupported(params.x2y2), - foundation: mapUnsupported(params.foundation), - sudoswap: mapUnsupported(params.sudoswap), - elementMarket: mapUnsupported(params.elementMarket), - nft20Zap: mapUnsupported(params.nft20Zap), - cryptopunks: mapUnsupported(params.cryptopunks), - looksRareV2: mapUnsupported(params.looksRareV2), - routerRewardsDistributor: mapUnsupported(params.routerRewardsDistributor), - looksRareRewardsDistributor: mapUnsupported(params.looksRareRewardsDistributor), - looksRareToken: mapUnsupported(params.looksRareToken), v2Factory: mapUnsupported(params.v2Factory), v3Factory: mapUnsupported(params.v3Factory), pairInitCodeHash: params.pairInitCodeHash, @@ -69,20 +52,6 @@ abstract contract DeployUniversalRouter is Script { function logParams() internal view { console2.log('permit2:', params.permit2); console2.log('weth9:', params.weth9); - console2.log('seaportV1_5:', params.seaportV1_5); - console2.log('seaportV1_4:', params.seaportV1_4); - console2.log('openseaConduit:', params.openseaConduit); - console2.log('nftxZap:', params.nftxZap); - console2.log('x2y2:', params.x2y2); - console2.log('foundation:', params.foundation); - console2.log('sudoswap:', params.sudoswap); - console2.log('elementMarket:', params.elementMarket); - console2.log('nft20Zap:', params.nft20Zap); - console2.log('cryptopunks:', params.cryptopunks); - console2.log('looksRareV2:', params.looksRareV2); - console2.log('routerRewardsDistributor:', params.routerRewardsDistributor); - console2.log('looksRareRewardsDistributor:', params.looksRareRewardsDistributor); - console2.log('looksRareToken:', params.looksRareToken); console2.log('v2Factory:', params.v2Factory); console2.log('v3Factory:', params.v3Factory); } diff --git a/script/deployParameters/DeployArbitrum.s.sol b/script/deployParameters/DeployArbitrum.s.sol index ca1a46aa..873e7a2d 100644 --- a/script/deployParameters/DeployArbitrum.s.sol +++ b/script/deployParameters/DeployArbitrum.s.sol @@ -9,20 +9,6 @@ contract DeployArbitrum is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: 0x3BD7512966CbC3406962f8877edbE80aea8A2904, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0xf1D7CC64Fb4452F05c498126312eBE29f30Fbcf9, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployArbitrumGoerli.s.sol b/script/deployParameters/DeployArbitrumGoerli.s.sol index a5089471..d9da02d9 100644 --- a/script/deployParameters/DeployArbitrumGoerli.s.sol +++ b/script/deployParameters/DeployArbitrumGoerli.s.sol @@ -9,20 +9,6 @@ contract DeployArbitrumGoerli is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3, - seaportV1_5: UNSUPPORTED_PROTOCOL, - seaportV1_4: UNSUPPORTED_PROTOCOL, - openseaConduit: UNSUPPORTED_PROTOCOL, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0x4893376342d5D7b3e31d4184c08b265e5aB2A3f6, pairInitCodeHash: BYTES32_ZERO, diff --git a/script/deployParameters/DeployAvalanche.s.sol b/script/deployParameters/DeployAvalanche.s.sol index a7d8fb0c..69585595 100644 --- a/script/deployParameters/DeployAvalanche.s.sol +++ b/script/deployParameters/DeployAvalanche.s.sol @@ -9,20 +9,6 @@ contract DeployAvalanche is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x9e5A52f57b3038F1B8EeE45F28b3C1967e22799C, v3Factory: 0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployBSC.s.sol b/script/deployParameters/DeployBSC.s.sol index e9ab85db..3b7dba3a 100644 --- a/script/deployParameters/DeployBSC.s.sol +++ b/script/deployParameters/DeployBSC.s.sol @@ -9,20 +9,6 @@ contract DeployBSC is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6, v3Factory: 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployBase.s.sol b/script/deployParameters/DeployBase.s.sol index 1706f224..43ddc254 100644 --- a/script/deployParameters/DeployBase.s.sol +++ b/script/deployParameters/DeployBase.s.sol @@ -9,20 +9,6 @@ contract DeployBase is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x4200000000000000000000000000000000000006, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6, v3Factory: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployBaseGoerli.s.sol b/script/deployParameters/DeployBaseGoerli.s.sol index 267bab4e..2713613e 100644 --- a/script/deployParameters/DeployBaseGoerli.s.sol +++ b/script/deployParameters/DeployBaseGoerli.s.sol @@ -9,20 +9,6 @@ contract DeployBaseGoerli is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x44D627f900da8AdaC7561bD73aA745F132450798, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0x9323c1d6D800ed51Bd7C6B216cfBec678B7d0BC2, pairInitCodeHash: BYTES32_ZERO, diff --git a/script/deployParameters/DeployBlast.s.sol b/script/deployParameters/DeployBlast.s.sol index 463e2fb6..fecc843b 100644 --- a/script/deployParameters/DeployBlast.s.sol +++ b/script/deployParameters/DeployBlast.s.sol @@ -9,20 +9,6 @@ contract DeployBlast is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x4300000000000000000000000000000000000004, - seaportV1_5: UNSUPPORTED_PROTOCOL, - seaportV1_4: UNSUPPORTED_PROTOCOL, - openseaConduit: UNSUPPORTED_PROTOCOL, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x5C346464d33F90bABaf70dB6388507CC889C1070, v3Factory: 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployCelo.s.sol b/script/deployParameters/DeployCelo.s.sol index 3242997b..fe0396ad 100644 --- a/script/deployParameters/DeployCelo.s.sol +++ b/script/deployParameters/DeployCelo.s.sol @@ -9,20 +9,6 @@ contract DeployCelo is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: UNSUPPORTED_PROTOCOL, - seaportV1_5: UNSUPPORTED_PROTOCOL, - seaportV1_4: UNSUPPORTED_PROTOCOL, - openseaConduit: UNSUPPORTED_PROTOCOL, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x79a530c8e2fA8748B7B40dd3629C0520c2cCf03f, v3Factory: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployCeloAlfajores.s.sol b/script/deployParameters/DeployCeloAlfajores.s.sol index 3ebf7d23..12696c88 100644 --- a/script/deployParameters/DeployCeloAlfajores.s.sol +++ b/script/deployParameters/DeployCeloAlfajores.s.sol @@ -9,20 +9,6 @@ contract DeployCeloAlfajores is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: UNSUPPORTED_PROTOCOL, - seaportV1_5: UNSUPPORTED_PROTOCOL, - seaportV1_4: UNSUPPORTED_PROTOCOL, - openseaConduit: UNSUPPORTED_PROTOCOL, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc, pairInitCodeHash: BYTES32_ZERO, diff --git a/script/deployParameters/DeployGoerli.s.sol b/script/deployParameters/DeployGoerli.s.sol index e848dbfe..b4bf6541 100644 --- a/script/deployParameters/DeployGoerli.s.sol +++ b/script/deployParameters/DeployGoerli.s.sol @@ -9,20 +9,6 @@ contract DeployGoerli is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: 0x177246Adb119ed83f982d1e3c4859F354578D5eF, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: 0x25b4EfC43c9dCAe134233CD577fFca7CfAd6748F, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: 0x35C2215F2FFe8917B06454eEEaba189877F200cf, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployMainnet.s.sol b/script/deployParameters/DeployMainnet.s.sol index 419b0e80..d706dd89 100644 --- a/script/deployParameters/DeployMainnet.s.sol +++ b/script/deployParameters/DeployMainnet.s.sol @@ -9,20 +9,6 @@ contract DeployMainnet is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: 0x941A6d105802CCCaa06DE58a13a6F49ebDCD481C, - x2y2: 0x74312363e45DCaBA76c59ec49a7Aa8A65a67EeD3, - foundation: 0xcDA72070E455bb31C7690a170224Ce43623d0B6f, - sudoswap: 0x2B2e8cDA09bBA9660dCA5cB6233787738Ad68329, - elementMarket: 0x20F780A973856B93f63670377900C1d2a50a77c4, - nft20Zap: 0xA42f6cADa809Bcf417DeefbdD69C5C5A909249C0, - cryptopunks: 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB, - looksRareV2: 0x0000000000E655fAe4d56241588680F86E3b2377, - routerRewardsDistributor: 0xea37093ce161f090e443f304e1bF3a8f14D7bb40, - looksRareRewardsDistributor: 0x0554f068365eD43dcC98dcd7Fd7A8208a5638C72, - looksRareToken: 0xf4d2888d29D722226FafA5d9B24F9164c092421E, v2Factory: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployOptimism.s.sol b/script/deployParameters/DeployOptimism.s.sol index c11b2d5f..1443d6de 100644 --- a/script/deployParameters/DeployOptimism.s.sol +++ b/script/deployParameters/DeployOptimism.s.sol @@ -9,20 +9,6 @@ contract DeployOptimism is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x4200000000000000000000000000000000000006, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x0c3c1c532F1e39EdF36BE9Fe0bE1410313E074Bf, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployOptimismGoerli.s.sol b/script/deployParameters/DeployOptimismGoerli.s.sol index 5a063f51..8f0d993b 100644 --- a/script/deployParameters/DeployOptimismGoerli.s.sol +++ b/script/deployParameters/DeployOptimismGoerli.s.sol @@ -9,20 +9,6 @@ contract DeployOptimismGoerli is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x4200000000000000000000000000000000000006, - seaportV1_5: UNSUPPORTED_PROTOCOL, - seaportV1_4: UNSUPPORTED_PROTOCOL, - openseaConduit: UNSUPPORTED_PROTOCOL, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0xB656dA17129e7EB733A557f4EBc57B76CFbB5d10, pairInitCodeHash: BYTES32_ZERO, diff --git a/script/deployParameters/DeployPolygon.s.sol b/script/deployParameters/DeployPolygon.s.sol index c34821d9..0189fc6c 100644 --- a/script/deployParameters/DeployPolygon.s.sol +++ b/script/deployParameters/DeployPolygon.s.sol @@ -9,20 +9,6 @@ contract DeployPolygon is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: 0xEAF5453b329Eb38Be159a872a6ce91c9A8fb0260, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0x9e5A52f57b3038F1B8EeE45F28b3C1967e22799C, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/script/deployParameters/DeployPolygonMumbai.s.sol b/script/deployParameters/DeployPolygonMumbai.s.sol index 52835b05..e090513e 100644 --- a/script/deployParameters/DeployPolygonMumbai.s.sol +++ b/script/deployParameters/DeployPolygonMumbai.s.sol @@ -9,20 +9,6 @@ contract DeployPolygonMumbai is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: BYTES32_ZERO, diff --git a/script/deployParameters/DeploySepolia.s.sol b/script/deployParameters/DeploySepolia.s.sol index b475c0ef..43804e74 100644 --- a/script/deployParameters/DeploySepolia.s.sol +++ b/script/deployParameters/DeploySepolia.s.sol @@ -9,20 +9,6 @@ contract DeploySepolia is DeployUniversalRouter { params = RouterParameters({ permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3, weth9: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14, - seaportV1_5: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, - seaportV1_4: 0x00000000000001ad428e4906aE43D8F9852d0dD6, - openseaConduit: 0x1E0049783F008A0085193E00003D00cd54003c71, - nftxZap: UNSUPPORTED_PROTOCOL, - x2y2: UNSUPPORTED_PROTOCOL, - foundation: UNSUPPORTED_PROTOCOL, - sudoswap: UNSUPPORTED_PROTOCOL, - elementMarket: UNSUPPORTED_PROTOCOL, - nft20Zap: UNSUPPORTED_PROTOCOL, - cryptopunks: UNSUPPORTED_PROTOCOL, - looksRareV2: UNSUPPORTED_PROTOCOL, - routerRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareRewardsDistributor: UNSUPPORTED_PROTOCOL, - looksRareToken: UNSUPPORTED_PROTOCOL, v2Factory: 0xB7f907f7A9eBC822a80BD25E224be42Ce0A698A0, v3Factory: 0x0227628f3F023bb0B980b67D528571c95c6DaC1c, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, diff --git a/solmate b/solmate deleted file mode 120000 index 4ff2d3ea..00000000 --- a/solmate +++ /dev/null @@ -1 +0,0 @@ -./lib/solmate \ No newline at end of file diff --git a/test/foundry-tests/UniswapV2.t.sol b/test/foundry-tests/UniswapV2.t.sol index c925ea85..43f5663c 100644 --- a/test/foundry-tests/UniswapV2.t.sol +++ b/test/foundry-tests/UniswapV2.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; -import {Permit2} from 'permit2/src/Permit2.sol'; +import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IUniswapV2Factory} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol'; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; @@ -12,41 +12,24 @@ import {Constants} from '../../contracts/libraries/Constants.sol'; import {Commands} from '../../contracts/libraries/Commands.sol'; import {RouterParameters} from '../../contracts/base/RouterImmutables.sol'; -import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; -import '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol'; - abstract contract UniswapV2Test is Test { address constant RECIPIENT = address(10); uint256 constant AMOUNT = 1 ether; uint256 constant BALANCE = 100000 ether; IUniswapV2Factory constant FACTORY = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); ERC20 constant WETH9 = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - Permit2 constant PERMIT2 = Permit2(0x000000000022D473030F116dDEE9F6B43aC78BA3); + IPermit2 constant PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3); address constant FROM = address(1234); UniversalRouter router; function setUp() public virtual { - vm.createSelectFork(vm.envString('FORK_URL'), 16000000); + vm.createSelectFork(vm.envString('FORK_URL'), 20010000); setUpTokens(); RouterParameters memory params = RouterParameters({ permit2: address(PERMIT2), weth9: address(WETH9), - seaportV1_5: address(0), - seaportV1_4: address(0), - openseaConduit: address(0), - nftxZap: address(0), - x2y2: address(0), - foundation: address(0), - sudoswap: address(0), - elementMarket: address(0), - nft20Zap: address(0), - cryptopunks: address(0), - looksRareV2: address(0), - routerRewardsDistributor: address(0), - looksRareRewardsDistributor: address(0), - looksRareToken: address(0), v2Factory: address(FACTORY), v3Factory: address(0), pairInitCodeHash: bytes32(0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f), diff --git a/test/foundry-tests/UniversalRouter.t.sol b/test/foundry-tests/UniversalRouter.t.sol index 4e0d7e99..e1d3b612 100644 --- a/test/foundry-tests/UniversalRouter.t.sol +++ b/test/foundry-tests/UniversalRouter.t.sol @@ -7,44 +7,26 @@ import {Payments} from '../../contracts/modules/Payments.sol'; import {Constants} from '../../contracts/libraries/Constants.sol'; import {Commands} from '../../contracts/libraries/Commands.sol'; import {MockERC20} from './mock/MockERC20.sol'; -import {MockERC1155} from './mock/MockERC1155.sol'; import {Callbacks} from '../../contracts/base/Callbacks.sol'; import {ExampleModule} from '../../contracts/test/ExampleModule.sol'; import {RouterParameters} from '../../contracts/base/RouterImmutables.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import 'permit2/src/interfaces/IAllowanceTransfer.sol'; - -import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; -import '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol'; +import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; contract UniversalRouterTest is Test { - address constant RECIPIENT = address(10); + address constant RECIPIENT = address(1234); uint256 constant AMOUNT = 10 ** 18; UniversalRouter router; ExampleModule testModule; MockERC20 erc20; - MockERC1155 erc1155; Callbacks callbacks; function setUp() public { RouterParameters memory params = RouterParameters({ permit2: address(0), weth9: address(0), - seaportV1_5: address(0), - seaportV1_4: address(0), - openseaConduit: address(0), - nftxZap: address(0), - x2y2: address(0), - foundation: address(0), - sudoswap: address(0), - elementMarket: address(0), - nft20Zap: address(0), - cryptopunks: address(0), - looksRareV2: address(0), - routerRewardsDistributor: address(0), - looksRareRewardsDistributor: address(0), - looksRareToken: address(0), v2Factory: address(0), v3Factory: address(0), pairInitCodeHash: bytes32(0), @@ -53,7 +35,6 @@ contract UniversalRouterTest is Test { router = new UniversalRouter(params); testModule = new ExampleModule(); erc20 = new MockERC20(); - erc1155 = new MockERC1155(); callbacks = new Callbacks(); } @@ -116,53 +97,9 @@ contract UniversalRouterTest is Test { router.execute(commands, inputs); } - function testSweepERC1155NotFullAmount() public { - bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.SWEEP_ERC1155))); - bytes[] memory inputs = new bytes[](1); - uint256 id = 0; - inputs[0] = abi.encode(address(erc1155), RECIPIENT, id, AMOUNT / 2); - - erc1155.mint(address(router), id, AMOUNT); - assertEq(erc1155.balanceOf(RECIPIENT, id), 0); - - router.execute(commands, inputs); - - assertEq(erc1155.balanceOf(RECIPIENT, id), AMOUNT); - } - - function testSweepERC1155() public { - bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.SWEEP_ERC1155))); - bytes[] memory inputs = new bytes[](1); - uint256 id = 0; - inputs[0] = abi.encode(address(erc1155), RECIPIENT, id, AMOUNT); - - erc1155.mint(address(router), id, AMOUNT); - assertEq(erc1155.balanceOf(RECIPIENT, id), 0); - - router.execute(commands, inputs); - - assertEq(erc1155.balanceOf(RECIPIENT, id), AMOUNT); - } - - function testSweepERC1155InsufficientOutput() public { - bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.SWEEP_ERC1155))); - bytes[] memory inputs = new bytes[](1); - uint256 id = 0; - inputs[0] = abi.encode(address(erc1155), RECIPIENT, id, AMOUNT + 1); - - erc1155.mint(address(router), id, AMOUNT); - - vm.expectRevert(Payments.InsufficientToken.selector); - router.execute(commands, inputs); - } - function testSupportsInterface() public { - bool supportsERC1155 = callbacks.supportsInterface(type(IERC1155Receiver).interfaceId); - bool supportsERC721 = callbacks.supportsInterface(type(IERC721Receiver).interfaceId); bool supportsERC165 = callbacks.supportsInterface(type(IERC165).interfaceId); - assertEq(supportsERC1155, true); - assertEq(supportsERC721, true); assertEq(supportsERC165, true); } } diff --git a/test/foundry-tests/mock/MockERC1155.sol b/test/foundry-tests/mock/MockERC1155.sol deleted file mode 100644 index 57b58d11..00000000 --- a/test/foundry-tests/mock/MockERC1155.sol +++ /dev/null @@ -1,13 +0,0 @@ -pragma solidity ^0.8.17; - -import {ERC1155} from 'solmate/src/tokens/ERC1155.sol'; - -contract MockERC1155 is ERC1155 { - function uri(uint256) public pure override returns (string memory) { - return ''; - } - - function mint(address to, uint256 id, uint256 amount) external { - _mint(to, id, amount, ''); - } -} diff --git a/test/foundry-tests/mock/MockERC20.sol b/test/foundry-tests/mock/MockERC20.sol index 79932912..a22847e6 100644 --- a/test/foundry-tests/mock/MockERC20.sol +++ b/test/foundry-tests/mock/MockERC20.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; +pragma solidity ^0.8.24; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; diff --git a/test/integration-tests/CheckOwnership.test.ts b/test/integration-tests/CheckOwnership.test.ts index c3171fee..2d8d3e4c 100644 --- a/test/integration-tests/CheckOwnership.test.ts +++ b/test/integration-tests/CheckOwnership.test.ts @@ -1,18 +1,11 @@ import { CommandType, RoutePlanner } from './shared/planner' import { expect } from './shared/expect' -import { ERC1155, ERC721, Permit2, UniversalRouter } from '../../typechain' -import { - seaportV1_4Orders, - seaportInterface, - getAdvancedOrderParams, - purchaseDataForTwoTownstarsSeaport, - AdvancedOrder, -} from './shared/protocolHelpers/seaport' -import { resetFork, COVEN_721, USDC, TOWNSTAR_1155 } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, COVEN_ADDRESS, DEADLINE, OPENSEA_CONDUIT_KEY, TOWNSTAR_ADDRESS } from './shared/constants' +import { UniversalRouter } from '../../typechain' +import { resetFork, USDC } from './shared/mainnetForkHelpers' +import { ALICE_ADDRESS, DEADLINE } from './shared/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import hre from 'hardhat' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' +import deployUniversalRouter from './shared/deployUniversalRouter' import { findCustomErrorSelector } from './shared/parseEvents' import { BigNumber, Contract } from 'ethers' import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' @@ -21,170 +14,7 @@ const { ethers } = hre describe('Check Ownership', () => { let alice: SignerWithAddress let router: UniversalRouter - let permit2: Permit2 let planner: RoutePlanner - let cryptoCovens: ERC721 - let townStarNFT: ERC1155 - - describe('checks ownership ERC721', () => { - beforeEach(async () => { - await resetFork(16784175) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - cryptoCovens = COVEN_721.connect(alice) as ERC721 - }) - - it('passes with valid owner', async () => { - const { advancedOrder } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - planner.addCommand(CommandType.OWNER_CHECK_721, [ - params.offerer, - COVEN_ADDRESS, - params.offer[0].identifierOrCriteria, - ]) - - const { commands, inputs } = planner - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)).to.not.be.reverted - }) - - it('reverts for invalid ownership', async () => { - const { advancedOrder } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - planner.addCommand(CommandType.OWNER_CHECK_721, [ - alice.address, - COVEN_ADDRESS, - params.offer[0].identifierOrCriteria, - ]) - - const { commands, inputs } = planner - - const customErrorSelector = findCustomErrorSelector(router.interface, 'InvalidOwnerERC721') - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) - .to.be.revertedWithCustomError(router, 'ExecutionFailed') - .withArgs(0, customErrorSelector) - }) - - it('checks ownership after a seaport trade for one ERC721', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [value.toString(), calldata]) - planner.addCommand(CommandType.OWNER_CHECK_721, [ - alice.address, - COVEN_ADDRESS, - params.offer[0].identifierOrCriteria, - ]) - - const { commands, inputs } = planner - - const ownerBefore = await cryptoCovens.ownerOf(params.offer[0].identifierOrCriteria) - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, value.mul(-1)) - const ownerAfter = await cryptoCovens.ownerOf(params.offer[0].identifierOrCriteria) - - expect(ownerBefore.toLowerCase()).to.eq(params.offerer.toLowerCase()) - expect(ownerAfter.toLowerCase()).to.eq(alice.address.toLowerCase()) - }) - }) - - describe('checks ownership ERC1155', () => { - let calldata: string - let advancedOrder0: AdvancedOrder - let advancedOrder1: AdvancedOrder - let value: BigNumber - - beforeEach(async () => { - await resetFork(17179617) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - townStarNFT = TOWNSTAR_1155.connect(alice) as ERC1155 - ;({ calldata, advancedOrder0, advancedOrder1, value } = purchaseDataForTwoTownstarsSeaport(alice.address)) - }) - - it('passes with valid ownership', async () => { - const params0 = advancedOrder0.parameters - - planner.addCommand(CommandType.OWNER_CHECK_1155, [ - params0.offerer, - TOWNSTAR_ADDRESS, - params0.offer[0].identifierOrCriteria, - 1, - ]) - - const { commands, inputs } = planner - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)).to.not.be.reverted - }) - - it('reverts for invalid ownership', async () => { - const params0 = advancedOrder0.parameters - - planner.addCommand(CommandType.OWNER_CHECK_1155, [ - alice.address, - TOWNSTAR_ADDRESS, - params0.offer[0].identifierOrCriteria, - 1, - ]) - - const { commands, inputs } = planner - const customErrorSelector = findCustomErrorSelector(router.interface, 'InvalidOwnerERC1155') - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) - .to.be.revertedWithCustomError(router, 'ExecutionFailed') - .withArgs(0, customErrorSelector) - }) - - it('checks ownership after a seaport trade for two ERC1155s', async () => { - const params0 = advancedOrder0.parameters - const params1 = advancedOrder1.parameters - - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - planner.addCommand(CommandType.OWNER_CHECK_1155, [ - alice.address, - TOWNSTAR_ADDRESS, - params0.offer[0].identifierOrCriteria, - 1, - ]) - planner.addCommand(CommandType.OWNER_CHECK_1155, [ - alice.address, - TOWNSTAR_ADDRESS, - params1.offer[0].identifierOrCriteria, - 1, - ]) - - const { commands, inputs } = planner - - const balance0Before = await townStarNFT.balanceOf(alice.address, params0.offer[0].identifierOrCriteria) - const balance1Before = await townStarNFT.balanceOf(alice.address, params1.offer[0].identifierOrCriteria) - - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, value.mul(-1)) - - const balance0After = await townStarNFT.balanceOf(alice.address, params0.offer[0].identifierOrCriteria) - const balance1After = await townStarNFT.balanceOf(alice.address, params1.offer[0].identifierOrCriteria) - - expect(balance0After.sub(balance0Before)).to.eq(1) - expect(balance1After.sub(balance1Before)).to.eq(1) - }) - }) describe('checks balance ERC20', () => { let aliceUSDCBalance: BigNumber @@ -197,8 +27,7 @@ describe('Check Ownership', () => { params: [ALICE_ADDRESS], }) alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter + router = (await deployUniversalRouter()).connect(alice) as UniversalRouter usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, alice) aliceUSDCBalance = await usdcContract.balanceOf(ALICE_ADDRESS) planner = new RoutePlanner() diff --git a/test/integration-tests/Cryptopunks.test.ts b/test/integration-tests/Cryptopunks.test.ts deleted file mode 100644 index 7e4eae2c..00000000 --- a/test/integration-tests/Cryptopunks.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import { UniversalRouter, Permit2 } from '../../typechain' -import { resetFork, CRYPTOPUNKS_MARKET } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE } from './shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber, Contract } from 'ethers' -import { expect } from 'chai' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' - -const { ethers } = hre - -describe('Cryptopunks', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let cryptopunks: Contract - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - - await resetFork(15848050) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - cryptopunks = CRYPTOPUNKS_MARKET.connect(alice) - }) - - // In this test we will buy crypto punk # 2976 for 74.95 ETH - describe('Buy 1 crypto punk', () => { - it('purchases token ids 2976', async () => { - const value = BigNumber.from('74950000000000000000') - planner.addCommand(CommandType.CRYPTOPUNKS, [2976, ALICE_ADDRESS, value]) - const { commands, inputs } = planner - - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, value.mul(-1)) - - // Expect that alice has the NFT - await expect((await cryptopunks.punkIndexToAddress(2976)).toLowerCase()).to.eq(ALICE_ADDRESS) - }) - }) -}) diff --git a/test/integration-tests/Element.test.ts b/test/integration-tests/Element.test.ts deleted file mode 100644 index 11015909..00000000 --- a/test/integration-tests/Element.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import ELEMENT_ABI from './shared/abis/Element.json' -import { ERC721, UniversalRouter, Permit2 } from '../../typechain' -import { resetFork } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE } from './shared/constants' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import { abi as ERC721_ABI } from '../../artifacts/solmate/src/tokens/ERC721.sol/ERC721.json' -import { expect } from 'chai' -import { EXAMPLE_ETH_SELL_ORDER, EXAMPLE_ETH_SELL_ORDER_SIG } from './shared/protocolHelpers/element' - -const { ethers } = hre - -const ELEMENT_721_INTERFACE = new ethers.utils.Interface(ELEMENT_ABI) - -describe('Element Market', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let testNFTContract: ERC721 - - const order = EXAMPLE_ETH_SELL_ORDER - const signature = EXAMPLE_ETH_SELL_ORDER_SIG - const nftContractAddress = order.nft - - beforeEach(async () => { - // txn is at block 16627214 - await resetFork(16627214 - 1) - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - testNFTContract = new ethers.Contract(nftContractAddress, ERC721_ABI).connect(alice) as ERC721 - }) - - it('purchases open order', async () => { - const value = BigNumber.from(order.erc20TokenAmount) - const calldata = ELEMENT_721_INTERFACE.encodeFunctionData('buyERC721Ex', [ - order, - signature, - order.taker, // taker - '0x', // extraData - ]) - - planner.addCommand(CommandType.ELEMENT_MARKET, [value.toString(), calldata]) - const { commands, inputs } = planner - - const ownerBefore = await testNFTContract.ownerOf(order.nftId) - - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })).to.changeEtherBalance( - alice, - value.mul(-1) - ) - - const ownerAfter = await testNFTContract.ownerOf(order.nftId) - expect(ownerBefore).to.eq(order.maker) - expect(ownerAfter).to.eq(order.taker) - }) -}) diff --git a/test/integration-tests/Foundation.test.ts b/test/integration-tests/Foundation.test.ts deleted file mode 100644 index 921b564f..00000000 --- a/test/integration-tests/Foundation.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import FOUNDATION_ABI from './shared/abis/Foundation.json' -import { UniversalRouter, Permit2 } from '../../typechain' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { resetFork, MENTAL_WORLDS_721 } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, MENTAL_WORLDS_ADDRESS } from './shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import { expect } from 'chai' -import { CommandType, RoutePlanner } from './shared/planner' -const { ethers } = hre - -const FOUNDATION_INTERFACE = new ethers.utils.Interface(FOUNDATION_ABI) -const REFERRER = '0x459e213D8B5E79d706aB22b945e3aF983d51BC4C' - -describe('Foundation', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - }) - - // In this test we will buy token id 32 of mental worlds NFT (0xEf96021Af16BD04918b0d87cE045d7984ad6c38c), - // which costs 0.01 ETH - describe('Buy a mental worlds NFT from Foundation', () => { - beforeEach(async () => { - await resetFork(15725945) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - }) - - it('purchases token id 32 of mental worlds', async () => { - const value = BigNumber.from('10000000000000000') - const calldata = FOUNDATION_INTERFACE.encodeFunctionData('buyV2', [MENTAL_WORLDS_ADDRESS, 32, value, REFERRER]) - planner.addCommand(CommandType.FOUNDATION, [value, calldata, ALICE_ADDRESS, MENTAL_WORLDS_ADDRESS, 32]) - const { commands, inputs } = planner - - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalances([alice, REFERRER], [value.mul(-1), value.div(100)]) - - // Expect that alice has the NFT - await expect((await MENTAL_WORLDS_721.connect(alice).ownerOf(32)).toLowerCase()).to.eq(ALICE_ADDRESS) - }) - }) -}) diff --git a/test/integration-tests/LooksRareV2.test.ts b/test/integration-tests/LooksRareV2.test.ts deleted file mode 100644 index f2a0f0e4..00000000 --- a/test/integration-tests/LooksRareV2.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import { UniversalRouter, Permit2, ERC721 } from '../../typechain' -import { resetFork, DRAGON_721 } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, ZERO_ADDRESS } from './shared/constants' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { expect } from 'chai' -const { ethers } = hre -import { - createLooksRareV2Order, - createLooksRareV2Orders, - looksRareV2Interface, - looksRareV2Orders, - LRV2APIOrder, -} from './shared/protocolHelpers/looksRareV2' - -describe('LooksRareV2', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let dragonNFT: ERC721 - let order: LRV2APIOrder - let order2: LRV2APIOrder - - describe('Single Buy', () => { - beforeEach(async () => { - await resetFork(17030829) - dragonNFT = DRAGON_721.connect(alice) - - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('Buys a Dragon', async () => { - order = looksRareV2Orders[0] - const { takerBid, makerOrder, makerSignature, value, merkleTree } = createLooksRareV2Order(order, alice.address) - const tokenId = makerOrder.itemIds[0] - const calldata = looksRareV2Interface.encodeFunctionData('executeTakerBid', [ - takerBid, - makerOrder, - makerSignature, - merkleTree, - ZERO_ADDRESS, - ]) - planner.addCommand(CommandType.LOOKS_RARE_V2, [value, calldata]) - - const { commands, inputs } = planner - - await expect((await dragonNFT.connect(alice).ownerOf(tokenId)).toLowerCase()).not.to.eq(ALICE_ADDRESS) - - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - await expect((await dragonNFT.connect(alice).ownerOf(tokenId)).toLowerCase()).to.eq(ALICE_ADDRESS) - }) - }) - - describe('Bulk Buy', () => { - beforeEach(async () => { - await resetFork(17037139) - dragonNFT = DRAGON_721.connect(alice) - - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('Buys 2 Dragons', async () => { - order = looksRareV2Orders[1] - order2 = looksRareV2Orders[2] - const { takerBids, makerOrders, makerSignatures, totalValue, merkleTrees } = createLooksRareV2Orders( - [order, order2], - alice.address - ) - - const tokenId = makerOrders[0].itemIds[0] - const tokenId2 = makerOrders[1].itemIds[0] - const calldata = looksRareV2Interface.encodeFunctionData('executeMultipleTakerBids', [ - takerBids, - makerOrders, - makerSignatures, - merkleTrees, - ZERO_ADDRESS, - false, - ]) - - planner.addCommand(CommandType.LOOKS_RARE_V2, [totalValue, calldata]) - - const { commands, inputs } = planner - - await expect((await dragonNFT.connect(alice).ownerOf(tokenId)).toLowerCase()).not.to.eq(ALICE_ADDRESS) - await expect((await dragonNFT.connect(alice).ownerOf(tokenId2)).toLowerCase()).not.to.eq(ALICE_ADDRESS) - - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: totalValue }) - - await expect((await dragonNFT.connect(alice).ownerOf(tokenId)).toLowerCase()).to.eq(ALICE_ADDRESS) - await expect((await dragonNFT.connect(alice).ownerOf(tokenId2)).toLowerCase()).to.eq(ALICE_ADDRESS) - }) - }) -}) diff --git a/test/integration-tests/NFT20.test.ts b/test/integration-tests/NFT20.test.ts deleted file mode 100644 index b1945245..00000000 --- a/test/integration-tests/NFT20.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import NFT20_ABI from './shared/abis/NFT20.json' -import { UniversalRouter, Permit2, ERC721 } from '../../typechain' -import { ALPHABETTIES_721, resetFork } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, ALPHABETTIES_ADDRESS, DEADLINE } from './shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import { expect } from 'chai' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -const { ethers } = hre - -const NFT20_INTERFACE = new ethers.utils.Interface(NFT20_ABI) - -describe('NFT20', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let alphabetties: ERC721 - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - - await resetFork(15770228) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - alphabetties = ALPHABETTIES_721.connect(alice) as ERC721 - }) - - // In this test we will buy token ids 129, 193, 278 of Alphabetties (0x6d05064fe99e40f1c3464e7310a23ffaded56e20). - // We will send 0.021~ ETH (20583701229648230 wei), and we will get refunded 1086067487962785 wei - describe('Buy 3 alphabetties from NFT20', () => { - it('purchases token ids 129, 193, 278 of Alphabetties', async () => { - const toSend = BigNumber.from('20583701229648230') - const expectedRefund = BigNumber.from('1086067487962785') - const calldata = NFT20_INTERFACE.encodeFunctionData('ethForNft', [ - ALPHABETTIES_ADDRESS, - ['129', '193', '278'], - ['1', '1', '1'], - ALICE_ADDRESS, - 0, - false, - ]) - planner.addCommand(CommandType.NFT20, [toSend, calldata]) - - const { commands, inputs } = planner - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: toSend }) - ).to.changeEtherBalance(alice, toSend.sub(expectedRefund).mul(-1)) - - // Expect that alice has the NFTs - await expect((await alphabetties.ownerOf(129)).toLowerCase()).to.eq(ALICE_ADDRESS) - await expect((await alphabetties.ownerOf(193)).toLowerCase()).to.eq(ALICE_ADDRESS) - await expect((await alphabetties.ownerOf(278)).toLowerCase()).to.eq(ALICE_ADDRESS) - }) - }) -}) diff --git a/test/integration-tests/NFTX.test.ts b/test/integration-tests/NFTX.test.ts deleted file mode 100644 index 53a45fe0..00000000 --- a/test/integration-tests/NFTX.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import { expect } from './shared/expect' -import { UniversalRouter, Permit2, ERC721, ERC1155 } from '../../typechain' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { parseEvents } from './shared/parseEvents' -import NFTX_ZAP_ABI from './shared/abis/NFTXZap.json' -import { TWERKY_1155, resetFork, MILADY_721 } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, NFTX_MILADY_VAULT_ID, NFTX_ERC_1155_VAULT_ID, ETH_ADDRESS } from './shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expandTo18DecimalsBN } from './shared/helpers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -const { ethers } = hre - -const nftxZapInterface = new ethers.utils.Interface(NFTX_ZAP_ABI) - -describe('NFTX', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let miladyContract: ERC721 - let twerkyContract: ERC1155 - let planner: RoutePlanner - - beforeEach(async () => { - await resetFork(17029001) // 17029002 - 1 - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - miladyContract = MILADY_721.connect(alice) - twerkyContract = TWERKY_1155.connect(alice) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('completes an ERC-721 buyAndRedeem order with random selection', async () => { - const value = expandTo18DecimalsBN(2.036523961400441269) - const numMiladys = 1 - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_MILADY_VAULT_ID, - numMiladys, - [], - '0xd9627aa400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000001bfb8d0ff32c43470000000000000000000000000000000000000000000000000e27c49886e6000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000227c7df69d3ed1ae7574a1a7685fded90292eb48869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000465b3a7f1b643618cb', - alice.address, - ]) - - planner.addCommand(CommandType.NFTX, [value.toString(), calldata]) - const { commands, inputs } = planner - - const miladyBalanceBefore = await miladyContract.balanceOf(alice.address) - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - const miladyBalanceAfter = await miladyContract.balanceOf(alice.address) - - expect(miladyBalanceAfter.sub(miladyBalanceBefore)).to.eq(numMiladys) - }) - - it('completes an ERC-721 buyAndRedeem order with specific selection', async () => { - const value = expandTo18DecimalsBN(2.036523961400441269) - const numMiladys = 1 - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_MILADY_VAULT_ID, - numMiladys, - [7132], - '0xd9627aa400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000001bfb8d0ff32c43470000000000000000000000000000000000000000000000000e27c49886e6000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000227c7df69d3ed1ae7574a1a7685fded90292eb48869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000465b3a7f1b643618cb', - alice.address, - ]) - - planner.addCommand(CommandType.NFTX, [value.toString(), calldata]) - const { commands, inputs } = planner - - const miladyBalanceBefore = await miladyContract.balanceOf(alice.address) - const miladyOwnerBefore = await miladyContract.ownerOf(7132) - await (await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })).wait() - const miladyBalanceAfter = await miladyContract.balanceOf(alice.address) - const miladyOwnerAfter = await miladyContract.ownerOf(7132) - - expect(miladyBalanceAfter.sub(miladyBalanceBefore)).to.eq(numMiladys) - expect(miladyOwnerBefore).to.not.eq(alice.address) - expect(miladyOwnerAfter).to.eq(alice.address) - }) - - it('completes an ERC-1155 buyAndRedeem order with random selection', async () => { - const value = expandTo18DecimalsBN(0.09115921) - const numTwerkys = 2 - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_ERC_1155_VAULT_ID, - numTwerkys, - [13, 16], - '0xd9627aa40000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000014240569380d14a0000000000000000000000000000000000000000000000001d6bc0c48bd4000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000078e09c5ec42d505742a52fd10078a57ea186002a869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000f24dfbbcf664372d25', - alice.address, - ]) - - planner.addCommand(CommandType.NFTX, [value.toString(), calldata]) - const { commands, inputs } = planner - - const tx = await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - const receipt = await tx.wait() - const tokenIds = parseEvents(twerkyContract.interface, receipt).map((event) => event!.args.id) - expect(await twerkyContract.balanceOf(alice.address, tokenIds[0])).to.eq(1) - expect(await twerkyContract.balanceOf(alice.address, tokenIds[1])).to.eq(1) - }) - - it('completes an ERC-1155 buyAndRedeem order with specific selection', async () => { - const value = expandTo18DecimalsBN(0.02561498) - const numTwerkys = 1 - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_ERC_1155_VAULT_ID, - numTwerkys, - [13], - '0xd9627aa40000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000005a8cca0bd3ccc90000000000000000000000000000000000000000000000000eb5e06245ea000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000078e09c5ec42d505742a52fd10078a57ea186002a869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000c6fd773d7864372d7e', - alice.address, - ]) - - planner.addCommand(CommandType.NFTX, [value.toString(), calldata]) - const { commands, inputs } = planner - - const twerkyBalanceBefore = await twerkyContract.balanceOf(alice.address, 13) - await (await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })).wait() - const twerkyBalanceAfter = await twerkyContract.balanceOf(alice.address, 13) - - expect(twerkyBalanceAfter.sub(twerkyBalanceBefore)).to.eq(numTwerkys) - }) - - it('returns all extra ETH when sending too much', async () => { - const value = expandTo18DecimalsBN(10) - const numMiladys = 1 - const saleCost = BigNumber.from('2016360357822219079') - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_MILADY_VAULT_ID, - numMiladys, - [7132], - '0xd9627aa400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000001bfb8d0ff32c43470000000000000000000000000000000000000000000000000e27c49886e6000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000227c7df69d3ed1ae7574a1a7685fded90292eb48869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000465b3a7f1b643618cb', - alice.address, - ]) - - planner.addCommand(CommandType.NFTX, [value.toString(), calldata]) - planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, alice.address, 0]) - const { commands, inputs } = planner - - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })).to.changeEtherBalance( - alice, - saleCost.mul(-1) - ) - }) -}) diff --git a/test/integration-tests/Seaport.test.ts b/test/integration-tests/Seaport.test.ts deleted file mode 100644 index fe6fb3d0..00000000 --- a/test/integration-tests/Seaport.test.ts +++ /dev/null @@ -1,313 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import { expect } from './shared/expect' -import { BigNumber, Contract } from 'ethers' -import { UniversalRouter, Permit2, ERC721, ERC1155 } from '../../typechain' -import { - seaportV1_5Orders, - seaportInterface, - getAdvancedOrderParams, - seaportV1_4Orders, - purchaseDataForTwoTownstarsSeaport, -} from './shared/protocolHelpers/seaport' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { COVEN_721, resetFork, TOWNSTAR_1155, GALA } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, ETH_ADDRESS, OPENSEA_CONDUIT_KEY } from './shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { findCustomErrorSelector } from './shared/parseEvents' -import { getPermitSignature } from './shared/protocolHelpers/permit2' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -const { ethers } = hre - -describe('Seaport v1.5', () => { - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let townStarNFT: ERC1155 - let alice: SignerWithAddress - - describe('ETH -> NFT', () => { - beforeEach(async () => { - await resetFork(17179617) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - townStarNFT = TOWNSTAR_1155.connect(alice) as ERC1155 - }) - - it('completes a fulfillAdvancedOrder type', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_5Orders[0]) - const params = advancedOrder.parameters - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - const tokenId = params.offer[0].identifierOrCriteria - - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - const { commands, inputs } = planner - - const balanceBefore = await townStarNFT.balanceOf(alice.address, tokenId) - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, value.mul(-1)) - - const balanceAfter = await townStarNFT.balanceOf(alice.address, tokenId) - - expect(balanceAfter.sub(balanceBefore)).to.eq(1) - }) - - it('revertable fulfillAdvancedOrder reverts and sweeps ETH', async () => { - let { advancedOrder, value } = getAdvancedOrderParams(seaportV1_5Orders[0]) - const params = advancedOrder.parameters - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - const tokenId = params.offer[0].identifierOrCriteria - - // Allow seaport to revert - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata], true) - planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, alice.address, 0]) - - const { commands, inputs } = planner - - const balanceBefore = await townStarNFT.balanceOf(alice.address, tokenId) - - // don't send enough ETH, so the seaport purchase reverts - value = BigNumber.from(value).sub(1) - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, 0) - - const balanceAfter = await townStarNFT.balanceOf(alice.address, tokenId) - // The balance was unchanged, the user got the eth back - expect(balanceBefore).to.eq(balanceAfter) - }) - - it('completes a fulfillAvailableAdvancedOrders type', async () => { - const { calldata, advancedOrder0, advancedOrder1, value } = purchaseDataForTwoTownstarsSeaport(alice.address) - const params0 = advancedOrder0.parameters - const params1 = advancedOrder1.parameters - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - const { commands, inputs } = planner - - const balance0Before = await townStarNFT.balanceOf(alice.address, params0.offer[0].identifierOrCriteria) - const balance1Before = await townStarNFT.balanceOf(alice.address, params1.offer[0].identifierOrCriteria) - - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, value.mul(-1)) - - const balance0After = await townStarNFT.balanceOf(alice.address, params0.offer[0].identifierOrCriteria) - const balance1After = await townStarNFT.balanceOf(alice.address, params1.offer[0].identifierOrCriteria) - - expect(balance0After.sub(balance0Before)).to.eq(1) - expect(balance1After.sub(balance1Before)).to.eq(1) - }) - - it('reverts if order does not go through', async () => { - let invalidSeaportOrder = JSON.parse(JSON.stringify(seaportV1_5Orders[1])) - - invalidSeaportOrder.protocol_data.signature = '0xdeadbeef' - const { advancedOrder: seaportOrder, value: seaportValue } = getAdvancedOrderParams(invalidSeaportOrder) - - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - seaportOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [seaportValue.toString(), calldata]) - const { commands, inputs } = planner - - const testCustomErrors = await (await ethers.getContractFactory('TestCustomErrors')).deploy() - const customErrorSelector = findCustomErrorSelector(testCustomErrors.interface, 'InvalidSignature') - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: seaportValue })) - .to.be.revertedWithCustomError(router, 'ExecutionFailed') - .withArgs(0, customErrorSelector) - }) - }) -}) - -describe('Seaport v1.4', () => { - let alice: SignerWithAddress - let bob: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let cryptoCovens: ERC721 - let galaContract: Contract - let townStarNFT: Contract - - describe('ERC20 -> NFT', () => { - beforeEach(async () => { - await resetFork(16784348 - 1) // 1 block before the order was created - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - galaContract = new ethers.Contract(GALA.address, TOKEN_ABI, alice) - - // alice can't sign permits as we don't have her private key. Instead bob is used - bob = (await ethers.getSigners())[1] - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter - planner = new RoutePlanner() - townStarNFT = TOWNSTAR_1155.connect(bob) as ERC1155 - await galaContract.connect(bob).approve(permit2.address, ethers.constants.MaxUint256) - - // Alice seeds bob's account with GALA for tests - await galaContract.transfer(bob.address, 100000 * 10 ** 8) - }) - - it('completes a fulfillAdvancedOrder type', async () => { - let { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[2]) - value = value.div(2) // the numerator/denominator mean this is a partial fill - const params = advancedOrder.parameters - const considerationToken = params.consideration[0].token - const tokenId = params.offer[0].identifierOrCriteria - - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - bob.address, - ]) - - const permit = { - details: { - token: considerationToken, - amount: value, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - planner.addCommand(CommandType.APPROVE_ERC20, [considerationToken, 0]) - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [considerationToken, router.address, value]) - planner.addCommand(CommandType.SEAPORT_V1_4, [0, calldata]) - - const { commands, inputs } = planner - await expect(await townStarNFT.balanceOf(bob.address, tokenId)).to.eq(0) - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)).to.changeTokenBalance( - galaContract, - bob.address, - value.mul(-1) - ) - await expect(await townStarNFT.balanceOf(bob.address, tokenId)).to.eq(1) - }) - }) - - describe('ETH -> NFT', () => { - beforeEach(async () => { - await resetFork(16784176 - 1) // 1 block before the order was created - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - cryptoCovens = COVEN_721.connect(alice) as ERC721 - }) - - it('completes a fulfillAdvancedOrder type', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [value.toString(), calldata]) - const { commands, inputs } = planner - - const ownerBefore = await cryptoCovens.ownerOf(params.offer[0].identifierOrCriteria) - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(alice, value.mul(-1)) - const ownerAfter = await cryptoCovens.ownerOf(params.offer[0].identifierOrCriteria) - - expect(ownerBefore.toLowerCase()).to.eq(params.offerer.toLowerCase()) - expect(ownerAfter).to.eq(alice.address) - }) - - it('revertable fulfillAdvancedOrder reverts and sweeps ETH', async () => { - let { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - // Allow seaport to revert - planner.addCommand(CommandType.SEAPORT_V1_4, [value.toString(), calldata], true) - planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, alice.address, 0]) - - const commands = planner.commands - const inputs = planner.inputs - - const ownerBefore = await cryptoCovens.ownerOf(params.offer[0].identifierOrCriteria) - const ethBefore = await ethers.provider.getBalance(alice.address) - - // don't send enough ETH, so the seaport purchase reverts - value = BigNumber.from(value).sub('1') - const receipt = await ( - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).wait() - - const ownerAfter = await cryptoCovens.ownerOf(params.offer[0].identifierOrCriteria) - const ethAfter = await ethers.provider.getBalance(alice.address) - const gasSpent = receipt.gasUsed.mul(receipt.effectiveGasPrice) - const ethDelta = ethBefore.sub(ethAfter) - - // The owner was unchanged, the user got the eth back - expect(ownerBefore.toLowerCase()).to.eq(ownerAfter.toLowerCase()) - expect(ethDelta).to.eq(gasSpent) - }) - - it('reverts if order does not go through', async () => { - let invalidSeaportOrder = JSON.parse(JSON.stringify(seaportV1_4Orders[0])) - - invalidSeaportOrder.protocol_data.signature = '0xdeadbeef' - const { advancedOrder: seaportOrder, value: seaportValue } = getAdvancedOrderParams(invalidSeaportOrder) - - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - seaportOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [seaportValue.toString(), calldata]) - const { commands, inputs } = planner - - const testCustomErrors = await (await ethers.getContractFactory('TestCustomErrors')).deploy() - const customErrorSelector = findCustomErrorSelector(testCustomErrors.interface, 'InvalidSignature') - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: seaportValue })) - .to.be.revertedWithCustomError(router, 'ExecutionFailed') - .withArgs(0, customErrorSelector) - }) - }) -}) diff --git a/test/integration-tests/Sudoswap.test.ts b/test/integration-tests/Sudoswap.test.ts deleted file mode 100644 index 7f9267d4..00000000 --- a/test/integration-tests/Sudoswap.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import SUDOSWAP_ABI from './shared/abis/Sudoswap.json' -import { ERC721, UniversalRouter, Permit2, ERC20 } from '../../typechain' -import { resetFork } from './shared/mainnetForkHelpers' -import { DEADLINE } from './shared/constants' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import { abi as ERC721_ABI } from '../../artifacts/solmate/src/tokens/ERC721.sol/ERC721.json' -import { abi as ERC20_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -import { expect } from 'chai' -import { getPermitSignature } from './shared/protocolHelpers/permit2' - -const { ethers } = hre - -const SUDOSWAP_INTERFACE = new ethers.utils.Interface(SUDOSWAP_ABI) -const SUDOLETS_ADDRESS = '0xfa9937555dc20a020a161232de4d2b109c62aa9c' -const BASED_GHOUL_ADDRESS = '0xeF1a89cbfAbE59397FfdA11Fc5DF293E9bC5Db90' -export const FRAX_ADDRESS = '0x853d955acef822db058eb8505911ed77f175b99e' - -describe('Sudoswap', () => { - let bob: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - await resetFork(16643381) // use recent block - planner = new RoutePlanner() - bob = (await ethers.getSigners())[1] - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter - }) - - describe('Buy with ETH', () => { - let sudolets: ERC721 - - beforeEach(async () => { - sudolets = new ethers.Contract(SUDOLETS_ADDRESS, ERC721_ABI).connect(bob) as ERC721 - }) - - // In this test we will buy token ids 173, 239, 240 of Sudolets (0xfa9937555dc20a020a161232de4d2b109c62aa9c), - // which costs 0.073 ETH (exactly 73337152777777692 wei) - it('purchases token ids 173, 239, 240 of Sudolets', async () => { - const value = BigNumber.from('73337152777777692') - const calldata = SUDOSWAP_INTERFACE.encodeFunctionData('robustSwapETHForSpecificNFTs', [ - [[['0x339e7004372e04b1d59443f0ddc075efd9d80360', ['173', '239', '240']], value]], - bob.address, - bob.address, - 1700000000, - ]) - planner.addCommand(CommandType.SUDOSWAP, [value, calldata]) - - const { commands, inputs } = planner - await expect( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - ).to.changeEtherBalance(bob, value.mul(-1)) - - // Expect that bob has the NFTs - expect(await sudolets.ownerOf(173)).to.eq(bob.address) - expect(await sudolets.ownerOf(173)).to.eq(bob.address) - expect(await sudolets.ownerOf(240)).to.eq(bob.address) - }) - }) - - describe('Buy using ERC20', () => { - let fraxToken: ERC20 - let basedGhoul: ERC721 - - beforeEach(async () => { - basedGhoul = new ethers.Contract(BASED_GHOUL_ADDRESS, ERC721_ABI).connect(bob) as ERC721 - fraxToken = new ethers.Contract(FRAX_ADDRESS, ERC20_ABI).connect(bob) as ERC20 - - const fraxWhaleSinger = await ethers.getImpersonatedSigner('0x839f654749F493f5407bde26556E5052376f144E') - // transfer FRAX from whale to bob - await fraxToken.connect(fraxWhaleSinger).transfer(bob.address, ethers.utils.parseEther('10000')) - // approve permit2 for all for bob's frax - await fraxToken.connect(bob).approve(permit2.address, ethers.constants.MaxUint256) - }) - - // buying 2 NFTs will cost exactly 226.492 FRAX - it('it buys tokens 2402, 2509 with FRAX ERC20 token', async () => { - const value = BigNumber.from('226492000000000000000') - const ghlFraxPairAddress = '0x9c9604405dea60d5AC4433FCf87D76a0bC6bB68B' - const calldata = SUDOSWAP_INTERFACE.encodeFunctionData('robustSwapERC20ForSpecificNFTs', [ - [[[ghlFraxPairAddress, ['2402', '2509']], value]], - value, - bob.address, - 1700000000, - ]) - - const permit = { - details: { - token: fraxToken.address, - amount: value, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - planner.addCommand(CommandType.APPROVE_ERC20, [fraxToken.address, 1]) - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [fraxToken.address, router.address, value]) - planner.addCommand(CommandType.SUDOSWAP, [0, calldata]) - const { commands, inputs } = planner - - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - - // Expect that bob has the NFTs - expect(await basedGhoul.ownerOf(2402)).to.eq(bob.address) - expect(await basedGhoul.ownerOf(2509)).to.eq(bob.address) - }) - - it('buys NFTs with ERC20 already approved', async () => { - planner.addCommand(CommandType.APPROVE_ERC20, [fraxToken.address, 1]) - await router['execute(bytes,bytes[],uint256)'](planner.commands, planner.inputs, DEADLINE, { value: 0 }) - - const value = BigNumber.from('226492000000000000000') - const ghlFraxPairAddress = '0x9c9604405dea60d5AC4433FCf87D76a0bC6bB68B' - const calldata = SUDOSWAP_INTERFACE.encodeFunctionData('robustSwapERC20ForSpecificNFTs', [ - [[[ghlFraxPairAddress, ['2402', '2509']], value]], - value, - bob.address, - 1700000000, - ]) - - const permit = { - details: { - token: fraxToken.address, - amount: value, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - planner = new RoutePlanner() - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [fraxToken.address, router.address, value]) - planner.addCommand(CommandType.SUDOSWAP, [0, calldata]) - const { commands, inputs } = planner - - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - - // Expect that bob has the NFTs - expect(await basedGhoul.ownerOf(2402)).to.eq(bob.address) - expect(await basedGhoul.ownerOf(2509)).to.eq(bob.address) - }) - }) -}) diff --git a/test/integration-tests/Uniswap.test.ts b/test/integration-tests/Uniswap.test.ts index cb964be0..eaf5c840 100644 --- a/test/integration-tests/Uniswap.test.ts +++ b/test/integration-tests/Uniswap.test.ts @@ -1,14 +1,12 @@ import type { Contract } from '@ethersproject/contracts' import { TransactionReceipt } from '@ethersproject/abstract-provider' import { Pair } from '@uniswap/v2-sdk' -import { FeeAmount } from '@uniswap/v3-sdk' import { parseEvents, V2_EVENTS, V3_EVENTS } from './shared/parseEvents' import { expect } from './shared/expect' -import { encodePath } from './shared/swapRouter02Helpers' import { BigNumber, BigNumberish } from 'ethers' -import { Permit2, UniversalRouter } from '../../typechain' +import { IPermit2, UniversalRouter } from '../../typechain' import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -import { resetFork, WETH, DAI, USDC, USDT } from './shared/mainnetForkHelpers' +import { resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from './shared/mainnetForkHelpers' import { ADDRESS_THIS, ALICE_ADDRESS, @@ -24,17 +22,18 @@ import { } from './shared/constants' import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' +import deployUniversalRouter from './shared/deployUniversalRouter' import { RoutePlanner, CommandType } from './shared/planner' import hre from 'hardhat' import { getPermitSignature, getPermitBatchSignature, PermitSingle } from './shared/protocolHelpers/permit2' +import { encodePathExactInput, encodePathExactOutput } from './shared/swapRouter02Helpers' const { ethers } = hre describe('Uniswap V2 and V3 Tests:', () => { let alice: SignerWithAddress let bob: SignerWithAddress let router: UniversalRouter - let permit2: Permit2 + let permit2: IPermit2 let daiContract: Contract let wethContract: Contract let usdcContract: Contract @@ -51,8 +50,8 @@ describe('Uniswap V2 and V3 Tests:', () => { daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + permit2 = PERMIT2.connect(bob) as IPermit2 + router = (await deployUniversalRouter()).connect(bob) as UniversalRouter planner = new RoutePlanner() // alice gives bob some tokens @@ -72,7 +71,7 @@ describe('Uniswap V2 and V3 Tests:', () => { it('V2 exactIn, permiting the exact amount', async () => { const amountInDAI = expandTo18DecimalsBN(100) - const minAmountOutWETH = expandTo18DecimalsBN(0.03) + const minAmountOutWETH = expandTo18DecimalsBN(0.02) // second bob signs a permit to allow the router to access his DAI permit = { @@ -102,7 +101,7 @@ describe('Uniswap V2 and V3 Tests:', () => { }) it('V2 exactOut, permiting the maxAmountIn', async () => { - const maxAmountInDAI = expandTo18DecimalsBN(3000) + const maxAmountInDAI = expandTo18DecimalsBN(4000) const amountOutWETH = expandTo18DecimalsBN(1) // second bob signs a permit to allow the router to access his DAI @@ -165,7 +164,7 @@ describe('Uniswap V2 and V3 Tests:', () => { it('V3 exactIn, permiting the exact amount', async () => { const amountInDAI = expandTo18DecimalsBN(100) - const minAmountOutWETH = expandTo18DecimalsBN(0.03) + const minAmountOutWETH = expandTo18DecimalsBN(0.02) // first bob approves permit2 to access his DAI await daiContract.connect(bob).approve(permit2.address, MAX_UINT) @@ -200,7 +199,7 @@ describe('Uniswap V2 and V3 Tests:', () => { }) it('V3 exactOut, permiting the exact amount', async () => { - const maxAmountInDAI = expandTo18DecimalsBN(3000) + const maxAmountInDAI = expandTo18DecimalsBN(4000) const amountOutWETH = expandTo18DecimalsBN(1) // first bob approves permit2 to access his DAI @@ -426,7 +425,7 @@ describe('Uniswap V2 and V3 Tests:', () => { describe('Trade on UniswapV3', () => { const amountIn: BigNumber = expandTo18DecimalsBN(500) - const amountInMax: BigNumber = expandTo18DecimalsBN(2000) + const amountInMax: BigNumber = expandTo18DecimalsBN(5000) const amountOut: BigNumber = expandTo18DecimalsBN(1) beforeEach(async () => { @@ -508,8 +507,6 @@ describe('Uniswap V2 and V3 Tests:', () => { // trade DAI in for WETH out const tokens = [DAI.address, USDC.address, WETH.address] const path = encodePathExactOutput(tokens) - // for these tests Bob gives the router max approval on permit2 - // await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) const { commands, inputs } = planner @@ -1259,12 +1256,4 @@ describe('Uniswap V2 and V3 Tests:', () => { gasSpent, } } - - function encodePathExactInput(tokens: string[]) { - return encodePath(tokens, new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)) - } - - function encodePathExactOutput(tokens: string[]) { - return encodePath(tokens.slice().reverse(), new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)) - } }) diff --git a/test/integration-tests/UniversalRouter.test.ts b/test/integration-tests/UniversalRouter.test.ts index 2e86f517..62c91aac 100644 --- a/test/integration-tests/UniversalRouter.test.ts +++ b/test/integration-tests/UniversalRouter.test.ts @@ -1,42 +1,22 @@ -import { - UniversalRouter, - Permit2, - ERC20, - IWETH9, - MockLooksRareRewardsDistributor, - ERC721, - ERC1155, -} from '../../typechain' -import { BigNumber, BigNumberish } from 'ethers' +import { UniversalRouter, ERC20, IWETH9, IPermit2 } from '../../typechain' +import { BigNumber } 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/src/tokens/ERC20.sol/ERC20.json' import { abi as WETH_ABI } from '../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json' -import NFTX_ZAP_ABI from './shared/abis/NFTXZap.json' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' +import deployUniversalRouter from './shared/deployUniversalRouter' import { ADDRESS_THIS, ALICE_ADDRESS, DEADLINE, - OPENSEA_CONDUIT_KEY, - ROUTER_REWARDS_DISTRIBUTOR, SOURCE_MSG_SENDER, MAX_UINT160, MAX_UINT, ETH_ADDRESS, - NFTX_MILADY_VAULT_ID, } from './shared/constants' -import { - seaportInterface, - getAdvancedOrderParams, - AdvancedOrder, - Order, - seaportV1_4Orders, - seaportV1_5Orders, -} from './shared/protocolHelpers/seaport' -import { resetFork, WETH, DAI, MILADY_721, TOWNSTAR_1155 } from './shared/mainnetForkHelpers' +import { resetFork, WETH, DAI, PERMIT2 } from './shared/mainnetForkHelpers' import { CommandType, RoutePlanner } from './shared/planner' import { makePair } from './shared/swapRouter02Helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' @@ -45,17 +25,15 @@ import hre from 'hardhat' import { findCustomErrorSelector } from './shared/parseEvents' 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 let router: UniversalRouter - let permit2: Permit2 + let permit2: IPermit2 let daiContract: ERC20 let wethContract: IWETH9 let mockLooksRareToken: ERC20 - let mockLooksRareRewardsDistributor: MockLooksRareRewardsDistributor let pair_DAI_WETH: Pair beforeEach(async () => { @@ -66,21 +44,11 @@ describe('UniversalRouter', () => { params: [ALICE_ADDRESS], }) - // mock rewards contracts - const tokenFactory = await ethers.getContractFactory('MintableERC20') - const mockDistributorFactory = await ethers.getContractFactory('MockLooksRareRewardsDistributor') - mockLooksRareToken = (await tokenFactory.connect(alice).deploy(expandTo18DecimalsBN(5))) as ERC20 - mockLooksRareRewardsDistributor = (await mockDistributorFactory.deploy( - ROUTER_REWARDS_DISTRIBUTOR, - mockLooksRareToken.address - )) as MockLooksRareRewardsDistributor daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice) as ERC20 wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9 pair_DAI_WETH = await makePair(alice, DAI, WETH) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = ( - await deployUniversalRouter(permit2, mockLooksRareRewardsDistributor.address, mockLooksRareToken.address) - ).connect(alice) as UniversalRouter + permit2 = PERMIT2.connect(alice) as IPermit2 + router = (await deployUniversalRouter()).connect(alice) as UniversalRouter }) describe('#execute', () => { @@ -149,227 +117,38 @@ describe('UniversalRouter', () => { }) 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 - + // create malicious calldata to sweep ETH out of the router 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, - ]) + + const reentrantWETH = await (await ethers.getContractFactory('ReenteringWETH')).deploy() + router = (await deployUniversalRouter(reentrantWETH.address)).connect(alice) as UniversalRouter + await reentrantWETH.setParameters(router.address, sweepCalldata) planner = new RoutePlanner() - planner.addCommand(CommandType.NFTX, [0, reentrantCalldata]) + const value = expandTo18DecimalsBN(1) + planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, value]) ;({ commands, inputs } = planner) - const customErrorSelector = findCustomErrorSelector(reentrantProtocol.interface, 'NotAllowedReenter') - await expect(router['execute(bytes,bytes[])'](commands, inputs)) - .to.be.revertedWithCustomError(router, 'ExecutionFailed') - .withArgs(0, customErrorSelector) - }) - }) - - describe('#collectRewards', () => { - let amountRewards: BigNumberish - beforeEach(async () => { - amountRewards = expandTo18DecimalsBN(0.5) - mockLooksRareToken.connect(alice).transfer(mockLooksRareRewardsDistributor.address, amountRewards) - }) - - it('transfers owed rewards into the distributor contract', async () => { - const balanceBefore = await mockLooksRareToken.balanceOf(ROUTER_REWARDS_DISTRIBUTOR) - await router.collectRewards('0x00') - const balanceAfter = await mockLooksRareToken.balanceOf(ROUTER_REWARDS_DISTRIBUTOR) - expect(balanceAfter.sub(balanceBefore)).to.eq(amountRewards) + await expect(router['execute(bytes,bytes[])'](commands, inputs, { value: value })).to.be.revertedWithCustomError( + reentrantWETH, + 'NotAllowedReenter' + ) }) }) }) describe('UniversalRouter', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let mockLooksRareToken: ERC20 - let mockLooksRareRewardsDistributor: MockLooksRareRewardsDistributor - let daiContract: ERC20 - let wethContract: IWETH9 - let townStarNFT: ERC1155 - - describe('#execute', () => { + describe('partial fills', async () => { let planner: RoutePlanner beforeEach(() => { planner = new RoutePlanner() }) - describe('ERC20 --> NFT', () => { - let advancedOrder: AdvancedOrder - let value: BigNumber - - beforeEach(async () => { - await resetFork(17179617) - alice = await ethers.getSigner(ALICE_ADDRESS) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice) as ERC20 - wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9 - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - townStarNFT = TOWNSTAR_1155.connect(alice) as ERC1155 - ;({ advancedOrder, value } = getAdvancedOrderParams(seaportV1_5Orders[0])) - await daiContract.approve(permit2.address, MAX_UINT) - await wethContract.approve(permit2.address, MAX_UINT) - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) - }) - - it('completes a trade for ERC20 --> ETH --> Seaport NFT', async () => { - const maxAmountIn = expandTo18DecimalsBN(100_000) - const tokenId = advancedOrder.parameters.offer[0].identifierOrCriteria - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - ADDRESS_THIS, - value, - maxAmountIn, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, value]) - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - const { commands, inputs } = planner - const balanceBefore = await townStarNFT.balanceOf(alice.address, tokenId) - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) - const balanceAfter = await townStarNFT.balanceOf(alice.address, tokenId) - expect(balanceAfter.sub(balanceBefore)).to.eq(1) - }) - - it('completes a trade for WETH --> ETH --> Seaport NFT', async () => { - const tokenId = advancedOrder.parameters.offer[0].identifierOrCriteria - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [WETH.address, ADDRESS_THIS, value]) - planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, value]) - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - - const { commands, inputs } = planner - const balanceBefore = await townStarNFT.balanceOf(alice.address, tokenId) - const wethBalanceBefore = await wethContract.balanceOf(alice.address) - - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)).to.changeEtherBalance( - alice, - 0 - ) - - const balanceAfter = await townStarNFT.balanceOf(alice.address, tokenId) - const wethBalanceAfter = await wethContract.balanceOf(alice.address) - - expect(balanceAfter.sub(balanceBefore)).to.eq(1) - expect(wethBalanceBefore.sub(wethBalanceAfter)).to.eq(value) - }) - }) - - describe('partial fills', async () => { - let nftxValue: BigNumber - let numMiladys: number - let value: BigNumber - let invalidSeaportCalldata: string - let seaportValue: BigNumber - let miladyContract: ERC721 - - beforeEach(async () => { - await resetFork(17029001) // 17029002 - 1 - alice = await ethers.getSigner(ALICE_ADDRESS) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - - // mock rewards contracts - const tokenFactory = await ethers.getContractFactory('MintableERC20') - const mockDistributorFactory = await ethers.getContractFactory('MockLooksRareRewardsDistributor') - mockLooksRareToken = (await tokenFactory.connect(alice).deploy(expandTo18DecimalsBN(5))) as ERC20 - mockLooksRareRewardsDistributor = (await mockDistributorFactory.deploy( - ROUTER_REWARDS_DISTRIBUTOR, - mockLooksRareToken.address - )) as MockLooksRareRewardsDistributor - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = ( - await deployUniversalRouter(permit2, mockLooksRareRewardsDistributor.address, mockLooksRareToken.address) - ).connect(alice) as UniversalRouter - - miladyContract = MILADY_721.connect(alice) as ERC721 - - // add valid nftx order to planner - nftxValue = expandTo18DecimalsBN(2.036523961400441269) - numMiladys = 1 - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_MILADY_VAULT_ID, - numMiladys, - [], - '0xd9627aa400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000001bfb8d0ff32c43470000000000000000000000000000000000000000000000000e27c49886e6000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000227c7df69d3ed1ae7574a1a7685fded90292eb48869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000465b3a7f1b643618cb', - alice.address, - ]) - planner.addCommand(CommandType.NFTX, [nftxValue, calldata]) - - let invalidSeaportOrder = JSON.parse(JSON.stringify(seaportV1_4Orders[1])) - invalidSeaportOrder.protocol_data.signature = '0xdeadbeef' - let seaportOrder: Order - ;({ advancedOrder: seaportOrder, value: seaportValue } = getAdvancedOrderParams(invalidSeaportOrder)) - invalidSeaportCalldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - seaportOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - value = seaportValue.add(nftxValue) - }) - - it('reverts if no commands are allowed to revert', async () => { - planner.addCommand(CommandType.SEAPORT_V1_4, [seaportValue, invalidSeaportCalldata]) - - const { commands, inputs } = planner - - const testCustomErrors = await (await ethers.getContractFactory('TestCustomErrors')).deploy() - const customErrorSelector = findCustomErrorSelector(testCustomErrors.interface, 'InvalidSignature') - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - .to.be.revertedWithCustomError(router, 'ExecutionFailed') - .withArgs(1, customErrorSelector) - }) - - it('does not revert if invalid seaport transaction allowed to fail', async () => { - planner.addCommand(CommandType.SEAPORT_V1_4, [seaportValue, invalidSeaportCalldata], true) - const { commands, inputs } = planner - - const miladyBalanceBefore = await miladyContract.balanceOf(alice.address) - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) - const miladyBalanceAfter = await miladyContract.balanceOf(alice.address) - expect(miladyBalanceAfter.sub(miladyBalanceBefore)).to.eq(numMiladys) - }) - }) + // TODO need to rewrite these tests for non-NFT commands + it('reverts if no commands are allowed to revert') + it('does not revert if failed command allowed to fail') }) }) diff --git a/test/integration-tests/X2Y2.test.ts b/test/integration-tests/X2Y2.test.ts deleted file mode 100644 index c6ebfa2f..00000000 --- a/test/integration-tests/X2Y2.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { CommandType, RoutePlanner } from './shared/planner' -import { abi as ERC721_ABI } from '../../artifacts/solmate/src/tokens/ERC721.sol/ERC721.json' -import { UniversalRouter, Permit2 } from '../../typechain' -import { resetFork, ENS_721, CAMEO_1155 } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, CAMEO_ADDRESS, DEADLINE, ENS_NFT_ADDRESS } from './shared/constants' -import { parseEvents } from './shared/parseEvents' -import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { expect } from 'chai' -import { X2Y2Order, x2y2Orders, X2Y2_INTERFACE } from './shared/protocolHelpers/x2y2' -const { ethers } = hre - -const ERC721_INTERFACE = new ethers.utils.Interface(ERC721_ABI) - -describe('X2Y2', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - }) - - describe('ERC-721 purchase', () => { - let commands: string - let inputs: string[] - let erc721Order: X2Y2Order - - beforeEach(async () => { - await resetFork() - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - - erc721Order = x2y2Orders[0] - const functionSelector = X2Y2_INTERFACE.getSighash(X2Y2_INTERFACE.getFunction('run')) - const calldata = functionSelector + erc721Order.input.slice(2) - planner.addCommand(CommandType.X2Y2_721, [ - erc721Order.price, - calldata, - ALICE_ADDRESS, - ENS_NFT_ADDRESS, - erc721Order.token_id, - ]) - ;({ commands, inputs } = planner) - }) - - it('purchases 1 ERC-721 on X2Y2', async () => { - const receipt = await ( - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: erc721Order.price }) - ).wait() - const erc721TransferEvent = parseEvents(ERC721_INTERFACE, receipt)[1]?.args! - - const newOwner = await ENS_721.connect(alice).ownerOf(erc721Order.token_id) - await expect(newOwner.toLowerCase()).to.eq(ALICE_ADDRESS) - await expect(erc721TransferEvent.from).to.be.eq(router.address) - await expect(erc721TransferEvent.to.toLowerCase()).to.be.eq(ALICE_ADDRESS) - await expect(erc721TransferEvent.id).to.be.eq(erc721Order.token_id) - }) - }) - - describe('ERC-1155 purchase', () => { - let commands: string - let inputs: string[] - let erc1155Order: X2Y2Order - - beforeEach(async () => { - await resetFork(15650000) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - - erc1155Order = x2y2Orders[1] - const functionSelector = X2Y2_INTERFACE.getSighash(X2Y2_INTERFACE.getFunction('run')) - const calldata = functionSelector + erc1155Order.input.slice(2) - planner.addCommand(CommandType.X2Y2_1155, [ - erc1155Order.price, - calldata, - ALICE_ADDRESS, - CAMEO_ADDRESS, - erc1155Order.token_id, - 1, - ]) - ;({ commands, inputs } = planner) - }) - - it('purchases 1 ERC-1155 on X2Y2', async () => { - await expect(await CAMEO_1155.connect(alice).balanceOf(alice.address, erc1155Order.token_id)).to.eq(0) - await ( - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: erc1155Order.price }) - ).wait() - await expect(await CAMEO_1155.connect(alice).balanceOf(alice.address, erc1155Order.token_id)).to.eq(1) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts b/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts index 450c0e4b..6b156abd 100644 --- a/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts +++ b/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts @@ -1,121 +1,30 @@ import { CommandType, RoutePlanner } from './../shared/planner' -import { Permit2, UniversalRouter } from '../../../typechain' +import { UniversalRouter } from '../../../typechain' import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { - seaportV1_4Orders, - seaportInterface, - getAdvancedOrderParams, - purchaseDataForTwoTownstarsSeaport, -} from './../shared/protocolHelpers/seaport' import { resetFork, USDC } from './../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, COVEN_ADDRESS, DEADLINE, OPENSEA_CONDUIT_KEY, TOWNSTAR_ADDRESS } from './../shared/constants' +import { ALICE_ADDRESS, DEADLINE } from './../shared/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import hre from 'hardhat' -import deployUniversalRouter, { deployPermit2 } from './../shared/deployUniversalRouter' +import deployUniversalRouter from './../shared/deployUniversalRouter' import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' const { ethers } = hre describe('Check Ownership Gas', () => { let alice: SignerWithAddress let router: UniversalRouter - let permit2: Permit2 let planner: RoutePlanner beforeEach(async () => { - await resetFork(16784175) + await resetFork() await hre.network.provider.request({ method: 'hardhat_impersonateAccount', params: [ALICE_ADDRESS], }) alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter + router = (await deployUniversalRouter()).connect(alice) as UniversalRouter planner = new RoutePlanner() }) - it('gas: does not check ownership after a seaport trade, one NFT', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [value.toString(), calldata]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - it('gas: checks ownership after a seaport trade, one NFT', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [value.toString(), calldata]) - planner.addCommand(CommandType.OWNER_CHECK_721, [ - alice.address, - COVEN_ADDRESS, - params.offer[0].identifierOrCriteria, - ]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - it('gas: checks ownership after a seaport trade, two NFTs', async () => { - await resetFork(17179617) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - - const { calldata, advancedOrder0, advancedOrder1, value } = purchaseDataForTwoTownstarsSeaport(alice.address) - const params0 = advancedOrder0.parameters - const params1 = advancedOrder1.parameters - - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - planner.addCommand(CommandType.OWNER_CHECK_1155, [ - alice.address, - TOWNSTAR_ADDRESS, - params0.offer[0].identifierOrCriteria, - 1, - ]) - planner.addCommand(CommandType.OWNER_CHECK_1155, [ - alice.address, - TOWNSTAR_ADDRESS, - params1.offer[0].identifierOrCriteria, - 1, - ]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - it('gas: just ownership check', async () => { - const { advancedOrder } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const params = advancedOrder.parameters - - planner.addCommand(CommandType.OWNER_CHECK_721, [ - params.offerer, - COVEN_ADDRESS, - params.offer[0].identifierOrCriteria, - ]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) - }) - it('gas: balance check ERC20', async () => { const usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, alice) const aliceUSDCBalance = await usdcContract.balanceOf(ALICE_ADDRESS) diff --git a/test/integration-tests/gas-tests/CryptoPunk.gas.test.ts b/test/integration-tests/gas-tests/CryptoPunk.gas.test.ts deleted file mode 100644 index 5c9bd0ea..00000000 --- a/test/integration-tests/gas-tests/CryptoPunk.gas.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CommandType, RoutePlanner } from '../shared/planner' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { UniversalRouter, Permit2 } from '../../../typechain' -import { resetFork } from '../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE } from '../shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' - -const { ethers } = hre - -describe('Cryptopunks', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - - await resetFork(15848050) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - }) - - it('purchases 1 cryptopunk gas', async () => { - const value = BigNumber.from('74950000000000000000') - planner.addCommand(CommandType.CRYPTOPUNKS, [2976, ALICE_ADDRESS, value]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: value })) - }) -}) diff --git a/test/integration-tests/gas-tests/Element.gas.test.ts b/test/integration-tests/gas-tests/Element.gas.test.ts deleted file mode 100644 index 76caf432..00000000 --- a/test/integration-tests/gas-tests/Element.gas.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { CommandType, RoutePlanner } from '../shared/planner' -import ELEMENT_ABI from '../shared/abis/Element.json' -import { UniversalRouter, Permit2 } from '../../../typechain' -import { resetFork } from '../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE } from '../shared/constants' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import { EXAMPLE_ETH_SELL_ORDER, EXAMPLE_ETH_SELL_ORDER_SIG } from '../shared/protocolHelpers/element' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' - -const { ethers } = hre - -const ELEMENT_721_INTERFACE = new ethers.utils.Interface(ELEMENT_ABI) - -describe('Element Market gas tests', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - const order = EXAMPLE_ETH_SELL_ORDER - const signature = EXAMPLE_ETH_SELL_ORDER_SIG - - beforeEach(async () => { - // txn is at block 16627214 - await resetFork(16627214 - 1) - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - }) - - it('gas: purchases open order', async () => { - const value = BigNumber.from(order.erc20TokenAmount) - const calldata = ELEMENT_721_INTERFACE.encodeFunctionData('buyERC721Ex', [ - order, - signature, - order.taker, // taker - '0x', // extraData - ]) - - planner.addCommand(CommandType.ELEMENT_MARKET, [value.toString(), calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) -}) diff --git a/test/integration-tests/gas-tests/Foundation.gas.test.ts b/test/integration-tests/gas-tests/Foundation.gas.test.ts deleted file mode 100644 index f3d18050..00000000 --- a/test/integration-tests/gas-tests/Foundation.gas.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import FOUNDATION_ABI from '../shared/abis/Foundation.json' -import { UniversalRouter, Permit2 } from '../../../typechain' -import { resetFork } from '../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE } from '../shared/constants' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import { CommandType, RoutePlanner } from '../shared/planner' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -const { ethers } = hre - -const FOUNDATION_INTERFACE = new ethers.utils.Interface(FOUNDATION_ABI) -const MENTAL_WORLDS_ADDRESS = '0xEf96021Af16BD04918b0d87cE045d7984ad6c38c' -const REFERRER = '0x459e213D8B5E79d706aB22b945e3aF983d51BC4C' - -describe('Foundation Gas Tests', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - }) - - // In this test we will buy token id 32 of mental worlds NFT (0xEf96021Af16BD04918b0d87cE045d7984ad6c38c), - // which costs 0.01 ETH - describe('Buy a mental worlds NFT from Foundation', () => { - beforeEach(async () => { - await resetFork(15725945) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - }) - - it('gas: token id 32 of mental worlds', async () => { - const value = BigNumber.from('10000000000000000') - const calldata = FOUNDATION_INTERFACE.encodeFunctionData('buyV2', [MENTAL_WORLDS_ADDRESS, 32, value, REFERRER]) - planner.addCommand(CommandType.FOUNDATION, [value, calldata, ALICE_ADDRESS, MENTAL_WORLDS_ADDRESS, 32]) - - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: value })) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/LooksRareV2.gas.test.ts b/test/integration-tests/gas-tests/LooksRareV2.gas.test.ts deleted file mode 100644 index 8bf604ae..00000000 --- a/test/integration-tests/gas-tests/LooksRareV2.gas.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { CommandType, RoutePlanner } from './../shared/planner' -import { UniversalRouter, Permit2 } from '../../../typechain' -import { resetFork } from './../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, ZERO_ADDRESS } from './../shared/constants' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -const { ethers } = hre -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -import { - LRV2APIOrder, - createLooksRareV2Order, - createLooksRareV2Orders, - looksRareV2Interface, - looksRareV2Orders, -} from '../shared/protocolHelpers/looksRareV2' - -describe('LooksRareV2 Gas Test', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let order: LRV2APIOrder - let order2: LRV2APIOrder - - describe('Single Buy', () => { - beforeEach(async () => { - await resetFork(17030829) - - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('Buy a 721', async () => { - order = looksRareV2Orders[0] - const { takerBid, makerOrder, makerSignature, value, merkleTree } = createLooksRareV2Order(order, alice.address) - const calldata = looksRareV2Interface.encodeFunctionData('executeTakerBid', [ - takerBid, - makerOrder, - makerSignature, - merkleTree, - ZERO_ADDRESS, - ]) - planner.addCommand(CommandType.LOOKS_RARE_V2, [value, calldata]) - - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - }) - - describe('Bulk Buy', () => { - beforeEach(async () => { - await resetFork(17037139) - - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('Buys 2 721s', async () => { - order = looksRareV2Orders[1] - order2 = looksRareV2Orders[2] - const { takerBids, makerOrders, makerSignatures, totalValue, merkleTrees } = createLooksRareV2Orders( - [order, order2], - alice.address - ) - - const calldata = looksRareV2Interface.encodeFunctionData('executeMultipleTakerBids', [ - takerBids, - makerOrders, - makerSignatures, - merkleTrees, - ZERO_ADDRESS, - false, - ]) - - planner.addCommand(CommandType.LOOKS_RARE_V2, [totalValue, calldata]) - - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: totalValue })) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/NFT20.gas.test.ts b/test/integration-tests/gas-tests/NFT20.gas.test.ts deleted file mode 100644 index c1d928ad..00000000 --- a/test/integration-tests/gas-tests/NFT20.gas.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { CommandType, RoutePlanner } from './../shared/planner' -import NFT20_ABI from './../shared/abis/NFT20.json' -import { UniversalRouter, Permit2 } from '../../../typechain' -import { resetFork } from './../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, ALPHABETTIES_ADDRESS, DEADLINE } from './../shared/constants' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -const { ethers } = hre - -const NFT20_INTERFACE = new ethers.utils.Interface(NFT20_ABI) - -describe('NFT20', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - - await resetFork(15770228) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - }) - - // In this test we will buy token ids 129, 193, 278 of Alphabetties (0x6d05064fe99e40f1c3464e7310a23ffaded56e20). - // We will send 0.021~ ETH (20583701229648230 wei), and we will get refunded 1086067487962785 wei - describe('Buy 3 alphabetties from NFT20', () => { - it('gas: purchases token ids 129, 193, 278 of Alphabetties', async () => { - const value = BigNumber.from('20583701229648230') - const calldata = NFT20_INTERFACE.encodeFunctionData('ethForNft', [ - ALPHABETTIES_ADDRESS, - ['129', '193', '278'], - ['1', '1', '1'], - ALICE_ADDRESS, - 0, - false, - ]) - planner.addCommand(CommandType.NFT20, [value, calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: value })) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/NFTX.gas.test.ts b/test/integration-tests/gas-tests/NFTX.gas.test.ts deleted file mode 100644 index 5f0f3965..00000000 --- a/test/integration-tests/gas-tests/NFTX.gas.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { CommandType, RoutePlanner } from './../shared/planner' -import { UniversalRouter, Permit2 } from '../../../typechain' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import NFTX_ZAP_ABI from './../shared/abis/NFTXZap.json' -import { resetFork } from './../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, NFTX_MILADY_VAULT_ID } from './../shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expandTo18DecimalsBN } from './../shared/helpers' -import hre from 'hardhat' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -const { ethers } = hre - -const nftxZapInterface = new ethers.utils.Interface(NFTX_ZAP_ABI) - -describe('NFTX Gas Tests', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - await resetFork(17029001) // 17029002 - 1 - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('gas: buyAndRedeem w/ specific selection', async () => { - const value = expandTo18DecimalsBN(4) - const numMiladys = 1 - const calldata = nftxZapInterface.encodeFunctionData('buyAndRedeem', [ - NFTX_MILADY_VAULT_ID, - numMiladys, - [7132], - '0xd9627aa400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000001bfb8d0ff32c43470000000000000000000000000000000000000000000000000e27c49886e6000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000227c7df69d3ed1ae7574a1a7685fded90292eb48869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000000000465b3a7f1b643618cb', - alice.address, - ]) - - planner.addCommand(CommandType.NFTX, [value.toString(), calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) -}) diff --git a/test/integration-tests/gas-tests/Payments.gas.test.ts b/test/integration-tests/gas-tests/Payments.gas.test.ts index c537efec..0d4a1eab 100644 --- a/test/integration-tests/gas-tests/Payments.gas.test.ts +++ b/test/integration-tests/gas-tests/Payments.gas.test.ts @@ -1,12 +1,12 @@ import type { Contract } from '@ethersproject/contracts' -import { UniversalRouter, Permit2 } from '../../../typechain' +import { UniversalRouter } from '../../../typechain' import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { resetFork, DAI, WETH } from '../shared/mainnetForkHelpers' import { ALICE_ADDRESS, DEADLINE, ETH_ADDRESS, ONE_PERCENT_BIPS } from '../shared/constants' import { expandTo18DecimalsBN } from '../shared/helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import hre from 'hardhat' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' +import deployUniversalRouter from '../shared/deployUniversalRouter' import { RoutePlanner, CommandType } from '../shared/planner' import snapshotGasCost from '@uniswap/snapshot-gas-cost' const { ethers } = hre @@ -18,7 +18,6 @@ describe('Payments Gas Tests', () => { let alice: SignerWithAddress let bob: SignerWithAddress let router: UniversalRouter - let permit2: Permit2 let daiContract: Contract let wethContract: Contract let planner: RoutePlanner @@ -33,8 +32,7 @@ describe('Payments Gas Tests', () => { bob = (await ethers.getSigners())[1] daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice) wethContract = new ethers.Contract(WETH.address, new ethers.utils.Interface(WETH_ABI.abi), alice) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter + router = (await deployUniversalRouter()).connect(alice) as UniversalRouter planner = new RoutePlanner() }) @@ -130,13 +128,5 @@ describe('Payments Gas Tests', () => { await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) }) - - it('gas: APPROVE_ERC20', async () => { - const SEAPORT_V2_ID: number = 1 - planner.addCommand(CommandType.APPROVE_ERC20, [DAI.address, SEAPORT_V2_ID]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) - }) }) }) diff --git a/test/integration-tests/gas-tests/SeaportV1_4.gas.test.ts b/test/integration-tests/gas-tests/SeaportV1_4.gas.test.ts deleted file mode 100644 index b829f1d9..00000000 --- a/test/integration-tests/gas-tests/SeaportV1_4.gas.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { CommandType, RoutePlanner } from '../shared/planner' -import { UniversalRouter, Permit2 } from '../../../typechain' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { seaportV1_4Orders, seaportInterface, getAdvancedOrderParams } from '../shared/protocolHelpers/seaport' -import { GALA, resetFork } from '../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, OPENSEA_CONDUIT_KEY } from '../shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -import { Contract } from 'ethers' -import { getPermitSignature } from '../shared/protocolHelpers/permit2' -const { ethers } = hre - -describe('Seaport v1.4 Gas Tests', () => { - let alice: SignerWithAddress - let bob: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - let galaContract: Contract - - describe('ETH -> NFT', () => { - beforeEach(async () => { - await resetFork(16784176 - 1) // 1 block before the order was created - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('gas: fulfillAdvancedOrder', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[0]) - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_4, [value.toString(), calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - }) - - describe('ERC20 -> NFT', () => { - beforeEach(async () => { - await resetFork(16784348 - 1) // 1 block before the order was created - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - galaContract = new ethers.Contract(GALA.address, TOKEN_ABI, alice) - - // alice can't sign permits as we don;t have her private key. Instead bob is used - bob = (await ethers.getSigners())[1] - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter - planner = new RoutePlanner() - await galaContract.connect(bob).approve(permit2.address, ethers.constants.MaxUint256) - - // Alice seeds bob's account with GALA for tests - await galaContract.transfer(bob.address, 100000 * 10 ** 8) - }) - - it('gas: fulfillAdvancedOrder', async () => { - let { advancedOrder, value } = getAdvancedOrderParams(seaportV1_4Orders[2]) - value = value.div(2) // the numerator/denominator mean this is a partial fill - const params = advancedOrder.parameters - const considerationToken = params.consideration[0].token - - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - bob.address, - ]) - - const permit = { - details: { - token: considerationToken, - amount: value, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - planner.addCommand(CommandType.APPROVE_ERC20, [considerationToken, 0]) - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [considerationToken, router.address, value]) - planner.addCommand(CommandType.SEAPORT_V1_4, [0, calldata]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/SeaportV1_5.gas.test.ts b/test/integration-tests/gas-tests/SeaportV1_5.gas.test.ts deleted file mode 100644 index c1b3d74d..00000000 --- a/test/integration-tests/gas-tests/SeaportV1_5.gas.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { CommandType, RoutePlanner } from '../shared/planner' -import { UniversalRouter, Permit2 } from '../../../typechain' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { - seaportV1_5Orders, - seaportInterface, - getAdvancedOrderParams, - purchaseDataForTwoTownstarsSeaport, -} from '../shared/protocolHelpers/seaport' -import { resetFork } from '../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, OPENSEA_CONDUIT_KEY } from '../shared/constants' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -const { ethers } = hre - -describe('Seaport v1.5 Gas Tests', () => { - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - describe('ETH -> NFT', () => { - let alice: SignerWithAddress - beforeEach(async () => { - await resetFork(17179617) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - planner = new RoutePlanner() - }) - - it('gas: fulfillAdvancedOrder', async () => { - const { advancedOrder, value } = getAdvancedOrderParams(seaportV1_5Orders[0]) - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - it('gas: fulfillAvailableAdvancedOrders 2 orders', async () => { - const { calldata, value } = purchaseDataForTwoTownstarsSeaport(alice.address) - - planner.addCommand(CommandType.SEAPORT_V1_5, [value, calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/Sudoswap.gas.test.ts b/test/integration-tests/gas-tests/Sudoswap.gas.test.ts deleted file mode 100644 index b22ce784..00000000 --- a/test/integration-tests/gas-tests/Sudoswap.gas.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { CommandType, RoutePlanner } from '../shared/planner' -import SUDOSWAP_ABI from '../shared/abis/Sudoswap.json' -import { abi as ERC20_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -import { UniversalRouter, Permit2, ERC20 } from '../../../typechain' -import { resetFork } from '../shared/mainnetForkHelpers' -import { DEADLINE } from '../shared/constants' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { BigNumber } from 'ethers' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -import { FRAX_ADDRESS } from '../Sudoswap.test' -import { getPermitSignature } from '../shared/protocolHelpers/permit2' -const { ethers } = hre - -const SUDOSWAP_INTERFACE = new ethers.utils.Interface(SUDOSWAP_ABI) - -describe('Sudoswap Gas Tests', () => { - let bob: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - await resetFork(16643381) // use recent block - planner = new RoutePlanner() - bob = (await ethers.getSigners())[1] - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter - }) - - // In this test we will buy token ids 173, 239, 240 of Sudolets (0xfa9937555dc20a020a161232de4d2b109c62aa9c), - // which costs 0.073 ETH (exactly 73337152777777692 wei) - describe('ETH -> NFT', () => { - it('gas: purchases token ids 173, 239, 240 of Sudolets', async () => { - const value = BigNumber.from('73337152777777692') - const calldata = SUDOSWAP_INTERFACE.encodeFunctionData('robustSwapETHForSpecificNFTs', [ - [[['0x339e7004372e04b1d59443f0ddc075efd9d80360', ['173', '239', '240']], value]], - bob.address, - bob.address, - 1700000000, - ]) - - planner.addCommand(CommandType.SUDOSWAP, [value, calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: value })) - }) - }) - - describe('ERC20 -> NFT', () => { - let fraxToken: ERC20 - - beforeEach(async () => { - fraxToken = new ethers.Contract(FRAX_ADDRESS, ERC20_ABI).connect(bob) as ERC20 - const fraxWhaleSinger = await ethers.getImpersonatedSigner('0x839f654749F493f5407bde26556E5052376f144E') - // transfer FRAX from whale to bob - await fraxToken.connect(fraxWhaleSinger).transfer(bob.address, ethers.utils.parseEther('10000')) - // approve permit2 for all for bob's frax - await fraxToken.connect(bob).approve(permit2.address, ethers.constants.MaxUint256) - }) - - // buying 2 NFTs will cost exactly 226.492 FRAX - it('gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token', async () => { - const value = BigNumber.from('226492000000000000000') - const ghlFraxPairAddress = '0x9c9604405dea60d5AC4433FCf87D76a0bC6bB68B' - const calldata = SUDOSWAP_INTERFACE.encodeFunctionData('robustSwapERC20ForSpecificNFTs', [ - [[[ghlFraxPairAddress, ['2402', '2509']], value]], - value, - bob.address, - 1700000000, - ]) - - const permit = { - details: { - token: fraxToken.address, - amount: value, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - planner.addCommand(CommandType.APPROVE_ERC20, [fraxToken.address, 1]) - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [fraxToken.address, router.address, value]) - planner.addCommand(CommandType.SUDOSWAP, [0, calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - // buying 2 NFTs will cost exactly 226.492 FRAX - it('gas: purchases NFTs with FRAX ERC20 token already approved', async () => { - planner.addCommand(CommandType.APPROVE_ERC20, [fraxToken.address, 1]) - await router['execute(bytes,bytes[],uint256)'](planner.commands, planner.inputs, DEADLINE, { value: 0 }) - - const value = BigNumber.from('226492000000000000000') - const ghlFraxPairAddress = '0x9c9604405dea60d5AC4433FCf87D76a0bC6bB68B' - const calldata = SUDOSWAP_INTERFACE.encodeFunctionData('robustSwapERC20ForSpecificNFTs', [ - [[[ghlFraxPairAddress, ['2402', '2509']], value]], - value, - bob.address, - 1700000000, - ]) - - const permit = { - details: { - token: fraxToken.address, - amount: value, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - planner = new RoutePlanner() - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [fraxToken.address, router.address, value]) - planner.addCommand(CommandType.SUDOSWAP, [0, calldata]) - const { commands, inputs } = planner - - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/Uniswap.gas.test.ts b/test/integration-tests/gas-tests/Uniswap.gas.test.ts index 5f7634ad..899744e8 100644 --- a/test/integration-tests/gas-tests/Uniswap.gas.test.ts +++ b/test/integration-tests/gas-tests/Uniswap.gas.test.ts @@ -1,25 +1,26 @@ import type { Contract } from '@ethersproject/contracts' import { CurrencyAmount, Ether, Percent, Token, TradeType } from '@uniswap/sdk-core' import { Route as V2RouteSDK, Pair } from '@uniswap/v2-sdk' -import { Route as V3RouteSDK, FeeAmount } from '@uniswap/v3-sdk' +import { Route as V3RouteSDK } from '@uniswap/v3-sdk' import { SwapRouter, Trade } from '@uniswap/router-sdk' import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' +import deployUniversalRouter from '../shared/deployUniversalRouter' import { getPermitBatchSignature } from '../shared/protocolHelpers/permit2' import { makePair, expandTo18Decimals, - encodePath, pool_DAI_WETH, pool_DAI_USDC, pool_USDC_WETH, pool_USDC_USDT, pool_WETH_USDT, + encodePathExactOutput, + encodePathExactInput, } from '../shared/swapRouter02Helpers' import { BigNumber, BigNumberish } from 'ethers' -import { UniversalRouter, Permit2 } from '../../../typechain' +import { IPermit2, UniversalRouter } from '../../../typechain' import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -import { approveAndExecuteSwapRouter02, resetFork, WETH, DAI, USDC, USDT } from '../shared/mainnetForkHelpers' +import { approveAndExecuteSwapRouter02, resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from '../shared/mainnetForkHelpers' import { ADDRESS_THIS, ALICE_ADDRESS, @@ -39,19 +40,11 @@ import hre from 'hardhat' import { RoutePlanner, CommandType } from '../shared/planner' const { ethers } = hre -function encodePathExactInput(tokens: string[]) { - return encodePath(tokens, new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)) -} - -function encodePathExactOutput(tokens: string[]) { - return encodePath(tokens.slice().reverse(), new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)) -} - describe('Uniswap Gas Tests', () => { let alice: SignerWithAddress let bob: SignerWithAddress let router: UniversalRouter - let permit2: Permit2 + let permit2: IPermit2 let daiContract: Contract let wethContract: Contract let planner: RoutePlanner @@ -71,8 +64,8 @@ describe('Uniswap Gas Tests', () => { bob = (await ethers.getSigners())[1] daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + permit2 = PERMIT2.connect(bob) as IPermit2 + router = (await deployUniversalRouter()).connect(bob) as UniversalRouter pair_DAI_WETH = await makePair(bob, DAI, WETH) pair_DAI_USDC = await makePair(bob, DAI, USDC) pair_USDC_WETH = await makePair(bob, USDC, WETH) @@ -187,8 +180,8 @@ describe('Uniswap Gas Tests', () => { beforeEach(async () => { planner = new RoutePlanner() // for these tests Bob gives the router max approval on permit2 - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) + await permit2.connect(bob).approve(DAI.address, router.address, MAX_UINT160, DEADLINE) + await permit2.connect(bob).approve(WETH.address, router.address, MAX_UINT160, DEADLINE) }) describe('ERC20 --> ERC20', () => { @@ -509,7 +502,7 @@ describe('Uniswap Gas Tests', () => { describe('with Universal Router.', () => { const amountIn: BigNumber = expandTo18DecimalsBN(500) - const amountInMax: BigNumber = expandTo18DecimalsBN(2000) + const amountInMax: BigNumber = expandTo18DecimalsBN(5000) const amountOut: BigNumber = expandTo18DecimalsBN(1) const addV3ExactInTrades = ( @@ -536,8 +529,8 @@ describe('Uniswap Gas Tests', () => { planner = new RoutePlanner() // for these tests Bob gives the router max approval on permit2 - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) + await permit2.connect(bob).approve(DAI.address, router.address, MAX_UINT160, DEADLINE) + await permit2.connect(bob).approve(WETH.address, router.address, MAX_UINT160, DEADLINE) }) describe('ERC20 --> ERC20', () => { diff --git a/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts b/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts index bce33d2f..b7dc0353 100644 --- a/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts +++ b/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts @@ -1,30 +1,13 @@ -import { UniversalRouter, Permit2, IWETH9, ERC20 } from '../../../typechain' +import { UniversalRouter, IWETH9, ERC20 } from '../../../typechain' import { expect } from '../shared/expect' -import { - ALICE_ADDRESS, - ADDRESS_THIS, - DEADLINE, - MAX_UINT, - MAX_UINT160, - OPENSEA_CONDUIT_KEY, - SOURCE_MSG_SENDER, -} from '../shared/constants' +import { ALICE_ADDRESS } from '../shared/constants' import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { abi as WETH_ABI } from '../../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' import { resetFork, WETH, DAI } from '../shared/mainnetForkHelpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import hre from 'hardhat' -import { expandTo18DecimalsBN } from '../shared/helpers' -import { - seaportV1_5Orders, - seaportInterface, - getAdvancedOrderParams, - AdvancedOrder, -} from '../shared/protocolHelpers/seaport' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -import { RoutePlanner, CommandType } from '../shared/planner' -import { BigNumber } from 'ethers' +import deployUniversalRouter from '../shared/deployUniversalRouter' +import { RoutePlanner } from '../shared/planner' const { ethers } = hre @@ -32,12 +15,11 @@ describe('UniversalRouter Gas Tests', () => { let alice: SignerWithAddress let planner: RoutePlanner let router: UniversalRouter - let permit2: Permit2 let daiContract: ERC20 let wethContract: IWETH9 beforeEach(async () => { - await resetFork(17179617) + await resetFork() alice = await ethers.getSigner(ALICE_ADDRESS) await hre.network.provider.request({ method: 'hardhat_impersonateAccount', @@ -45,76 +27,11 @@ describe('UniversalRouter Gas Tests', () => { }) daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice) as ERC20 wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9 - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter + router = (await deployUniversalRouter()).connect(alice) as UniversalRouter planner = new RoutePlanner() }) it('gas: bytecode size', async () => { expect(((await router.provider.getCode(router.address)).length - 2) / 2).to.matchSnapshot() }) - - describe('trading for NFTs', async () => { - let advancedOrder: AdvancedOrder - let value: BigNumber - - beforeEach(async () => { - ;({ advancedOrder, value } = getAdvancedOrderParams(seaportV1_5Orders[0])) - await daiContract.approve(permit2.address, MAX_UINT) - await wethContract.approve(permit2.address, MAX_UINT) - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) - }) - - it('gas: ETH --> Seaport NFT', async () => { - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - it('gas: ERC20 --> ETH --> Seaport NFT', async () => { - const maxAmountIn = expandTo18DecimalsBN(100_000) - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - router.address, - value, - maxAmountIn, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [alice.address, value]) - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - - it('gas: WETH --> ETH --> Seaport NFT', async () => { - const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [ - advancedOrder, - [], - OPENSEA_CONDUIT_KEY, - alice.address, - ]) - - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [WETH.address, ADDRESS_THIS, value]) - planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, value]) - planner.addCommand(CommandType.SEAPORT_V1_5, [value.toString(), calldata]) - - const { commands, inputs } = planner - await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })) - }) - }) }) diff --git a/test/integration-tests/gas-tests/UniversalVSSwapRouter.gas.test.ts b/test/integration-tests/gas-tests/UniversalVSSwapRouter.gas.test.ts index ae52a122..cc71964b 100644 --- a/test/integration-tests/gas-tests/UniversalVSSwapRouter.gas.test.ts +++ b/test/integration-tests/gas-tests/UniversalVSSwapRouter.gas.test.ts @@ -1,25 +1,31 @@ -import { encodeSqrtRatioX96, FeeAmount, Pool, TickMath } from '@uniswap/v3-sdk' +import { Pool } from '@uniswap/v3-sdk' import { Pair, Route as V2RouteSDK } from '@uniswap/v2-sdk' import { Route as V3RouteSDK } from '@uniswap/v3-sdk' -import { encodePath, expandTo18Decimals } from '../shared/swapRouter02Helpers' +import { + encodePath, + expandTo18Decimals, + pool_DAI_USDT, + pool_DAI_WETH, + pool_USDC_USDT, + pool_USDC_WETH, +} from '../shared/swapRouter02Helpers' import { BigNumber } from 'ethers' import { SwapRouter } from '@uniswap/router-sdk' import { executeSwapRouter02Swap, resetFork, - WETH, DAI, USDC, - USDT, approveSwapRouter02, + PERMIT2, } from '../shared/mainnetForkHelpers' import { ALICE_ADDRESS, DEADLINE, MAX_UINT, MAX_UINT160, SOURCE_MSG_SENDER } from '../shared/constants' import { expandTo6DecimalsBN } from '../shared/helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' +import deployUniversalRouter from '../shared/deployUniversalRouter' import { RoutePlanner, CommandType } from '../shared/planner' import hre from 'hardhat' -import { UniversalRouter, Permit2, ERC20__factory, ERC20 } from '../../../typechain' +import { UniversalRouter, ERC20__factory, ERC20, IPermit2 } from '../../../typechain' import { getPermitSignature, PermitSingle } from '../shared/protocolHelpers/permit2' import { CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core' import snapshotGasCost from '@uniswap/snapshot-gas-cost' @@ -31,7 +37,7 @@ describe('Uniswap UX Tests gas:', () => { let bob: SignerWithAddress let router: UniversalRouter - let permit2: Permit2 + let permit2: IPermit2 let usdcContract: ERC20 let planner: RoutePlanner @@ -52,8 +58,8 @@ describe('Uniswap UX Tests gas:', () => { usdcContract = ERC20__factory.connect(USDC.address, alice) - permit2 = (await deployPermit2()).connect(bob) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + permit2 = PERMIT2.connect(alice) as IPermit2 + router = (await deployUniversalRouter()).connect(bob) as UniversalRouter planner = new RoutePlanner() @@ -70,16 +76,6 @@ describe('Uniswap UX Tests gas:', () => { 3000 USDC —V2—> DAI */ - const createPool = (tokenA: Token, tokenB: Token, fee: FeeAmount) => { - return new Pool(tokenA, tokenB, fee, sqrtRatioX96, 1_000_000, TickMath.getTickAtSqrtRatio(sqrtRatioX96)) - } - - const sqrtRatioX96 = encodeSqrtRatioX96(1, 1) - const USDC_WETH = createPool(USDC, WETH, FeeAmount.HIGH) - const DAI_WETH = createPool(DAI, WETH, FeeAmount.HIGH) - const USDC_USDT = createPool(USDC, USDT, FeeAmount.LOWEST) - const USDT_DAI = createPool(DAI, USDT, FeeAmount.LOWEST) - const USDC_DAI_V2 = new Pair( CurrencyAmount.fromRawAmount(USDC, 10000000), CurrencyAmount.fromRawAmount(DAI, 10000000) @@ -93,7 +89,7 @@ describe('Uniswap UX Tests gas:', () => { SIMPLE_SWAP = new Trade({ v3Routes: [ { - routev3: new V3RouteSDK([USDC_WETH, DAI_WETH], USDC, DAI), + routev3: new V3RouteSDK([pool_USDC_WETH, pool_DAI_WETH], USDC, DAI), inputAmount: simpleSwapAmountInUSDC, outputAmount: CurrencyAmount.fromRawAmount(DAI, expandTo18Decimals(1000)), }, @@ -105,12 +101,12 @@ describe('Uniswap UX Tests gas:', () => { COMPLEX_SWAP = new Trade({ v3Routes: [ { - routev3: new V3RouteSDK([USDC_WETH, DAI_WETH], USDC, DAI), + routev3: new V3RouteSDK([pool_USDC_WETH, pool_DAI_WETH], USDC, DAI), inputAmount: complexSwapAmountInSplit1, outputAmount: CurrencyAmount.fromRawAmount(DAI, expandTo18Decimals(3000)), }, { - routev3: new V3RouteSDK([USDC_USDT, USDT_DAI], USDC, DAI), + routev3: new V3RouteSDK([pool_USDC_USDT, pool_DAI_USDT], USDC, DAI), inputAmount: complexSwapAmountInSplit2, outputAmount: CurrencyAmount.fromRawAmount(DAI, expandTo18Decimals(4000)), }, @@ -191,7 +187,7 @@ describe('Uniswap UX Tests gas:', () => { describe('Approvals', async () => { it('Cost for infinite approval of permit2/swaprouter02 contract', async () => { // Bob max-approves the permit2 contract to access his DAI and WETH - await snapshotGasCost(await usdcContract.approve(permit2.address, MAX_UINT)) + await snapshotGasCost(await usdcContract.approve(PERMIT2.address, MAX_UINT)) }) }) @@ -202,7 +198,8 @@ describe('Uniswap UX Tests gas:', () => { beforeEach(async () => { // bob has already given his infinite approval of USDC to permit2 const permitApprovalTx = await usdcContract.connect(bob).approve(permit2.address, MAX_UINT) - approvePermit2Gas = (await permitApprovalTx.wait()).gasUsed + const receipt = await permitApprovalTx.wait() + approvePermit2Gas = receipt.gasUsed const swapRouter02ApprovalTx = (await approveSwapRouter02(bob, USDC))! approveSwapRouter02Gas = swapRouter02ApprovalTx.gasUsed @@ -469,7 +466,7 @@ describe('Uniswap UX Tests gas:', () => { } // Launch SwapRouter03 - const router2 = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + const router2 = (await deployUniversalRouter()).connect(bob) as UniversalRouter const router2ApprovalTx = (await approveSwapRouter02(bob, USDC, router2.address))! totalGas = totalGas.add(router2ApprovalTx.gasUsed) @@ -480,7 +477,7 @@ describe('Uniswap UX Tests gas:', () => { } // Launch SwapRouter04 - const router3 = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + const router3 = (await deployUniversalRouter()).connect(bob) as UniversalRouter const router3ApprovalTx = (await approveSwapRouter02(bob, USDC, router3.address))! totalGas = totalGas.add(router3ApprovalTx.gasUsed) @@ -509,7 +506,7 @@ describe('Uniswap UX Tests gas:', () => { } // Launch Universal Router v2 - const router2 = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + const router2 = (await deployUniversalRouter()).connect(bob) as UniversalRouter // Do 5 simple swaps for (let i = 0; i < 5; i++) { @@ -523,7 +520,7 @@ describe('Uniswap UX Tests gas:', () => { } // Launch Universal Router v3 - const router3 = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + const router3 = (await deployUniversalRouter()).connect(bob) as UniversalRouter // Do 5 simple swaps for (let i = 0; i < 5; i++) { @@ -555,7 +552,7 @@ describe('Uniswap UX Tests gas:', () => { } // Launch Universal Router v2 - const router2 = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + const router2 = (await deployUniversalRouter()).connect(bob) as UniversalRouter MAX_PERMIT.spender = router2.address let calldata2 = await getPermitSignature(MAX_PERMIT, bob, permit2) planner.addCommand(CommandType.PERMIT2_PERMIT, [MAX_PERMIT, calldata2]) @@ -568,7 +565,7 @@ describe('Uniswap UX Tests gas:', () => { } // Launch Universal Router v3 - const router3 = (await deployUniversalRouter(permit2)).connect(bob) as UniversalRouter + const router3 = (await deployUniversalRouter()).connect(bob) as UniversalRouter MAX_PERMIT.spender = router3.address let calldata3 = await getPermitSignature(MAX_PERMIT, bob, permit2) planner.addCommand(CommandType.PERMIT2_PERMIT, [MAX_PERMIT, calldata3]) @@ -586,12 +583,7 @@ describe('Uniswap UX Tests gas:', () => { }) function encodePathExactInput(route: IRoute) { - const addresses = routeToAddresses(route) - const feeTiers = new Array(addresses.length - 1) - for (let i = 0; i < feeTiers.length; i++) { - feeTiers[i] = addresses[i] == WETH.address || addresses[i + 1] == WETH.address ? FeeAmount.HIGH : FeeAmount.LOWEST - } - return encodePath(addresses, feeTiers) + return encodePath(routeToAddresses(route)) } function routeToAddresses(route: IRoute) { diff --git a/test/integration-tests/gas-tests/X2Y2.gas.test.ts b/test/integration-tests/gas-tests/X2Y2.gas.test.ts deleted file mode 100644 index 842230ee..00000000 --- a/test/integration-tests/gas-tests/X2Y2.gas.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { CommandType, RoutePlanner } from './../shared/planner' -import { UniversalRouter, Permit2 } from '../../../typechain' -import { resetFork } from './../shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, ENS_NFT_ADDRESS, CAMEO_ADDRESS } from './../shared/constants' -import snapshotGasCost from '@uniswap/snapshot-gas-cost' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import hre from 'hardhat' -import { X2Y2Order, x2y2Orders, X2Y2_INTERFACE } from '../shared/protocolHelpers/x2y2' -import deployUniversalRouter, { deployPermit2 } from '../shared/deployUniversalRouter' -const { ethers } = hre - -describe('X2Y2', () => { - let alice: SignerWithAddress - let router: UniversalRouter - let permit2: Permit2 - let planner: RoutePlanner - - beforeEach(async () => { - planner = new RoutePlanner() - alice = await ethers.getSigner(ALICE_ADDRESS) - }) - - describe('ERC-721 purchase', () => { - let commands: string - let inputs: string[] - let erc721Order: X2Y2Order - - beforeEach(async () => { - await resetFork() - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - - erc721Order = x2y2Orders[0] - const functionSelector = X2Y2_INTERFACE.getSighash(X2Y2_INTERFACE.getFunction('run')) - const calldata = functionSelector + erc721Order.input.slice(2) - planner.addCommand(CommandType.X2Y2_721, [ - erc721Order.price, - calldata, - ALICE_ADDRESS, - ENS_NFT_ADDRESS, - erc721Order.token_id, - ]) - ;({ commands, inputs } = planner) - }) - - it('gas: purchases 1 ERC-721 on X2Y2', async () => { - await snapshotGasCost( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: erc721Order.price }) - ) - }) - }) - - describe('ERC-1155 purchase', () => { - let commands: string - let inputs: string[] - let erc1155Order: X2Y2Order - - beforeEach(async () => { - await resetFork(15650000) - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - permit2 = (await deployPermit2()).connect(alice) as Permit2 - router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter - - erc1155Order = x2y2Orders[1] - const functionSelector = X2Y2_INTERFACE.getSighash(X2Y2_INTERFACE.getFunction('run')) - const calldata = functionSelector + erc1155Order.input.slice(2) - planner.addCommand(CommandType.X2Y2_1155, [ - erc1155Order.price, - calldata, - ALICE_ADDRESS, - CAMEO_ADDRESS, - erc1155Order.token_id, - 1, - ]) - ;({ commands, inputs } = planner) - }) - - it('gas: purchases 1 ERC-1155 on X2Y2', async () => { - await snapshotGasCost( - router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value: erc1155Order.price }) - ) - }) - }) -}) diff --git a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap index 6e029571..51121507 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap @@ -3,34 +3,6 @@ exports[`Check Ownership Gas gas: balance check ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39837, -} -`; - -exports[`Check Ownership Gas gas: checks ownership after a seaport trade, one NFT 1`] = ` -Object { - "calldataByteLength": 2180, - "gasUsed": 185340, -} -`; - -exports[`Check Ownership Gas gas: checks ownership after a seaport trade, two NFTs 1`] = ` -Object { - "calldataByteLength": 5028, - "gasUsed": 294767, -} -`; - -exports[`Check Ownership Gas gas: does not check ownership after a seaport trade, one NFT 1`] = ` -Object { - "calldataByteLength": 2020, - "gasUsed": 182447, -} -`; - -exports[`Check Ownership Gas gas: just ownership check 1`] = ` -Object { - "calldataByteLength": 356, - "gasUsed": 32519, + "gasUsed": 39759, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts 2.snap b/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts 2.snap deleted file mode 100644 index 8346c811..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts 2.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Cryptopunks purchases 1 cryptopunk gas 1`] = ` -Object { - "calldataByteLength": 356, - "gasUsed": 106004, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap deleted file mode 100644 index bd5c70ec..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Cryptopunks purchases 1 cryptopunk gas 1`] = ` -Object { - "calldataByteLength": 356, - "gasUsed": 109587, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap deleted file mode 100644 index e7e41b73..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Element Market gas tests gas: purchases open order 1`] = ` -Object { - "calldataByteLength": 964, - "gasUsed": 123947, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap deleted file mode 100644 index 43748aee..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Foundation Gas Tests Buy a mental worlds NFT from Foundation gas: token id 32 of mental worlds 1`] = ` -Object { - "calldataByteLength": 612, - "gasUsed": 182987, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap deleted file mode 100644 index f000a1f9..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LooksRareV2 Gas Test Bulk Buy Buys 2 721s 1`] = ` -Object { - "calldataByteLength": 2884, - "gasUsed": 335502, -} -`; - -exports[`LooksRareV2 Gas Test Single Buy Buy a 721 1`] = ` -Object { - "calldataByteLength": 1508, - "gasUsed": 189890, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap deleted file mode 100644 index 7854cf4a..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NFT20 Buy 3 alphabetties from NFT20 gas: purchases token ids 129, 193, 278 of Alphabetties 1`] = ` -Object { - "calldataByteLength": 836, - "gasUsed": 392225, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap deleted file mode 100644 index 7202d1a2..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` -Object { - "calldataByteLength": 964, - "gasUsed": 574873, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 8835d99b..56251cf0 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -1,57 +1,50 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Payments Gas Tests Individual Command Tests gas: APPROVE_ERC20 1`] = ` -Object { - "calldataByteLength": 324, - "gasUsed": 53851, -} -`; - exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39247, + "gasUsed": 39128, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67949, + "gasUsed": 67780, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38260, + "gasUsed": 38153, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33833, + "gasUsed": 33724, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46768, + "gasUsed": 46692, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53154, + "gasUsed": 52970, } `; exports[`Payments Gas Tests Individual Command Tests gas: WRAP_ETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 57425, + "gasUsed": 57301, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap deleted file mode 100644 index 992ad7dc..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` -Object { - "calldataByteLength": 2532, - "gasUsed": 237147, -} -`; - -exports[`Seaport v1.4 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` -Object { - "calldataByteLength": 2020, - "gasUsed": 182447, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap deleted file mode 100644 index 6df4f12c..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` -Object { - "calldataByteLength": 2020, - "gasUsed": 152100, -} -`; - -exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAvailableAdvancedOrders 2 orders 1`] = ` -Object { - "calldataByteLength": 4644, - "gasUsed": 288379, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap deleted file mode 100644 index 5e57a015..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` -Object { - "calldataByteLength": 1380, - "gasUsed": 289932, -} -`; - -exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` -Object { - "calldataByteLength": 1508, - "gasUsed": 311561, -} -`; - -exports[`Sudoswap Gas Tests ETH -> NFT gas: purchases token ids 173, 239, 240 of Sudolets 1`] = ` -Object { - "calldataByteLength": 836, - "gasUsed": 201657, -} -`; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 82261989..4705592f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 273118, + "gasUsed": 271359, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248840, + "gasUsed": 247093, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248840, + "gasUsed": 247093, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 273118, + "gasUsed": 271359, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 196805, + "gasUsed": 191528, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189601, + "gasUsed": 179118, } `; 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": 305318, + "gasUsed": 300577, } `; 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": 313390, + "gasUsed": 309796, } `; 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": 315444, + "gasUsed": 311874, } `; 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": 309818, + "gasUsed": 306382, } `; 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": 179783, + "gasUsed": 178884, } `; 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": 179558, + "gasUsed": 178659, } `; 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": 198193, + "gasUsed": 196684, } `; 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": 187611, + "gasUsed": 186755, } `; 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": 200301, + "gasUsed": 193725, } `; @@ -115,7 +115,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Router02. gas: ERC20 --> ERC20 exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 452, - "gasUsed": 193278, + "gasUsed": 189985, } `; @@ -143,209 +143,209 @@ 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": 128848, + "gasUsed": 128642, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 109042, + "gasUsed": 108947, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 246952, + "gasUsed": 243553, } `; 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": 246694, + "gasUsed": 243295, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179686, + "gasUsed": 176296, } `; 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": 179686, + "gasUsed": 176296, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 109016, + "gasUsed": 109053, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 252135, + "gasUsed": 248954, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 182300, + "gasUsed": 179077, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 125261, + "gasUsed": 125148, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 130432, + "gasUsed": 130412, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 138433, + "gasUsed": 138353, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108953, + "gasUsed": 108718, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127619, + "gasUsed": 127611, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Router02. gas: ERC20 --> ERC20 exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 420, - "gasUsed": 104491, + "gasUsed": 104144, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Router02. gas: ERC20 --> ERC20 exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 283773, + "gasUsed": 265954, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Router02. gas: ERC20 --> ERC20 exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 196595, + "gasUsed": 185123, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Router02. gas: ERC20 --> ERC20 exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 420, - "gasUsed": 114662, + "gasUsed": 113677, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Router02. gas: ERC20 --> ERC20 exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 423130, + "gasUsed": 256480, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Router02. gas: ERC20 --> ERC20 exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 313743, + "gasUsed": 176659, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117121, + "gasUsed": 107624, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 276297, + "gasUsed": 256009, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 192380, + "gasUsed": 179203, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118726, + "gasUsed": 117154, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 421548, + "gasUsed": 253167, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 315107, + "gasUsed": 176865, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 133388, + "gasUsed": 123873, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 135065, + "gasUsed": 133475, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 242368, + "gasUsed": 217436, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 129549, + "gasUsed": 128593, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 736a906a..60180095 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17917`; - -exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` -Object { - "calldataByteLength": 2468, - "gasUsed": 252709, -} -`; - -exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` -Object { - "calldataByteLength": 2020, - "gasUsed": 152100, -} -`; - -exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` -Object { - "calldataByteLength": 2308, - "gasUsed": 186879, -} -`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `12879`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index ad650092..8cd90000 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -3,36 +3,36 @@ exports[`Uniswap UX Tests gas: Approvals Cost for infinite approval of permit2/swaprouter02 contract 1`] = ` Object { "calldataByteLength": 68, - "gasUsed": 60311, + "gasUsed": 55846, } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1162406`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1110322`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1196788`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1144704`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3239734`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3101911`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3393155`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3255320`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4329408`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4134291`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4533182`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4338065`; -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 Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `532928`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `510754`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `533246`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `511084`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `310693`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `301726`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `310629`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `301674`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap deleted file mode 100644 index 724d5967..00000000 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` -Object { - "calldataByteLength": 2212, - "gasUsed": 210138, -} -`; - -exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` -Object { - "calldataByteLength": 2276, - "gasUsed": 211195, -} -`; diff --git a/test/integration-tests/shared/abis/Cryptopunks.json b/test/integration-tests/shared/abis/Cryptopunks.json deleted file mode 100644 index edb8925c..00000000 --- a/test/integration-tests/shared/abis/Cryptopunks.json +++ /dev/null @@ -1,595 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "punksOfferedForSale", - "outputs": [ - { - "name": "isForSale", - "type": "bool" - }, - { - "name": "punkIndex", - "type": "uint256" - }, - { - "name": "seller", - "type": "address" - }, - { - "name": "minValue", - "type": "uint256" - }, - { - "name": "onlySellTo", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "enterBidForPunk", - "outputs": [], - "payable": true, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - }, - { - "name": "minPrice", - "type": "uint256" - } - ], - "name": "acceptBidForPunk", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "addresses", - "type": "address[]" - }, - { - "name": "indices", - "type": "uint256[]" - } - ], - "name": "setInitialOwners", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [], - "name": "withdraw", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "imageHash", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "nextPunkIndexToAssign", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "punkIndexToAddress", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "standard", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "punkBids", - "outputs": [ - { - "name": "hasBid", - "type": "bool" - }, - { - "name": "punkIndex", - "type": "uint256" - }, - { - "name": "bidder", - "type": "address" - }, - { - "name": "value", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [], - "name": "allInitialOwnersAssigned", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "allPunksAssigned", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "buyPunk", - "outputs": [], - "payable": true, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "to", - "type": "address" - }, - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "transferPunk", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "withdrawBidForPunk", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "to", - "type": "address" - }, - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "setInitialOwner", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - }, - { - "name": "minSalePriceInWei", - "type": "uint256" - }, - { - "name": "toAddress", - "type": "address" - } - ], - "name": "offerPunkForSaleToAddress", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "punksRemainingToAssign", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - }, - { - "name": "minSalePriceInWei", - "type": "uint256" - } - ], - "name": "offerPunkForSale", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "getPunk", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "pendingWithdrawals", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "punkNoLongerForSale", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "inputs": [], - "payable": true, - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "to", - "type": "address" - }, - { - "indexed": false, - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "Assign", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, - { - "indexed": false, - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "PunkTransfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "punkIndex", - "type": "uint256" - }, - { - "indexed": false, - "name": "minValue", - "type": "uint256" - }, - { - "indexed": true, - "name": "toAddress", - "type": "address" - } - ], - "name": "PunkOffered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "punkIndex", - "type": "uint256" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - }, - { - "indexed": true, - "name": "fromAddress", - "type": "address" - } - ], - "name": "PunkBidEntered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "punkIndex", - "type": "uint256" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - }, - { - "indexed": true, - "name": "fromAddress", - "type": "address" - } - ], - "name": "PunkBidWithdrawn", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "punkIndex", - "type": "uint256" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - }, - { - "indexed": true, - "name": "fromAddress", - "type": "address" - }, - { - "indexed": true, - "name": "toAddress", - "type": "address" - } - ], - "name": "PunkBought", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "punkIndex", - "type": "uint256" - } - ], - "name": "PunkNoLongerForSale", - "type": "event" - } -] diff --git a/test/integration-tests/shared/abis/Element.json b/test/integration-tests/shared/abis/Element.json deleted file mode 100644 index 3779a5cb..00000000 --- a/test/integration-tests/shared/abis/Element.json +++ /dev/null @@ -1,1022 +0,0 @@ -[ - { - "inputs": [{ "internalType": "contract IEtherToken", "name": "weth", "type": "address" }], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "indexed": false, "internalType": "address", "name": "maker", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "taker", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "indexed": false, "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "indexed": false, - "internalType": "struct INFTOrdersFeature.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "indexed": false, "internalType": "address", "name": "erc721Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc721TokenId", "type": "uint256" } - ], - "name": "ERC721BuyOrderFilled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "maker", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "taker", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "indexed": false, "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "indexed": false, - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "indexed": false, "internalType": "address", "name": "erc721Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc721TokenId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "indexed": false, - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "name": "ERC721BuyOrderPreSigned", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "maker", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" } - ], - "name": "ERC721OrderCancelled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "indexed": false, "internalType": "address", "name": "maker", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "taker", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "indexed": false, "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "indexed": false, - "internalType": "struct INFTOrdersFeature.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "indexed": false, "internalType": "address", "name": "erc721Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc721TokenId", "type": "uint256" } - ], - "name": "ERC721SellOrderFilled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "maker", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "taker", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "indexed": false, "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "indexed": false, - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "indexed": false, "internalType": "address", "name": "erc721Token", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "erc721TokenId", "type": "uint256" } - ], - "name": "ERC721SellOrderPreSigned", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "maker", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "newHashNonce", "type": "uint256" } - ], - "name": "HashNonceIncremented", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "indexed": false, "internalType": "bytes", "name": "takerData", "type": "bytes" } - ], - "name": "TakerDataEmitted", - "type": "event" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder[]", - "name": "sellOrders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature[]", - "name": "signatures", - "type": "tuple[]" - }, - { "internalType": "bool", "name": "revertIfIncomplete", "type": "bool" } - ], - "name": "batchBuyERC721s", - "outputs": [{ "internalType": "bool[]", "name": "successes", "type": "bool[]" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder[]", - "name": "sellOrders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature[]", - "name": "signatures", - "type": "tuple[]" - }, - { "internalType": "address[]", "name": "takers", "type": "address[]" }, - { "internalType": "bytes[]", "name": "takerDatas", "type": "bytes[]" }, - { "internalType": "bool", "name": "revertIfIncomplete", "type": "bool" } - ], - "name": "batchBuyERC721sEx", - "outputs": [{ "internalType": "bool[]", "name": "successes", "type": "bool[]" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256[]", "name": "orderNonces", "type": "uint256[]" }], - "name": "batchCancelERC721Orders", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder[]", - "name": "sellOrders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder[]", - "name": "buyOrders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature[]", - "name": "sellOrderSignatures", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature[]", - "name": "buyOrderSignatures", - "type": "tuple[]" - } - ], - "name": "batchMatchERC721Orders", - "outputs": [ - { "internalType": "uint256[]", "name": "profits", "type": "uint256[]" }, - { "internalType": "bool[]", "name": "successes", "type": "bool[]" } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "sellOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - } - ], - "name": "buyERC721", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "sellOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "bytes", "name": "takerData", "type": "bytes" } - ], - "name": "buyERC721Ex", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "sellOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "ethAvailable", "type": "uint256" }, - { "internalType": "bytes", "name": "takerData", "type": "bytes" } - ], - "name": "buyERC721ExFromProxy", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "sellOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - } - ], - "name": "buyERC721FromProxy", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "orderNonce", "type": "uint256" }], - "name": "cancelERC721Order", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder", - "name": "order", - "type": "tuple" - } - ], - "name": "getERC721BuyOrderHash", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder", - "name": "order", - "type": "tuple" - } - ], - "name": "getERC721BuyOrderStatus", - "outputs": [{ "internalType": "enum LibNFTOrder.OrderStatus", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "uint248", "name": "nonceRange", "type": "uint248" } - ], - "name": "getERC721OrderStatusBitVector", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "order", - "type": "tuple" - } - ], - "name": "getERC721SellOrderHash", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "order", - "type": "tuple" - } - ], - "name": "getERC721SellOrderStatus", - "outputs": [{ "internalType": "enum LibNFTOrder.OrderStatus", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "maker", "type": "address" }], - "name": "getHashNonce", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { "inputs": [], "name": "incrementHashNonce", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "sellOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder", - "name": "buyOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "sellOrderSignature", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "buyOrderSignature", - "type": "tuple" - } - ], - "name": "matchERC721Orders", - "outputs": [{ "internalType": "uint256", "name": "profit", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "operator", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "bytes", "name": "data", "type": "bytes" } - ], - "name": "onERC721Received", - "outputs": [{ "internalType": "bytes4", "name": "success", "type": "bytes4" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder", - "name": "order", - "type": "tuple" - } - ], - "name": "preSignERC721BuyOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "order", - "type": "tuple" - } - ], - "name": "preSignERC721SellOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder", - "name": "buyOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - }, - { "internalType": "uint256", "name": "erc721TokenId", "type": "uint256" }, - { "internalType": "bool", "name": "unwrapNativeToken", "type": "bool" }, - { "internalType": "bytes", "name": "takerData", "type": "bytes" } - ], - "name": "sellERC721", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" }, - { - "components": [ - { "internalType": "contract IPropertyValidator", "name": "propertyValidator", "type": "address" }, - { "internalType": "bytes", "name": "propertyData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Property[]", - "name": "nftProperties", - "type": "tuple[]" - } - ], - "internalType": "struct LibNFTOrder.NFTBuyOrder", - "name": "order", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - } - ], - "name": "validateERC721BuyOrderSignature", - "outputs": [], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "maker", "type": "address" }, - { "internalType": "address", "name": "taker", "type": "address" }, - { "internalType": "uint256", "name": "expiry", "type": "uint256" }, - { "internalType": "uint256", "name": "nonce", "type": "uint256" }, - { "internalType": "contract IERC20", "name": "erc20Token", "type": "address" }, - { "internalType": "uint256", "name": "erc20TokenAmount", "type": "uint256" }, - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes", "name": "feeData", "type": "bytes" } - ], - "internalType": "struct LibNFTOrder.Fee[]", - "name": "fees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "nft", "type": "address" }, - { "internalType": "uint256", "name": "nftId", "type": "uint256" } - ], - "internalType": "struct LibNFTOrder.NFTSellOrder", - "name": "order", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum LibSignature.SignatureType", "name": "signatureType", "type": "uint8" }, - { "internalType": "uint8", "name": "v", "type": "uint8" }, - { "internalType": "bytes32", "name": "r", "type": "bytes32" }, - { "internalType": "bytes32", "name": "s", "type": "bytes32" } - ], - "internalType": "struct LibSignature.Signature", - "name": "signature", - "type": "tuple" - } - ], - "name": "validateERC721SellOrderSignature", - "outputs": [], - "stateMutability": "view", - "type": "function" - } -] diff --git a/test/integration-tests/shared/abis/Foundation.json b/test/integration-tests/shared/abis/Foundation.json deleted file mode 100644 index d57f731c..00000000 --- a/test/integration-tests/shared/abis/Foundation.json +++ /dev/null @@ -1,1581 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address payable", - "name": "treasury", - "type": "address" - }, - { - "internalType": "address", - "name": "feth", - "type": "address" - }, - { - "internalType": "address", - "name": "royaltyRegistry", - "type": "address" - }, - { - "internalType": "uint256", - "name": "duration", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "FoundationTreasuryNode_Address_Is_Not_A_Contract", - "type": "error" - }, - { - "inputs": [], - "name": "FoundationTreasuryNode_Caller_Not_Admin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "buyPrice", - "type": "uint256" - } - ], - "name": "NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketBuyPrice_Cannot_Buy_Unset_Price", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketBuyPrice_Cannot_Cancel_Unset_Price", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "NFTMarketBuyPrice_Only_Owner_Can_Set_Price", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketBuyPrice_Price_Already_Set", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketBuyPrice_Price_Too_High", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "seller", - "type": "address" - } - ], - "name": "NFTMarketBuyPrice_Seller_Mismatch", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketCore_FETH_Address_Is_Not_A_Contract", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketCore_Only_FETH_Can_Transfer_ETH", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketCore_Seller_Not_Found", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketFees_Address_Does_Not_Support_IRoyaltyRegistry", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketOffer_Cannot_Be_Made_While_In_Auction", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "currentOfferAmount", - "type": "uint256" - } - ], - "name": "NFTMarketOffer_Offer_Below_Min_Amount", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expiry", - "type": "uint256" - } - ], - "name": "NFTMarketOffer_Offer_Expired", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "currentOfferFrom", - "type": "address" - } - ], - "name": "NFTMarketOffer_Offer_From_Does_Not_Match", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "minOfferAmount", - "type": "uint256" - } - ], - "name": "NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketOffer_Reason_Required", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Already_Listed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "minAmount", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "reservePrice", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "maxDuration", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Exceeds_Max_Duration", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "extensionDuration", - "type": "uint256" - } - ], - "name": "NFTMarketReserveAuction_Less_Than_Extension_Duration", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "seller", - "type": "address" - } - ], - "name": "NFTMarketReserveAuction_Not_Matching_Seller", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "NFTMarketReserveAuction_Only_Owner_Can_Update_Auction", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Price_Already_Set", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMarketReserveAuction_Too_Much_Value_Provided", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "seller", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "buyer", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "protocolFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "creatorFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "sellerRev", - "type": "uint256" - } - ], - "name": "BuyPriceAccepted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "BuyPriceCanceled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "BuyPriceInvalidated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "seller", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "price", - "type": "uint256" - } - ], - "name": "BuyPriceSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "buyReferrer", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "buyReferrerProtocolFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "buyReferrerSellerFee", - "type": "uint256" - } - ], - "name": "BuyReferralPaid", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "buyer", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "seller", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "protocolFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "creatorFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "sellerRev", - "type": "uint256" - } - ], - "name": "OfferAccepted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "string", - "name": "reason", - "type": "string" - } - ], - "name": "OfferCanceledByAdmin", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "OfferInvalidated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "buyer", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "expiration", - "type": "uint256" - } - ], - "name": "OfferMade", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "bidder", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - } - ], - "name": "ReserveAuctionBidPlaced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "ReserveAuctionCanceled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "string", - "name": "reason", - "type": "string" - } - ], - "name": "ReserveAuctionCanceledByAdmin", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "seller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "duration", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "extensionDuration", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "reservePrice", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "ReserveAuctionCreated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "seller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "bidder", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "protocolFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "creatorFee", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "sellerRev", - "type": "uint256" - } - ], - "name": "ReserveAuctionFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "ReserveAuctionInvalidated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "reservePrice", - "type": "uint256" - } - ], - "name": "ReserveAuctionUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "WithdrawalToFETH", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "address", - "name": "offerFrom", - "type": "address" - }, - { - "internalType": "uint256", - "name": "minAmount", - "type": "uint256" - } - ], - "name": "acceptOffer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "nftContracts", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "tokenIds", - "type": "uint256[]" - }, - { - "internalType": "string", - "name": "reason", - "type": "string" - } - ], - "name": "adminCancelOffers", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "internalType": "string", - "name": "reason", - "type": "string" - } - ], - "name": "adminCancelReserveAuction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPrice", - "type": "uint256" - } - ], - "name": "buy", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPrice", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "referrer", - "type": "address" - } - ], - "name": "buyV2", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "cancelBuyPrice", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "cancelReserveAuction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reservePrice", - "type": "uint256" - } - ], - "name": "createReserveAuction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "finalizeReserveAuction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getBuyPrice", - "outputs": [ - { - "internalType": "address", - "name": "seller", - "type": "address" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - } - ], - "name": "getFeesAndRecipients", - "outputs": [ - { - "internalType": "uint256", - "name": "protocolFee", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "creatorRev", - "type": "uint256" - }, - { - "internalType": "address payable[]", - "name": "creatorRecipients", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "creatorShares", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "sellerRev", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "owner", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getFethAddress", - "outputs": [ - { - "internalType": "address", - "name": "fethAddress", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getFoundationTreasury", - "outputs": [ - { - "internalType": "address payable", - "name": "treasuryAddress", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getImmutableRoyalties", - "outputs": [ - { - "internalType": "address payable[]", - "name": "recipients", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "splitPerRecipientInBasisPoints", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "getMinBidAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "minimum", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getMinOfferAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "minimum", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "creator", - "type": "address" - } - ], - "name": "getMutableRoyalties", - "outputs": [ - { - "internalType": "address payable[]", - "name": "recipients", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "splitPerRecipientInBasisPoints", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getOffer", - "outputs": [ - { - "internalType": "address", - "name": "buyer", - "type": "address" - }, - { - "internalType": "uint256", - "name": "expiration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getOfferReferrer", - "outputs": [ - { - "internalType": "address payable", - "name": "referrer", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "getReserveAuction", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "seller", - "type": "address" - }, - { - "internalType": "uint256", - "name": "duration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "extensionDuration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "bidder", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "internalType": "struct NFTMarketReserveAuction.ReserveAuction", - "name": "auction", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "getReserveAuctionBidReferrer", - "outputs": [ - { - "internalType": "address payable", - "name": "referrer", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getReserveAuctionIdFor", - "outputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getRoyaltyRegistry", - "outputs": [ - { - "internalType": "address", - "name": "registry", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "getTokenCreator", - "outputs": [ - { - "internalType": "address payable", - "name": "creator", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "makeOffer", - "outputs": [ - { - "internalType": "uint256", - "name": "expiration", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "referrer", - "type": "address" - } - ], - "name": "makeOfferV2", - "outputs": [ - { - "internalType": "uint256", - "name": "expiration", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - } - ], - "name": "placeBid", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "referrer", - "type": "address" - } - ], - "name": "placeBidV2", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "nftContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - } - ], - "name": "setBuyPrice", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "auctionId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reservePrice", - "type": "uint256" - } - ], - "name": "updateReserveAuction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] diff --git a/test/integration-tests/shared/abis/LooksRareV2.json b/test/integration-tests/shared/abis/LooksRareV2.json deleted file mode 100644 index 1f402a70..00000000 --- a/test/integration-tests/shared/abis/LooksRareV2.json +++ /dev/null @@ -1,777 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "_owner", "type": "address" }, - { "internalType": "address", "name": "_protocolFeeRecipient", "type": "address" }, - { "internalType": "address", "name": "_transferManager", "type": "address" }, - { "internalType": "address", "name": "_weth", "type": "address" } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { "inputs": [], "name": "CallerInvalid", "type": "error" }, - { "inputs": [], "name": "ChainIdInvalid", "type": "error" }, - { "inputs": [], "name": "CreatorFeeBpTooHigh", "type": "error" }, - { "inputs": [], "name": "CurrencyInvalid", "type": "error" }, - { "inputs": [], "name": "ERC20TransferFromFail", "type": "error" }, - { "inputs": [], "name": "LengthsInvalid", "type": "error" }, - { "inputs": [], "name": "MerkleProofInvalid", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "length", "type": "uint256" }], - "name": "MerkleProofTooLarge", - "type": "error" - }, - { "inputs": [], "name": "NewGasLimitETHTransferTooLow", "type": "error" }, - { "inputs": [], "name": "NewProtocolFeeRecipientCannotBeNullAddress", "type": "error" }, - { "inputs": [], "name": "NoOngoingTransferInProgress", "type": "error" }, - { "inputs": [], "name": "NoSelectorForStrategy", "type": "error" }, - { "inputs": [], "name": "NoncesInvalid", "type": "error" }, - { "inputs": [], "name": "NotAContract", "type": "error" }, - { "inputs": [], "name": "NotAffiliateController", "type": "error" }, - { "inputs": [], "name": "NotOwner", "type": "error" }, - { "inputs": [], "name": "NotV2Strategy", "type": "error" }, - { "inputs": [], "name": "NullSignerAddress", "type": "error" }, - { "inputs": [], "name": "OutsideOfTimeRange", "type": "error" }, - { "inputs": [], "name": "PercentageTooHigh", "type": "error" }, - { "inputs": [], "name": "QuoteTypeInvalid", "type": "error" }, - { "inputs": [], "name": "ReentrancyFail", "type": "error" }, - { "inputs": [], "name": "RenouncementNotInProgress", "type": "error" }, - { "inputs": [], "name": "SameDomainSeparator", "type": "error" }, - { "inputs": [], "name": "SignatureEOAInvalid", "type": "error" }, - { "inputs": [], "name": "SignatureERC1271Invalid", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "length", "type": "uint256" }], - "name": "SignatureLengthInvalid", - "type": "error" - }, - { "inputs": [], "name": "SignatureParameterSInvalid", "type": "error" }, - { - "inputs": [{ "internalType": "uint8", "name": "v", "type": "uint8" }], - "name": "SignatureParameterVInvalid", - "type": "error" - }, - { "inputs": [], "name": "StrategyHasNoSelector", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "strategyId", "type": "uint256" }], - "name": "StrategyNotAvailable", - "type": "error" - }, - { "inputs": [], "name": "StrategyNotUsed", "type": "error" }, - { "inputs": [], "name": "StrategyProtocolFeeTooHigh", "type": "error" }, - { "inputs": [], "name": "TransferAlreadyInProgress", "type": "error" }, - { "inputs": [], "name": "TransferNotInProgress", "type": "error" }, - { "inputs": [], "name": "WrongPotentialOwner", "type": "error" }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "affiliate", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "currency", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "affiliateFee", "type": "uint256" } - ], - "name": "AffiliatePayment", - "type": "event" - }, - { "anonymous": false, "inputs": [], "name": "CancelOwnershipTransfer", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "currency", "type": "address" }, - { "indexed": false, "internalType": "bool", "name": "isAllowed", "type": "bool" } - ], - "name": "CurrencyStatusUpdated", - "type": "event" - }, - { "anonymous": false, "inputs": [], "name": "InitiateOwnershipRenouncement", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "previousOwner", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "potentialOwner", "type": "address" } - ], - "name": "InitiateOwnershipTransfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "address", "name": "affiliateController", "type": "address" }], - "name": "NewAffiliateController", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "bool", "name": "isActive", "type": "bool" }], - "name": "NewAffiliateProgramStatus", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "affiliate", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "rate", "type": "uint256" } - ], - "name": "NewAffiliateRate", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "bidNonce", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "askNonce", "type": "uint256" } - ], - "name": "NewBidAskNonces", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "address", "name": "creatorFeeManager", "type": "address" }], - "name": "NewCreatorFeeManager", - "type": "event" - }, - { "anonymous": false, "inputs": [], "name": "NewDomainSeparator", "type": "event" }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "uint256", "name": "gasLimitETHTransfer", "type": "uint256" }], - "name": "NewGasLimitETHTransfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "uint256", "name": "maxCreatorFeeBp", "type": "uint256" }], - "name": "NewMaxCreatorFeeBp", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "address", "name": "newOwner", "type": "address" }], - "name": "NewOwner", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "address", "name": "protocolFeeRecipient", "type": "address" }], - "name": "NewProtocolFeeRecipient", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "indexed": false, "internalType": "uint16", "name": "standardProtocolFeeBp", "type": "uint16" }, - { "indexed": false, "internalType": "uint16", "name": "minTotalFeeBp", "type": "uint16" }, - { "indexed": false, "internalType": "uint16", "name": "maxProtocolFeeBp", "type": "uint16" }, - { "indexed": false, "internalType": "bytes4", "name": "selector", "type": "bytes4" }, - { "indexed": false, "internalType": "bool", "name": "isMakerBid", "type": "bool" }, - { "indexed": false, "internalType": "address", "name": "implementation", "type": "address" } - ], - "name": "NewStrategy", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": false, "internalType": "uint256[]", "name": "orderNonces", "type": "uint256[]" } - ], - "name": "OrderNoncesCancelled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "indexed": false, "internalType": "bool", "name": "isActive", "type": "bool" }, - { "indexed": false, "internalType": "uint16", "name": "standardProtocolFeeBp", "type": "uint16" }, - { "indexed": false, "internalType": "uint16", "name": "minTotalFeeBp", "type": "uint16" } - ], - "name": "StrategyUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": false, "internalType": "uint256[]", "name": "subsetNonces", "type": "uint256[]" } - ], - "name": "SubsetNoncesCancelled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "components": [ - { "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "orderNonce", "type": "uint256" }, - { "internalType": "bool", "name": "isNonceInvalidated", "type": "bool" } - ], - "indexed": false, - "internalType": "struct ILooksRareProtocol.NonceInvalidationParameters", - "name": "nonceInvalidationParameters", - "type": "tuple" - }, - { "indexed": false, "internalType": "address", "name": "askUser", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "bidUser", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "currency", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "collection", "type": "address" }, - { "indexed": false, "internalType": "uint256[]", "name": "itemIds", "type": "uint256[]" }, - { "indexed": false, "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "indexed": false, "internalType": "address[2]", "name": "feeRecipients", "type": "address[2]" }, - { "indexed": false, "internalType": "uint256[3]", "name": "feeAmounts", "type": "uint256[3]" } - ], - "name": "TakerAsk", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "components": [ - { "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "orderNonce", "type": "uint256" }, - { "internalType": "bool", "name": "isNonceInvalidated", "type": "bool" } - ], - "indexed": false, - "internalType": "struct ILooksRareProtocol.NonceInvalidationParameters", - "name": "nonceInvalidationParameters", - "type": "tuple" - }, - { "indexed": false, "internalType": "address", "name": "bidUser", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "bidRecipient", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "currency", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "collection", "type": "address" }, - { "indexed": false, "internalType": "uint256[]", "name": "itemIds", "type": "uint256[]" }, - { "indexed": false, "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "indexed": false, "internalType": "address[2]", "name": "feeRecipients", "type": "address[2]" }, - { "indexed": false, "internalType": "uint256[3]", "name": "feeAmounts", "type": "uint256[3]" } - ], - "name": "TakerBid", - "type": "event" - }, - { - "inputs": [], - "name": "MAGIC_VALUE_ORDER_NONCE_EXECUTED", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "WETH", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint16", "name": "standardProtocolFeeBp", "type": "uint16" }, - { "internalType": "uint16", "name": "minTotalFeeBp", "type": "uint16" }, - { "internalType": "uint16", "name": "maxProtocolFeeBp", "type": "uint16" }, - { "internalType": "bytes4", "name": "selector", "type": "bytes4" }, - { "internalType": "bool", "name": "isMakerBid", "type": "bool" }, - { "internalType": "address", "name": "implementation", "type": "address" } - ], - "name": "addStrategy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "affiliateController", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], - "name": "affiliateRates", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256[]", "name": "orderNonces", "type": "uint256[]" }], - "name": "cancelOrderNonces", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "cancelOwnershipTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256[]", "name": "subsetNonces", "type": "uint256[]" }], - "name": "cancelSubsetNonces", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "chainId", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "confirmOwnershipRenouncement", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "confirmOwnershipTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "creatorFeeManager", - "outputs": [{ "internalType": "contract ICreatorFeeManager", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "domainSeparator", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Taker[]", - "name": "takerBids", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enum QuoteType", "name": "quoteType", "type": "uint8" }, - { "internalType": "uint256", "name": "globalNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "subsetNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "orderNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "internalType": "enum CollectionType", "name": "collectionType", "type": "uint8" }, - { "internalType": "address", "name": "collection", "type": "address" }, - { "internalType": "address", "name": "currency", "type": "address" }, - { "internalType": "address", "name": "signer", "type": "address" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "uint256", "name": "price", "type": "uint256" }, - { "internalType": "uint256[]", "name": "itemIds", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Maker[]", - "name": "makerAsks", - "type": "tuple[]" - }, - { "internalType": "bytes[]", "name": "makerSignatures", "type": "bytes[]" }, - { - "components": [ - { "internalType": "bytes32", "name": "root", "type": "bytes32" }, - { - "components": [ - { "internalType": "bytes32", "name": "value", "type": "bytes32" }, - { "internalType": "enum OrderStructs.MerkleTreeNodePosition", "name": "position", "type": "uint8" } - ], - "internalType": "struct OrderStructs.MerkleTreeNode[]", - "name": "proof", - "type": "tuple[]" - } - ], - "internalType": "struct OrderStructs.MerkleTree[]", - "name": "merkleTrees", - "type": "tuple[]" - }, - { "internalType": "address", "name": "affiliate", "type": "address" }, - { "internalType": "bool", "name": "isAtomic", "type": "bool" } - ], - "name": "executeMultipleTakerBids", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Taker", - "name": "takerAsk", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum QuoteType", "name": "quoteType", "type": "uint8" }, - { "internalType": "uint256", "name": "globalNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "subsetNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "orderNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "internalType": "enum CollectionType", "name": "collectionType", "type": "uint8" }, - { "internalType": "address", "name": "collection", "type": "address" }, - { "internalType": "address", "name": "currency", "type": "address" }, - { "internalType": "address", "name": "signer", "type": "address" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "uint256", "name": "price", "type": "uint256" }, - { "internalType": "uint256[]", "name": "itemIds", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Maker", - "name": "makerBid", - "type": "tuple" - }, - { "internalType": "bytes", "name": "makerSignature", "type": "bytes" }, - { - "components": [ - { "internalType": "bytes32", "name": "root", "type": "bytes32" }, - { - "components": [ - { "internalType": "bytes32", "name": "value", "type": "bytes32" }, - { "internalType": "enum OrderStructs.MerkleTreeNodePosition", "name": "position", "type": "uint8" } - ], - "internalType": "struct OrderStructs.MerkleTreeNode[]", - "name": "proof", - "type": "tuple[]" - } - ], - "internalType": "struct OrderStructs.MerkleTree", - "name": "merkleTree", - "type": "tuple" - }, - { "internalType": "address", "name": "affiliate", "type": "address" } - ], - "name": "executeTakerAsk", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Taker", - "name": "takerBid", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum QuoteType", "name": "quoteType", "type": "uint8" }, - { "internalType": "uint256", "name": "globalNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "subsetNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "orderNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "internalType": "enum CollectionType", "name": "collectionType", "type": "uint8" }, - { "internalType": "address", "name": "collection", "type": "address" }, - { "internalType": "address", "name": "currency", "type": "address" }, - { "internalType": "address", "name": "signer", "type": "address" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "uint256", "name": "price", "type": "uint256" }, - { "internalType": "uint256[]", "name": "itemIds", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Maker", - "name": "makerAsk", - "type": "tuple" - }, - { "internalType": "bytes", "name": "makerSignature", "type": "bytes" }, - { - "components": [ - { "internalType": "bytes32", "name": "root", "type": "bytes32" }, - { - "components": [ - { "internalType": "bytes32", "name": "value", "type": "bytes32" }, - { "internalType": "enum OrderStructs.MerkleTreeNodePosition", "name": "position", "type": "uint8" } - ], - "internalType": "struct OrderStructs.MerkleTreeNode[]", - "name": "proof", - "type": "tuple[]" - } - ], - "internalType": "struct OrderStructs.MerkleTree", - "name": "merkleTree", - "type": "tuple" - }, - { "internalType": "address", "name": "affiliate", "type": "address" } - ], - "name": "executeTakerBid", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes32", "name": "root", "type": "bytes32" }, - { "internalType": "uint256", "name": "proofLength", "type": "uint256" } - ], - "name": "hashBatchOrder", - "outputs": [{ "internalType": "bytes32", "name": "batchOrderHash", "type": "bytes32" }], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bool", "name": "bid", "type": "bool" }, - { "internalType": "bool", "name": "ask", "type": "bool" } - ], - "name": "incrementBidAskNonces", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "initiateOwnershipRenouncement", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "newPotentialOwner", "type": "address" }], - "name": "initiateOwnershipTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "isAffiliateProgramActive", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], - "name": "isCurrencyAllowed", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxCreatorFeeBp", - "outputs": [{ "internalType": "uint16", "name": "", "type": "uint16" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "ownershipStatus", - "outputs": [{ "internalType": "enum IOwnableTwoSteps.Status", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "potentialOwner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "protocolFeeRecipient", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Taker", - "name": "takerBid", - "type": "tuple" - }, - { - "components": [ - { "internalType": "enum QuoteType", "name": "quoteType", "type": "uint8" }, - { "internalType": "uint256", "name": "globalNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "subsetNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "orderNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "internalType": "enum CollectionType", "name": "collectionType", "type": "uint8" }, - { "internalType": "address", "name": "collection", "type": "address" }, - { "internalType": "address", "name": "currency", "type": "address" }, - { "internalType": "address", "name": "signer", "type": "address" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "uint256", "name": "price", "type": "uint256" }, - { "internalType": "uint256[]", "name": "itemIds", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "internalType": "bytes", "name": "additionalParameters", "type": "bytes" } - ], - "internalType": "struct OrderStructs.Maker", - "name": "makerAsk", - "type": "tuple" - }, - { "internalType": "address", "name": "sender", "type": "address" }, - { "internalType": "bytes32", "name": "orderHash", "type": "bytes32" } - ], - "name": "restrictedExecuteTakerBid", - "outputs": [{ "internalType": "uint256", "name": "protocolFeeAmount", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "name": "strategyInfo", - "outputs": [ - { "internalType": "bool", "name": "isActive", "type": "bool" }, - { "internalType": "uint16", "name": "standardProtocolFeeBp", "type": "uint16" }, - { "internalType": "uint16", "name": "minTotalFeeBp", "type": "uint16" }, - { "internalType": "uint16", "name": "maxProtocolFeeBp", "type": "uint16" }, - { "internalType": "bytes4", "name": "selector", "type": "bytes4" }, - { "internalType": "bool", "name": "isMakerBid", "type": "bool" }, - { "internalType": "address", "name": "implementation", "type": "address" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "transferManager", - "outputs": [{ "internalType": "contract TransferManager", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "newAffiliateController", "type": "address" }], - "name": "updateAffiliateController", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "isActive", "type": "bool" }], - "name": "updateAffiliateProgramStatus", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "affiliate", "type": "address" }, - { "internalType": "uint256", "name": "bp", "type": "uint256" } - ], - "name": "updateAffiliateRate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "newCreatorFeeManager", "type": "address" }], - "name": "updateCreatorFeeManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "currency", "type": "address" }, - { "internalType": "bool", "name": "isAllowed", "type": "bool" } - ], - "name": "updateCurrencyStatus", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { "inputs": [], "name": "updateDomainSeparator", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [{ "internalType": "uint256", "name": "newGasLimitETHTransfer", "type": "uint256" }], - "name": "updateETHGasLimitForTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint16", "name": "newMaxCreatorFeeBp", "type": "uint16" }], - "name": "updateMaxCreatorFeeBp", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "newProtocolFeeRecipient", "type": "address" }], - "name": "updateProtocolFeeRecipient", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "strategyId", "type": "uint256" }, - { "internalType": "bool", "name": "isActive", "type": "bool" }, - { "internalType": "uint16", "name": "newStandardProtocolFee", "type": "uint16" }, - { "internalType": "uint16", "name": "newMinTotalFee", "type": "uint16" } - ], - "name": "updateStrategy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], - "name": "userBidAskNonces", - "outputs": [ - { "internalType": "uint256", "name": "bidNonce", "type": "uint256" }, - { "internalType": "uint256", "name": "askNonce", "type": "uint256" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "uint256", "name": "", "type": "uint256" } - ], - "name": "userOrderNonce", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "uint256", "name": "", "type": "uint256" } - ], - "name": "userSubsetNonce", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - } -] diff --git a/test/integration-tests/shared/abis/NFT20.json b/test/integration-tests/shared/abis/NFT20.json deleted file mode 100644 index 2749ec8d..00000000 --- a/test/integration-tests/shared/abis/NFT20.json +++ /dev/null @@ -1,242 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "inputs": [], - "name": "ETH", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "NFT20", - "outputs": [ - { - "internalType": "contract INFT20Factory", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "UNIV2", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "UNIV3", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "WETH", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_nft", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "_toIds", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "_toAmounts", - "type": "uint256[]" - }, - { - "internalType": "address", - "name": "_receipient", - "type": "address" - }, - { - "internalType": "uint24", - "name": "_fee", - "type": "uint24" - }, - { - "internalType": "bool", - "name": "isV3", - "type": "bool" - } - ], - "name": "ethForNft", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_nft", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "_ids", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "_amounts", - "type": "uint256[]" - }, - { - "internalType": "bool", - "name": "isErc721", - "type": "bool" - }, - { - "internalType": "uint24", - "name": "_fee", - "type": "uint24" - }, - { - "internalType": "bool", - "name": "isV3", - "type": "bool" - } - ], - "name": "nftForEth", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenAmount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sendTo", - "type": "address" - } - ], - "name": "recoverERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_registry", - "type": "address" - } - ], - "name": "setNFT20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "withdrawEth", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] diff --git a/test/integration-tests/shared/abis/NFTXZap.json b/test/integration-tests/shared/abis/NFTXZap.json deleted file mode 100644 index d8872299..00000000 --- a/test/integration-tests/shared/abis/NFTXZap.json +++ /dev/null @@ -1,250 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "_nftxFactory", "type": "address" }, - { "internalType": "address", "name": "_WETH", "type": "address" }, - { "internalType": "address payable", "name": "_swapTarget", "type": "address" }, - { "internalType": "uint256", "name": "_dustThreshold", "type": "uint256" } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "count", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "ethSpent", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "to", "type": "address" } - ], - "name": "Buy", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "ethAmount", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "vTokenAmount", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "to", "type": "address" } - ], - "name": "DustReturned", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "count", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "ethReceived", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "to", "type": "address" } - ], - "name": "Sell", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "count", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "ethSpent", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "to", "type": "address" } - ], - "name": "Swap", - "type": "event" - }, - { - "inputs": [], - "name": "WETH", - "outputs": [{ "internalType": "contract IWETH", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "vaultId", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "uint256[]", "name": "specificIds", "type": "uint256[]" }, - { "internalType": "bytes", "name": "swapCallData", "type": "bytes" }, - { "internalType": "address payable", "name": "to", "type": "address" } - ], - "name": "buyAndRedeem", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "vaultId", "type": "uint256" }, - { "internalType": "uint256[]", "name": "idsIn", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "specificIds", "type": "uint256[]" }, - { "internalType": "bytes", "name": "swapCallData", "type": "bytes" }, - { "internalType": "address payable", "name": "to", "type": "address" } - ], - "name": "buyAndSwap1155", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "vaultId", "type": "uint256" }, - { "internalType": "uint256[]", "name": "idsIn", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "specificIds", "type": "uint256[]" }, - { "internalType": "bytes", "name": "swapCallData", "type": "bytes" }, - { "internalType": "address payable", "name": "to", "type": "address" } - ], - "name": "buyAndSwap721", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "dustThreshold", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "feeDistributor", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "vaultId", "type": "uint256" }, - { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }, - { "internalType": "bytes", "name": "swapCallData", "type": "bytes" }, - { "internalType": "address payable", "name": "to", "type": "address" } - ], - "name": "mintAndSell1155", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "vaultId", "type": "uint256" }, - { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }, - { "internalType": "bytes", "name": "swapCallData", "type": "bytes" }, - { "internalType": "address payable", "name": "to", "type": "address" } - ], - "name": "mintAndSell721", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "nftxFactory", - "outputs": [{ "internalType": "contract INFTXVaultFactory", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "name": "nftxVaultAddresses", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, - { "internalType": "bytes", "name": "", "type": "bytes" } - ], - "name": "onERC1155BatchReceived", - "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "bytes", "name": "", "type": "bytes" } - ], - "name": "onERC1155Received", - "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "uint256", "name": "", "type": "uint256" }, - { "internalType": "bytes", "name": "", "type": "bytes" } - ], - "name": "onERC721Received", - "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "_paused", "type": "bool" }], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], - "name": "rescue", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "_dustThreshold", "type": "uint256" }], - "name": "setDustThreshold", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { "stateMutability": "payable", "type": "receive" } -] diff --git a/test/integration-tests/shared/abis/Seaport.json b/test/integration-tests/shared/abis/Seaport.json deleted file mode 100644 index a7b51041..00000000 --- a/test/integration-tests/shared/abis/Seaport.json +++ /dev/null @@ -1,1143 +0,0 @@ -[ - { - "inputs": [{ "internalType": "address", "name": "conduitController", "type": "address" }], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "counter", "type": "uint256" } - ], - "internalType": "structOrderComponents[]", - "name": "orders", - "type": "tuple[]" - } - ], - "name": "cancel", - "outputs": [{ "internalType": "bool", "name": "cancelled", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "uint120", "name": "numerator", "type": "uint120" }, - { "internalType": "uint120", "name": "denominator", "type": "uint120" }, - { "internalType": "bytes", "name": "signature", "type": "bytes" }, - { "internalType": "bytes", "name": "extraData", "type": "bytes" } - ], - "internalType": "structAdvancedOrder", - "name": "advancedOrder", - "type": "tuple" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "enumSide", "name": "side", "type": "uint8" }, - { "internalType": "uint256", "name": "index", "type": "uint256" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "bytes32[]", "name": "criteriaProof", "type": "bytes32[]" } - ], - "internalType": "structCriteriaResolver[]", - "name": "criteriaResolvers", - "type": "tuple[]" - }, - { "internalType": "bytes32", "name": "fulfillerConduitKey", "type": "bytes32" }, - { "internalType": "address", "name": "recipient", "type": "address" } - ], - "name": "fulfillAdvancedOrder", - "outputs": [{ "internalType": "bool", "name": "fulfilled", "type": "bool" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "uint120", "name": "numerator", "type": "uint120" }, - { "internalType": "uint120", "name": "denominator", "type": "uint120" }, - { "internalType": "bytes", "name": "signature", "type": "bytes" }, - { "internalType": "bytes", "name": "extraData", "type": "bytes" } - ], - "internalType": "structAdvancedOrder[]", - "name": "advancedOrders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "enumSide", "name": "side", "type": "uint8" }, - { "internalType": "uint256", "name": "index", "type": "uint256" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "bytes32[]", "name": "criteriaProof", "type": "bytes32[]" } - ], - "internalType": "structCriteriaResolver[]", - "name": "criteriaResolvers", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[][]", - "name": "offerFulfillments", - "type": "tuple[][]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[][]", - "name": "considerationFulfillments", - "type": "tuple[][]" - }, - { "internalType": "bytes32", "name": "fulfillerConduitKey", "type": "bytes32" }, - { "internalType": "address", "name": "recipient", "type": "address" }, - { "internalType": "uint256", "name": "maximumFulfilled", "type": "uint256" } - ], - "name": "fulfillAvailableAdvancedOrders", - "outputs": [ - { "internalType": "bool[]", "name": "availableOrders", "type": "bool[]" }, - { - "components": [ - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structReceivedItem", - "name": "item", - "type": "tuple" - }, - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" } - ], - "internalType": "structExecution[]", - "name": "executions", - "type": "tuple[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "bytes", "name": "signature", "type": "bytes" } - ], - "internalType": "structOrder[]", - "name": "orders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[][]", - "name": "offerFulfillments", - "type": "tuple[][]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[][]", - "name": "considerationFulfillments", - "type": "tuple[][]" - }, - { "internalType": "bytes32", "name": "fulfillerConduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "maximumFulfilled", "type": "uint256" } - ], - "name": "fulfillAvailableOrders", - "outputs": [ - { "internalType": "bool[]", "name": "availableOrders", "type": "bool[]" }, - { - "components": [ - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structReceivedItem", - "name": "item", - "type": "tuple" - }, - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" } - ], - "internalType": "structExecution[]", - "name": "executions", - "type": "tuple[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "considerationToken", "type": "address" }, - { "internalType": "uint256", "name": "considerationIdentifier", "type": "uint256" }, - { "internalType": "uint256", "name": "considerationAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { "internalType": "address", "name": "offerToken", "type": "address" }, - { "internalType": "uint256", "name": "offerIdentifier", "type": "uint256" }, - { "internalType": "uint256", "name": "offerAmount", "type": "uint256" }, - { "internalType": "enumBasicOrderType", "name": "basicOrderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "offererConduitKey", "type": "bytes32" }, - { "internalType": "bytes32", "name": "fulfillerConduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalAdditionalRecipients", "type": "uint256" }, - { - "components": [ - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structAdditionalRecipient[]", - "name": "additionalRecipients", - "type": "tuple[]" - }, - { "internalType": "bytes", "name": "signature", "type": "bytes" } - ], - "internalType": "structBasicOrderParameters", - "name": "parameters", - "type": "tuple" - } - ], - "name": "fulfillBasicOrder", - "outputs": [{ "internalType": "bool", "name": "fulfilled", "type": "bool" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "considerationToken", "type": "address" }, - { "internalType": "uint256", "name": "considerationIdentifier", "type": "uint256" }, - { "internalType": "uint256", "name": "considerationAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { "internalType": "address", "name": "offerToken", "type": "address" }, - { "internalType": "uint256", "name": "offerIdentifier", "type": "uint256" }, - { "internalType": "uint256", "name": "offerAmount", "type": "uint256" }, - { "internalType": "enumBasicOrderType", "name": "basicOrderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "offererConduitKey", "type": "bytes32" }, - { "internalType": "bytes32", "name": "fulfillerConduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalAdditionalRecipients", "type": "uint256" }, - { - "components": [ - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structAdditionalRecipient[]", - "name": "additionalRecipients", - "type": "tuple[]" - }, - { "internalType": "bytes", "name": "signature", "type": "bytes" } - ], - "internalType": "structBasicOrderParameters", - "name": "parameters", - "type": "tuple" - } - ], - "name": "fulfillBasicOrder_efficient_6GL6yc", - "outputs": [{ "internalType": "bool", "name": "fulfilled", "type": "bool" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "bytes", "name": "signature", "type": "bytes" } - ], - "internalType": "structOrder", - "name": "order", - "type": "tuple" - }, - { "internalType": "bytes32", "name": "fulfillerConduitKey", "type": "bytes32" } - ], - "name": "fulfillOrder", - "outputs": [{ "internalType": "bool", "name": "fulfilled", "type": "bool" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "contractOfferer", "type": "address" }], - "name": "getContractOffererNonce", - "outputs": [{ "internalType": "uint256", "name": "nonce", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "offerer", "type": "address" }], - "name": "getCounter", - "outputs": [{ "internalType": "uint256", "name": "counter", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "counter", "type": "uint256" } - ], - "internalType": "structOrderComponents", - "name": "order", - "type": "tuple" - } - ], - "name": "getOrderHash", - "outputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "name": "getOrderStatus", - "outputs": [ - { "internalType": "bool", "name": "isValidated", "type": "bool" }, - { "internalType": "bool", "name": "isCancelled", "type": "bool" }, - { "internalType": "uint256", "name": "totalFilled", "type": "uint256" }, - { "internalType": "uint256", "name": "totalSize", "type": "uint256" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "incrementCounter", - "outputs": [{ "internalType": "uint256", "name": "newCounter", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "information", - "outputs": [ - { "internalType": "string", "name": "version", "type": "string" }, - { "internalType": "bytes32", "name": "domainSeparator", "type": "bytes32" }, - { "internalType": "address", "name": "conduitController", "type": "address" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "uint120", "name": "numerator", "type": "uint120" }, - { "internalType": "uint120", "name": "denominator", "type": "uint120" }, - { "internalType": "bytes", "name": "signature", "type": "bytes" }, - { "internalType": "bytes", "name": "extraData", "type": "bytes" } - ], - "internalType": "structAdvancedOrder[]", - "name": "orders", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "enumSide", "name": "side", "type": "uint8" }, - { "internalType": "uint256", "name": "index", "type": "uint256" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "bytes32[]", "name": "criteriaProof", "type": "bytes32[]" } - ], - "internalType": "structCriteriaResolver[]", - "name": "criteriaResolvers", - "type": "tuple[]" - }, - { - "components": [ - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[]", - "name": "offerComponents", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[]", - "name": "considerationComponents", - "type": "tuple[]" - } - ], - "internalType": "structFulfillment[]", - "name": "fulfillments", - "type": "tuple[]" - }, - { "internalType": "address", "name": "recipient", "type": "address" } - ], - "name": "matchAdvancedOrders", - "outputs": [ - { - "components": [ - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structReceivedItem", - "name": "item", - "type": "tuple" - }, - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" } - ], - "internalType": "structExecution[]", - "name": "executions", - "type": "tuple[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "bytes", "name": "signature", "type": "bytes" } - ], - "internalType": "structOrder[]", - "name": "orders", - "type": "tuple[]" - }, - { - "components": [ - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[]", - "name": "offerComponents", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "itemIndex", "type": "uint256" } - ], - "internalType": "structFulfillmentComponent[]", - "name": "considerationComponents", - "type": "tuple[]" - } - ], - "internalType": "structFulfillment[]", - "name": "fulfillments", - "type": "tuple[]" - } - ], - "name": "matchOrders", - "outputs": [ - { - "components": [ - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structReceivedItem", - "name": "item", - "type": "tuple" - }, - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" } - ], - "internalType": "structExecution[]", - "name": "executions", - "type": "tuple[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "contractName", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "internalType": "structOrderParameters", - "name": "parameters", - "type": "tuple" - }, - { "internalType": "bytes", "name": "signature", "type": "bytes" } - ], - "internalType": "structOrder[]", - "name": "orders", - "type": "tuple[]" - } - ], - "name": "validate", - "outputs": [{ "internalType": "bool", "name": "validated", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { "inputs": [], "name": "BadContractSignature", "type": "error" }, - { "inputs": [], "name": "BadFraction", "type": "error" }, - { - "inputs": [ - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "BadReturnValueFromERC20OnTransfer", - "type": "error" - }, - { "inputs": [{ "internalType": "uint8", "name": "v", "type": "uint8" }], "name": "BadSignatureV", "type": "error" }, - { "inputs": [], "name": "CannotCancelOrder", "type": "error" }, - { "inputs": [], "name": "ConsiderationCriteriaResolverOutOfRange", "type": "error" }, - { "inputs": [], "name": "ConsiderationLengthNotEqualToTotalOriginal", "type": "error" }, - { - "inputs": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "considerationIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "shortfallAmount", "type": "uint256" } - ], - "name": "ConsiderationNotMet", - "type": "error" - }, - { "inputs": [], "name": "CriteriaNotEnabledForItem", "type": "error" }, - { - "inputs": [ - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256[]", "name": "identifiers", "type": "uint256[]" }, - { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" } - ], - "name": "ERC1155BatchTransferGenericFailure", - "type": "error" - }, - { "inputs": [], "name": "InexactFraction", "type": "error" }, - { "inputs": [], "name": "InsufficientNativeTokensSupplied", "type": "error" }, - { "inputs": [], "name": "Invalid1155BatchTransferEncoding", "type": "error" }, - { "inputs": [], "name": "InvalidBasicOrderParameterEncoding", "type": "error" }, - { - "inputs": [{ "internalType": "address", "name": "conduit", "type": "address" }], - "name": "InvalidCallToConduit", - "type": "error" - }, - { - "inputs": [ - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "address", "name": "conduit", "type": "address" } - ], - "name": "InvalidConduit", - "type": "error" - }, - { - "inputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "name": "InvalidContractOrder", - "type": "error" - }, - { - "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }], - "name": "InvalidERC721TransferAmount", - "type": "error" - }, - { "inputs": [], "name": "InvalidFulfillmentComponentData", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "value", "type": "uint256" }], - "name": "InvalidMsgValue", - "type": "error" - }, - { "inputs": [], "name": "InvalidNativeOfferItem", "type": "error" }, - { "inputs": [], "name": "InvalidProof", "type": "error" }, - { - "inputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "name": "InvalidRestrictedOrder", - "type": "error" - }, - { "inputs": [], "name": "InvalidSignature", "type": "error" }, - { "inputs": [], "name": "InvalidSigner", "type": "error" }, - { - "inputs": [ - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" } - ], - "name": "InvalidTime", - "type": "error" - }, - { - "inputs": [{ "internalType": "uint256", "name": "fulfillmentIndex", "type": "uint256" }], - "name": "MismatchedFulfillmentOfferAndConsiderationComponents", - "type": "error" - }, - { - "inputs": [{ "internalType": "enumSide", "name": "side", "type": "uint8" }], - "name": "MissingFulfillmentComponentOnAggregation", - "type": "error" - }, - { "inputs": [], "name": "MissingItemAmount", "type": "error" }, - { "inputs": [], "name": "MissingOriginalConsiderationItems", "type": "error" }, - { - "inputs": [ - { "internalType": "address", "name": "account", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "NativeTokenTransferGenericFailure", - "type": "error" - }, - { - "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], - "name": "NoContract", - "type": "error" - }, - { "inputs": [], "name": "NoReentrantCalls", "type": "error" }, - { "inputs": [], "name": "NoSpecifiedOrdersAvailable", "type": "error" }, - { "inputs": [], "name": "OfferAndConsiderationRequiredOnFulfillment", "type": "error" }, - { "inputs": [], "name": "OfferCriteriaResolverOutOfRange", "type": "error" }, - { - "inputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "name": "OrderAlreadyFilled", - "type": "error" - }, - { - "inputs": [{ "internalType": "enumSide", "name": "side", "type": "uint8" }], - "name": "OrderCriteriaResolverOutOfRange", - "type": "error" - }, - { - "inputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "name": "OrderIsCancelled", - "type": "error" - }, - { - "inputs": [{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }], - "name": "OrderPartiallyFilled", - "type": "error" - }, - { "inputs": [], "name": "PartialFillsNotEnabledForOrder", "type": "error" }, - { - "inputs": [ - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "TokenTransferGenericFailure", - "type": "error" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "considerationIndex", "type": "uint256" } - ], - "name": "UnresolvedConsiderationCriteria", - "type": "error" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "orderIndex", "type": "uint256" }, - { "internalType": "uint256", "name": "offerIndex", "type": "uint256" } - ], - "name": "UnresolvedOfferCriteria", - "type": "error" - }, - { "inputs": [], "name": "UnusedItemParameters", "type": "error" }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "newCounter", "type": "uint256" }, - { "indexed": true, "internalType": "address", "name": "offerer", "type": "address" } - ], - "name": "CounterIncremented", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "indexed": true, "internalType": "address", "name": "offerer", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "zone", "type": "address" } - ], - "name": "OrderCancelled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { "indexed": true, "internalType": "address", "name": "offerer", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "zone", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "recipient", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "indexed": false, - "internalType": "structSpentItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifier", "type": "uint256" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "indexed": false, - "internalType": "structReceivedItem[]", - "name": "consideration", - "type": "tuple[]" - } - ], - "name": "OrderFulfilled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" }, - { - "components": [ - { "internalType": "address", "name": "offerer", "type": "address" }, - { "internalType": "address", "name": "zone", "type": "address" }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" } - ], - "internalType": "structOfferItem[]", - "name": "offer", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "enumItemType", "name": "itemType", "type": "uint8" }, - { "internalType": "address", "name": "token", "type": "address" }, - { "internalType": "uint256", "name": "identifierOrCriteria", "type": "uint256" }, - { "internalType": "uint256", "name": "startAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "endAmount", "type": "uint256" }, - { "internalType": "addresspayable", "name": "recipient", "type": "address" } - ], - "internalType": "structConsiderationItem[]", - "name": "consideration", - "type": "tuple[]" - }, - { "internalType": "enumOrderType", "name": "orderType", "type": "uint8" }, - { "internalType": "uint256", "name": "startTime", "type": "uint256" }, - { "internalType": "uint256", "name": "endTime", "type": "uint256" }, - { "internalType": "bytes32", "name": "zoneHash", "type": "bytes32" }, - { "internalType": "uint256", "name": "salt", "type": "uint256" }, - { "internalType": "bytes32", "name": "conduitKey", "type": "bytes32" }, - { "internalType": "uint256", "name": "totalOriginalConsiderationItems", "type": "uint256" } - ], - "indexed": false, - "internalType": "structOrderParameters", - "name": "orderParameters", - "type": "tuple" - } - ], - "name": "OrderValidated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "bytes32[]", "name": "orderHashes", "type": "bytes32[]" }], - "name": "OrdersMatched", - "type": "event" - } -] diff --git a/test/integration-tests/shared/abis/Sudoswap.json b/test/integration-tests/shared/abis/Sudoswap.json deleted file mode 100644 index a1a6911e..00000000 --- a/test/integration-tests/shared/abis/Sudoswap.json +++ /dev/null @@ -1,1109 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract ILSSVMPairFactoryLike", - "name": "_factory", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "factory", - "outputs": [ - { - "internalType": "contract ILSSVMPairFactoryLike", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract ERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "enum ILSSVMPairFactoryLike.PairVariant", - "name": "variant", - "type": "uint8" - } - ], - "name": "pairTransferERC20From", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC721", - "name": "nft", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "id", - "type": "uint256" - }, - { - "internalType": "enum ILSSVMPairFactoryLike.PairVariant", - "name": "variant", - "type": "uint8" - } - ], - "name": "pairTransferNFTFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numItems", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.PairSwapAny", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "maxCost", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapAny[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "robustSwapERC20ForAnyNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "maxCost", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecific[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "robustSwapERC20ForSpecificNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "maxCost", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecific[]", - "name": "tokenToNFTTrades", - "type": "tuple[]" - }, - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecificForToken[]", - "name": "nftToTokenTrades", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "tokenRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - } - ], - "internalType": "struct LSSVMRouter.RobustPairNFTsFoTokenAndTokenforNFTsTrade", - "name": "params", - "type": "tuple" - } - ], - "name": "robustSwapERC20ForSpecificNFTsAndNFTsToToken", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numItems", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.PairSwapAny", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "maxCost", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapAny[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "address payable", - "name": "ethRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "robustSwapETHForAnyNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "maxCost", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecific[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "address payable", - "name": "ethRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "robustSwapETHForSpecificNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "maxCost", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecific[]", - "name": "tokenToNFTTrades", - "type": "tuple[]" - }, - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecificForToken[]", - "name": "nftToTokenTrades", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "tokenRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - } - ], - "internalType": "struct LSSVMRouter.RobustPairNFTsFoTokenAndTokenforNFTsTrade", - "name": "params", - "type": "tuple" - } - ], - "name": "robustSwapETHForSpecificNFTsAndNFTsToToken", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific", - "name": "swapInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.RobustPairSwapSpecificForToken[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "address payable", - "name": "tokenRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "robustSwapNFTsForToken", - "outputs": [ - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numItems", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.PairSwapAny[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapERC20ForAnyNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapERC20ForSpecificNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numItems", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.PairSwapAny[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "address payable", - "name": "ethRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapETHForAnyNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "address payable", - "name": "ethRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapETHForSpecificNFTs", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingValue", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "nftToTokenTrades", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numItems", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.PairSwapAny[]", - "name": "tokenToNFTTrades", - "type": "tuple[]" - } - ], - "internalType": "struct LSSVMRouter.NFTsForAnyNFTsTrade", - "name": "trade", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapNFTsForAnyNFTsThroughERC20", - "outputs": [ - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "nftToTokenTrades", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numItems", - "type": "uint256" - } - ], - "internalType": "struct LSSVMRouter.PairSwapAny[]", - "name": "tokenToNFTTrades", - "type": "tuple[]" - } - ], - "internalType": "struct LSSVMRouter.NFTsForAnyNFTsTrade", - "name": "trade", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "ethRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapNFTsForAnyNFTsThroughETH", - "outputs": [ - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "nftToTokenTrades", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "tokenToNFTTrades", - "type": "tuple[]" - } - ], - "internalType": "struct LSSVMRouter.NFTsForSpecificNFTsTrade", - "name": "trade", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "inputAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapNFTsForSpecificNFTsThroughERC20", - "outputs": [ - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "nftToTokenTrades", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "tokenToNFTTrades", - "type": "tuple[]" - } - ], - "internalType": "struct LSSVMRouter.NFTsForSpecificNFTsTrade", - "name": "trade", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "ethRecipient", - "type": "address" - }, - { - "internalType": "address", - "name": "nftRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapNFTsForSpecificNFTsThroughETH", - "outputs": [ - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract LSSVMPair", - "name": "pair", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "nftIds", - "type": "uint256[]" - } - ], - "internalType": "struct LSSVMRouter.PairSwapSpecific[]", - "name": "swapList", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "minOutput", - "type": "uint256" - }, - { - "internalType": "address", - "name": "tokenRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapNFTsForToken", - "outputs": [ - { - "internalType": "uint256", - "name": "outputAmount", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] diff --git a/test/integration-tests/shared/abis/X2Y2.json b/test/integration-tests/shared/abis/X2Y2.json deleted file mode 100644 index 32869117..00000000 --- a/test/integration-tests/shared/abis/X2Y2.json +++ /dev/null @@ -1,1086 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "currency", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "incentive", - "type": "uint256" - } - ], - "name": "EvAuctionRefund", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - } - ], - "name": "EvCancel", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "delegate", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isRemoval", - "type": "bool" - } - ], - "name": "EvDelegate", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "index", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "error", - "type": "bytes" - } - ], - "name": "EvFailure", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "newValue", - "type": "uint256" - } - ], - "name": "EvFeeCapUpdate", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "maker", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "taker", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "orderSalt", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "settleSalt", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "intent", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "delegateType", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "contract IERC20Upgradeable", - "name": "currency", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "dataMask", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "indexed": false, - "internalType": "struct Market.OrderItem", - "name": "item", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "enum Market.Op", - "name": "op", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "orderIdx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "itemIdx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - }, - { - "internalType": "contract IDelegate", - "name": "executionDelegate", - "type": "address" - }, - { - "internalType": "bytes", - "name": "dataReplacement", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "bidIncentivePct", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aucMinIncrementPct", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aucIncDurationSecs", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "percentage", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - } - ], - "internalType": "struct Market.Fee[]", - "name": "fees", - "type": "tuple[]" - } - ], - "indexed": false, - "internalType": "struct Market.SettleDetail", - "name": "detail", - "type": "tuple" - } - ], - "name": "EvInventory", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "currency", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "EvProfit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "signer", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isRemoval", - "type": "bool" - } - ], - "name": "EvSigner", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "inputs": [], - "name": "RATE_BASE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32[]", - "name": "itemHashes", - "type": "bytes32[]" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "cancel", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "delegates", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "feeCapPct", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "feeCapPct_", - "type": "uint256" - }, - { - "internalType": "address", - "name": "weth_", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "inventoryStatus", - "outputs": [ - { - "internalType": "enum Market.InvStatus", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "ongoingAuctions", - "outputs": [ - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "netPrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endAt", - "type": "uint256" - }, - { - "internalType": "address", - "name": "bidder", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "uint256", - "name": "salt", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint256", - "name": "network", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "intent", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "delegateType", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "contract IERC20Upgradeable", - "name": "currency", - "type": "address" - }, - { - "internalType": "bytes", - "name": "dataMask", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct Market.OrderItem[]", - "name": "items", - "type": "tuple[]" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "signVersion", - "type": "uint8" - } - ], - "internalType": "struct Market.Order[]", - "name": "orders", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "enum Market.Op", - "name": "op", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "orderIdx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "itemIdx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - }, - { - "internalType": "contract IDelegate", - "name": "executionDelegate", - "type": "address" - }, - { - "internalType": "bytes", - "name": "dataReplacement", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "bidIncentivePct", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aucMinIncrementPct", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aucIncDurationSecs", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "percentage", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - } - ], - "internalType": "struct Market.Fee[]", - "name": "fees", - "type": "tuple[]" - } - ], - "internalType": "struct Market.SettleDetail[]", - "name": "details", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "salt", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountToEth", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountToWeth", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "bool", - "name": "canFail", - "type": "bool" - } - ], - "internalType": "struct Market.SettleShared", - "name": "shared", - "type": "tuple" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - } - ], - "internalType": "struct Market.RunInput", - "name": "input", - "type": "tuple" - } - ], - "name": "run", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "salt", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint256", - "name": "network", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "intent", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "delegateType", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "contract IERC20Upgradeable", - "name": "currency", - "type": "address" - }, - { - "internalType": "bytes", - "name": "dataMask", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct Market.OrderItem[]", - "name": "items", - "type": "tuple[]" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "signVersion", - "type": "uint8" - } - ], - "internalType": "struct Market.Order", - "name": "order", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "salt", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountToEth", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountToWeth", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "bool", - "name": "canFail", - "type": "bool" - } - ], - "internalType": "struct Market.SettleShared", - "name": "shared", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "enum Market.Op", - "name": "op", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "orderIdx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "itemIdx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "itemHash", - "type": "bytes32" - }, - { - "internalType": "contract IDelegate", - "name": "executionDelegate", - "type": "address" - }, - { - "internalType": "bytes", - "name": "dataReplacement", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "bidIncentivePct", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aucMinIncrementPct", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aucIncDurationSecs", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "percentage", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - } - ], - "internalType": "struct Market.Fee[]", - "name": "fees", - "type": "tuple[]" - } - ], - "internalType": "struct Market.SettleDetail", - "name": "detail", - "type": "tuple" - } - ], - "name": "run1", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "signers", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "toAdd", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "toRemove", - "type": "address[]" - } - ], - "name": "updateDelegates", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "val", - "type": "uint256" - } - ], - "name": "updateFeeCap", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "toAdd", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "toRemove", - "type": "address[]" - } - ], - "name": "updateSigners", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "weth", - "outputs": [ - { - "internalType": "contract IWETHUpgradable", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] diff --git a/test/integration-tests/shared/constants.ts b/test/integration-tests/shared/constants.ts index f6ed4c2a..158efe52 100644 --- a/test/integration-tests/shared/constants.ts +++ b/test/integration-tests/shared/constants.ts @@ -16,34 +16,9 @@ export const ADDRESS_THIS: string = '0x0000000000000000000000000000000000000002' export const SOURCE_MSG_SENDER: boolean = true export const SOURCE_ROUTER: boolean = false -// Protocol Data -export const OPENSEA_CONDUIT = '0x1E0049783F008A0085193E00003D00cd54003c71' -export const OPENSEA_CONDUIT_KEY = '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000' - -// NFT Addresses -export const COVEN_ADDRESS = '0x5180db8f5c931aae63c74266b211f580155ecac8' -export const DECENTRA_DRAGON_ADDRESS = '0xAA107cCFe230a29C345Fd97bc6eb9Bd2fccD0750' -export const TOWNSTAR_ADDRESS = '0xc36cF0cFcb5d905B8B513860dB0CFE63F6Cf9F5c' -export const TWERKY_ADDRESS = '0xf4680c917a873e2dd6ead72f9f433e74eb9c623c' -export const MILADY_ADDRESS = '0x5af0d9827e0c53e4799bb226655a1de152a425a5' -export const ALPHABETTIES_ADDRESS = '0x6d05064fe99e40f1c3464e7310a23ffaded56e20' -export const MENTAL_WORLDS_ADDRESS = '0xEf96021Af16BD04918b0d87cE045d7984ad6c38c' -export const CAMEO_ADDRESS = '0x93317E87a3a47821803CAADC54Ae418Af80603DA' -export const ENS_NFT_ADDRESS = '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85' -export const CRYPTOPUNKS_MARKET_ADDRESS = '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB' -export const NFTX_COVEN_VAULT = '0xd89b16331f39ab3878daf395052851d3ac8cf3cd' -export const NFTX_COVEN_VAULT_ID = '333' -export const NFTX_MILADY_VAULT = '0x227c7df69d3ed1ae7574a1a7685fded90292eb48' -export const NFTX_MILADY_VAULT_ID = '392' -export const NFTX_ERC_1155_VAULT = '0x78e09c5ec42d505742a52fd10078a57ea186002a' -export const NFTX_ERC_1155_VAULT_ID = '61' - // Constructor Params +export const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3' export const V2_FACTORY_MAINNET = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f' export const V3_FACTORY_MAINNET = '0x1F98431c8aD98523631AE4a59f267346ea31F984' export const V3_INIT_CODE_HASH_MAINNET = '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54' export const V2_INIT_CODE_HASH_MAINNET = '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' - -export const ROUTER_REWARDS_DISTRIBUTOR = '0x0000000000000000000000000000000000000000' -export const LOOKSRARE_REWARDS_DISTRIBUTOR = '0x0554f068365eD43dcC98dcd7Fd7A8208a5638C72' -export const LOOKSRARE_TOKEN = '0xf4d2888d29D722226FafA5d9B24F9164c092421E' diff --git a/test/integration-tests/shared/deployUniversalRouter.ts b/test/integration-tests/shared/deployUniversalRouter.ts index bfab0238..8fe3fdf0 100644 --- a/test/integration-tests/shared/deployUniversalRouter.ts +++ b/test/integration-tests/shared/deployUniversalRouter.ts @@ -1,39 +1,18 @@ import hre from 'hardhat' const { ethers } = hre -import { UniversalRouter, Permit2 } from '../../../typechain' +import { UniversalRouter } from '../../../typechain' import { V2_FACTORY_MAINNET, V3_FACTORY_MAINNET, V2_INIT_CODE_HASH_MAINNET, V3_INIT_CODE_HASH_MAINNET, - ROUTER_REWARDS_DISTRIBUTOR, - LOOKSRARE_REWARDS_DISTRIBUTOR, - LOOKSRARE_TOKEN, + PERMIT2_ADDRESS, } from './constants' -export async function deployRouter( - permit2: Permit2, - mockLooksRareRewardsDistributor?: string, - mockLooksRareToken?: string, - mockReentrantProtocol?: string -): Promise { +export async function deployRouter(mockReentrantWETH?: string): Promise { const routerParameters = { - permit2: permit2.address, - weth9: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - seaportV1_5: '0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC', - seaportV1_4: '0x00000000000001ad428e4906aE43D8F9852d0dD6', - openseaConduit: '0x1E0049783F008A0085193E00003D00cd54003c71', - nftxZap: mockReentrantProtocol ?? '0x941A6d105802CCCaa06DE58a13a6F49ebDCD481C', - x2y2: '0x74312363e45DCaBA76c59ec49a7Aa8A65a67EeD3', - foundation: '0xcDA72070E455bb31C7690a170224Ce43623d0B6f', - sudoswap: '0x2B2e8cDA09bBA9660dCA5cB6233787738Ad68329', - elementMarket: '0x20F780A973856B93f63670377900C1d2a50a77c4', - nft20Zap: '0xA42f6cADa809Bcf417DeefbdD69C5C5A909249C0', - cryptopunks: '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB', - looksRareV2: '0x0000000000E655fAe4d56241588680F86E3b2377', - routerRewardsDistributor: ROUTER_REWARDS_DISTRIBUTOR, - looksRareRewardsDistributor: mockLooksRareRewardsDistributor ?? LOOKSRARE_REWARDS_DISTRIBUTOR, - looksRareToken: mockLooksRareToken ?? LOOKSRARE_TOKEN, + permit2: PERMIT2_ADDRESS, + weth9: mockReentrantWETH ?? '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', v2Factory: V2_FACTORY_MAINNET, v3Factory: V3_FACTORY_MAINNET, pairInitCodeHash: V2_INIT_CODE_HASH_MAINNET, @@ -46,18 +25,3 @@ export async function deployRouter( } export default deployRouter - -export async function deployPermit2(): Promise { - const permit2Factory = await ethers.getContractFactory('Permit2') - const permit2 = (await permit2Factory.deploy()) as unknown as Permit2 - return permit2 -} - -export async function deployRouterAndPermit2( - mockLooksRareRewardsDistributor?: string, - mockLooksRareToken?: string -): Promise<[UniversalRouter, Permit2]> { - const permit2 = await deployPermit2() - const router = await deployRouter(permit2, mockLooksRareRewardsDistributor, mockLooksRareToken) - return [router, permit2] -} diff --git a/test/integration-tests/shared/mainnetForkHelpers.ts b/test/integration-tests/shared/mainnetForkHelpers.ts index a86ee011..2c52ba2f 100644 --- a/test/integration-tests/shared/mainnetForkHelpers.ts +++ b/test/integration-tests/shared/mainnetForkHelpers.ts @@ -1,19 +1,6 @@ -import { ERC721, ERC1155, ERC20, ERC20__factory } from '../../../typechain' -import { abi as ERC721_ABI } from '../../../artifacts/solmate/src/tokens/ERC721.sol/ERC721.json' -import { abi as ERC1155_ABI } from '../../../artifacts/solmate/src/tokens/ERC1155.sol/ERC1155.json' -import CRYPTOPUNKS_ABI from './abis/Cryptopunks.json' -import { - ALPHABETTIES_ADDRESS, - CAMEO_ADDRESS, - COVEN_ADDRESS, - ENS_NFT_ADDRESS, - MENTAL_WORLDS_ADDRESS, - TWERKY_ADDRESS, - CRYPTOPUNKS_MARKET_ADDRESS, - DECENTRA_DRAGON_ADDRESS, - TOWNSTAR_ADDRESS, - MILADY_ADDRESS, -} from './constants' +import { ERC20, ERC20__factory, IPermit2 } from '../../../typechain' +import { abi as PERMIT2_ABI } from '../../../artifacts/permit2/src/interfaces/IPermit2.sol/IPermit2.json' +import { PERMIT2_ADDRESS } from './constants' import { abi as V2_PAIR_ABI } from '../../../artifacts/@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol/IUniswapV2Pair.json' import { Currency, Token, WETH9 } from '@uniswap/sdk-core' import { TransactionResponse } from '@ethersproject/abstract-provider' @@ -102,27 +89,18 @@ export const executeSwapRouter02Swap = async ( return transactionResponse } -export const resetFork = async (block: number = 15360000) => { +export const resetFork = async () => { await hre.network.provider.request({ method: 'hardhat_reset', params: [ { forking: { jsonRpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, - blockNumber: block, + blockNumber: 20010000, }, }, ], }) } -export const COVEN_721 = new ethers.Contract(COVEN_ADDRESS, ERC721_ABI) as ERC721 -export const DRAGON_721 = new ethers.Contract(DECENTRA_DRAGON_ADDRESS, ERC721_ABI) as ERC721 -export const MILADY_721 = new ethers.Contract(MILADY_ADDRESS, ERC721_ABI) as ERC721 -export const ENS_721 = new ethers.Contract(ENS_NFT_ADDRESS, ERC721_ABI) as ERC721 -export const MENTAL_WORLDS_721 = new ethers.Contract(MENTAL_WORLDS_ADDRESS, ERC721_ABI) as ERC721 -export const ALPHABETTIES_721 = new ethers.Contract(ALPHABETTIES_ADDRESS, ERC721_ABI) as ERC721 -export const TWERKY_1155 = new ethers.Contract(TWERKY_ADDRESS, ERC1155_ABI) as ERC1155 -export const CAMEO_1155 = new ethers.Contract(CAMEO_ADDRESS, ERC1155_ABI) as ERC1155 -export const TOWNSTAR_1155 = new ethers.Contract(TOWNSTAR_ADDRESS, ERC1155_ABI) as ERC1155 -export const CRYPTOPUNKS_MARKET = new ethers.Contract(CRYPTOPUNKS_MARKET_ADDRESS, CRYPTOPUNKS_ABI) +export const PERMIT2 = new ethers.Contract(PERMIT2_ADDRESS, PERMIT2_ABI) as IPermit2 diff --git a/test/integration-tests/shared/orders/Element.json b/test/integration-tests/shared/orders/Element.json deleted file mode 100644 index 3c6240a8..00000000 --- a/test/integration-tests/shared/orders/Element.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "code": 0, - "data": { - "orders": [ - { - "chain": "eth", - "chainId": "0x1", - "orderHash": "0x2cf84f50f22f8f5f831502f05ab5c559d4025e05aefd140aaf3721f74103e58b_5", - "expirationTime": 1676864880, - "listingTime": 1676409509, - "createTime": 1676409576, - "maker": "0x762aa70e1a9e48822e9ecf4d23e8d6a0a529ed7e", - "taker": "0x0000000000000000000000000000000000000000", - "side": 1, - "saleKind": 3, - "paymentToken": "0x0000000000000000000000000000000000000000", - "quantity": "1", - "priceBase": 0.0037, - "priceUSD": 5.764585, - "price": 0.0037, - "standard": "element-ex-v3", - "contractAddress": "0x6cbdb357ace5db2cc178e12e7f5dc7c1da5002fc", - "tokenId": "728", - "schema": "ERC721", - "extra": null, - "exchangeData": "{\"basicCollections\":[{\"nftAddress\":\"0x6cbdb357ace5db2cc178e12e7f5dc7c1da5002fc\",\"platformFee\":50,\"royaltyFeeRecipient\":\"0x0000000000000000000000000000000000000000\",\"royaltyFee\":0,\"items\":[{\"erc20TokenAmount\":\"3700000000000000\",\"nftId\":\"728\"}]}],\"collections\":null,\"startNonce\":5,\"nonce\":5,\"hashNonce\":\"0\",\"platformFeeRecipient\":\"0x00ca62445b06a9adc1879a44485b4efdcb7b75f3\",\"v\":28,\"r\":\"0x2e4b386d0cf248a95aba4eb0fdfddddf2f691067701068ea99ae352a5964cfd8\",\"s\":\"0x62c98f7d6204fa31ba91be457ace42065ca3b6d72697f6215e88f3feb435f849\",\"listingTime\":1676409509,\"expirationTime\":1676864880,\"maker\":\"0x762aa70e1a9e48822e9ecf4d23e8d6a0a529ed7e\",\"hash\":\"0x2cf84f50f22f8f5f831502f05ab5c559d4025e05aefd140aaf3721f74103e58b\",\"paymentToken\":\"0x0000000000000000000000000000000000000000\"}" - } - ] - } -} diff --git a/test/integration-tests/shared/orders/LooksRareV2.json b/test/integration-tests/shared/orders/LooksRareV2.json deleted file mode 100644 index 6eb0234b..00000000 --- a/test/integration-tests/shared/orders/LooksRareV2.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - { - "id": "MTE1MjkyMTUwNDYwNjg0NzMyNw==", - "hash": "0x348f5ea7d561cf3e56f3db0385645b56a3c52cbf32aa5a8cd3d36cc74063d903", - "quoteType": 1, - "globalNonce": "0", - "subsetNonce": "0", - "orderNonce": "0", - "collection": "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", - "currency": "0x0000000000000000000000000000000000000000", - "signer": "0x08ab6bc579745623520791f31a15c4eb4e29946b", - "strategyId": 0, - "collectionType": 0, - "startTime": 1681221684, - "endTime": 1683285203, - "price": "200000000000000000", - "additionalParameters": "0x", - "signature": "0x2c0472f01e3ced7a2d1d59bdaf4c3f5bb9c0d1b8371347ef83b2de04ceae78f11bf8b97a4254215bdebd9c4266265acdb062528e6dd863e332db19031e767f501b", - "createdAt": "2023-04-03T21:38:48.903Z", - "merkleRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "merkleProof": [], - "amounts": ["1"], - "itemIds": ["905"], - "status": "VALID" - }, - { - "id": "", - "hash": "", - "quoteType": 1, - "globalNonce": "0", - "subsetNonce": "0", - "orderNonce": "0", - "collection": "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", - "currency": "0x0000000000000000000000000000000000000000", - "signer": "0x101f791249f225a9130d7e6020be444d281aae6c", - "strategyId": 0, - "collectionType": 0, - "startTime": 1681281786, - "endTime": 1683873169, - "price": "200000000000000000", - "additionalParameters": "0x", - "signature": "0x2a6e1d159bda2e2e2db65328599eb22fb224f5760d52cb5cddd03e0a69b99c9059cf2ffdb990ef49a5cf0cee063f51d562b66b6d02fc6a4ff6a48d144109c8381b", - "createdAt": "", - "merkleRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "merkleProof": [], - "amounts": ["1"], - "itemIds": ["3417"], - "status": "VALID" - }, - { - "id": "", - "hash": "", - "quoteType": 1, - "globalNonce": "0", - "subsetNonce": "0", - "orderNonce": "0", - "collection": "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", - "currency": "0x0000000000000000000000000000000000000000", - "signer": "0xfbb3daa40f1da3b847292c7544afef2a6d2106c8", - "strategyId": 0, - "collectionType": 0, - "startTime": 1681211346, - "endTime": 1683280045, - "price": "209900000000000000", - "additionalParameters": "0x", - "signature": "0xb85c978f8593f4841caff4bfb30872ee726e35ba43cb382a8cad40fff163275910ab2d0496d9be1954e4eb60710be850574a41adad24a5921de506c1acc5a1821b", - "createdAt": "", - "merkleRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "merkleProof": [], - "amounts": ["1"], - "itemIds": ["933"], - "status": "VALID" - } -] diff --git a/test/integration-tests/shared/orders/SeaportV1_4.json b/test/integration-tests/shared/orders/SeaportV1_4.json deleted file mode 100644 index 9760ac8e..00000000 --- a/test/integration-tests/shared/orders/SeaportV1_4.json +++ /dev/null @@ -1,151 +0,0 @@ -[ - { - "protocol_data": { - "parameters": { - "offerer": "0x681f7EAA59B970CE1A30DEc9E9C68C1E6C5f1f78", - "offer": [ - { - "itemType": 2, - "token": "0x5180db8F5c931aaE63c74266b211F580155ecac8", - "identifierOrCriteria": "4703", - "startAmount": "1", - "endAmount": "1" - } - ], - "consideration": [ - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "116315500000000000", - "endAmount": "116315500000000000", - "recipient": "0x681f7EAA59B970CE1A30DEc9E9C68C1E6C5f1f78" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "584500000000000", - "endAmount": "584500000000000", - "recipient": "0xac9d54ca08740A608B6C474e5CA07d51cA8117Fa" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "500000000", - "endAmount": "500000000", - "recipient": "0x6e367F09327c4C15a1a4BC28f7764d63a8F38F76" - } - ], - "startTime": "1678282215", - "endTime": "1680945040", - "orderType": 0, - "zone": "0x004C00500000aD104D7DBd00e3ae0A5C00560C00", - "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "salt": "24446860302761739304752683030156737591518664810215442929801498294838556555105", - "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", - "totalOriginalConsiderationItems": 2 - }, - "signature": "0x67c764297bb14105490e510b35a00a55f6bc873dfcc4b89de99da7ec6ca3cf7cd4ef8210e6db384e20488c9efd82afa2abfacfa1b066d5112c3c7d6bd0101224" - }, - "protocol_address": "0x00000000000001ad428e4906aE43D8F9852d0dD6" - }, - { - "protocol_data": { - "parameters": { - "offerer": "0x5e755d47c1874da844b31e08ba70f11d047f96d6", - "offer": [ - { - "itemType": 3, - "token": "0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c", - "identifierOrCriteria": "425352958651173079329218259289710264320000", - "startAmount": "2", - "endAmount": "2" - } - ], - "consideration": [ - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "350000000000000000", - "endAmount": "350000000000000000", - "recipient": "0x1D4Dd22898a37a6b3EfbeF0218FA46b668ad49B8" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "10000000000000000", - "endAmount": "10000000000000000", - "recipient": "0x0000a26b00c1F0DF003000390027140000fAa719" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "40000000000000000", - "endAmount": "40000000000000000", - "recipient": "0x62314D5A0F7CBed83Df49C53B9f2C687d2c18289" - } - ], - "startTime": "1681209152", - "endTime": "1683235925", - "orderType": 0, - "zone": "0x004C00500000aD104D7DBd00e3ae0A5C00560C00", - "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "salt": "24446860302761739304752683030156737591518664810215442929814213514577079252705", - "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", - "totalOriginalConsiderationItems": 3, - "counter": "getsDeletedAnyways" - }, - "signature": "0x3b5ef8ffae3d93e953bf30f580aeed80077fb2bcef8e4d8fa2595788ddb83613acd669a99c63437da3b9fc96f9d0c45f7846caf04fd1ab6e8ff965e64400ee20" - }, - "protocol_address": "0x00000000000001ad428e4906aE43D8F9852d0dD6" - }, - { - "protocol_data": { - "parameters": { - "offerer": "0x5e755d47c1874da844b31e08ba70f11d047f96d6", - "offer": [ - { - "itemType": 3, - "token": "0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c", - "identifierOrCriteria": "425352958651173079329218259289710264320000", - "startAmount": "2", - "endAmount": "2" - } - ], - "consideration": [ - { - "itemType": 1, - "token": "0x15d4c048f83bd7e37d49ea4c83a07267ec4203da", - "identifierOrCriteria": "0", - "startAmount": "223020000000", - "endAmount": "223020000000", - "recipient": "0x5e755d47c1874da844b31e08ba70f11d047f96d6" - }, - { - "itemType": 1, - "token": "0x15d4c048f83bd7e37d49ea4c83a07267ec4203da", - "identifierOrCriteria": "0", - "startAmount": "24780000000", - "endAmount": "24780000000", - "recipient": "0xa92abb0d0dd1e8e73006fc3b6229b7bd9e0d5c61" - } - ], - "startTime": "1678218282", - "endTime": "1680893082", - "orderType": 1, - "zone": "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", - "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "salt": "24446860302761739304752683030156737591518664810215442929813247523325878245709", - "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", - "totalOriginalConsiderationItems": "2" - }, - "signature": "0x6fd0032bb132c3724b730d55deb59924b8674405ae5e523a95a56b5a258af1d9cc9de9a90aef35ba9311afaf37eb8b0904f2f32e799abf63f073470c595aeefb" - }, - "protocol_address": "0x00000000000001ad428e4906aE43D8F9852d0dD6" - } -] diff --git a/test/integration-tests/shared/orders/SeaportV1_5.json b/test/integration-tests/shared/orders/SeaportV1_5.json deleted file mode 100644 index 171346f0..00000000 --- a/test/integration-tests/shared/orders/SeaportV1_5.json +++ /dev/null @@ -1,158 +0,0 @@ -[ - { - "protocol_data": { - "parameters": { - "offerer": "0x8cbcbaa074974a7ba6626f8d6ad8d27aaa4b3e80", - "offer": [ - { - "itemType": 3, - "token": "0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c", - "identifierOrCriteria": "582563412168646649449297327923187178012672", - "startAmount": "3", - "endAmount": "3" - } - ], - "consideration": [ - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "178495739625000000", - "endAmount": "178495739625000000", - "recipient": "0x8cbcbaa074974a7ba6626f8d6ad8d27aaa4b3e80" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "5099878275000000", - "endAmount": "5099878275000000", - "recipient": "0x0000a26b00c1f0df003000390027140000faa719" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "20399513100000000", - "endAmount": "20399513100000000", - "recipient": "0x14a33e1b12a52fe1f558ccbf908d38ed4087ff30" - } - ], - "orderType": 1, - "startTime": "1683059155", - "endTime": "1683145555", - "zone": "0x004C00500000aD104D7DBd00e3ae0A5C00560C00", - "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "salt": "24446860302761739304752683030156737591518664810215442929812219397503191775233", - "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", - "totalOriginalConsiderationItems": 3 - }, - "signature": "0x3cf22e15c2581d315d20e8f2fba8c007ba80b29e826b3ac3b063abb550271d01a62b95c9aa1f7adfae81ff3393530a822af3cf0b2ed11c1abba8e75b30ff5204" - }, - "protocol_address": "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC" - }, - { - "protocol_data": { - "parameters": { - "offerer": "0x7bca4682999b71d813d541a9cbf73e35216f1417", - "offer": [ - { - "endAmount": "1", - "identifierOrCriteria": "564868729088757849349201848336735231016960", - "itemType": 3, - "startAmount": "1", - "token": "0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c" - } - ], - "consideration": [ - { - "endAmount": "57312500000000000", - "identifierOrCriteria": "0", - "itemType": 0, - "recipient": "0x7bca4682999b71d813d541a9cbf73e35216f1417", - "startAmount": "57312500000000000", - "token": "0x0000000000000000000000000000000000000000" - }, - { - "endAmount": "1637500000000000", - "identifierOrCriteria": "0", - "itemType": 0, - "recipient": "0x0000a26b00c1f0df003000390027140000faa719", - "startAmount": "1637500000000000", - "token": "0x0000000000000000000000000000000000000000" - }, - { - "endAmount": "6550000000000000", - "identifierOrCriteria": "0", - "itemType": 0, - "recipient": "0x9cfb24366131c42d041139c8abbea45f6527a9b2", - "startAmount": "6550000000000000", - "token": "0x0000000000000000000000000000000000000000" - } - ], - "orderType": 1, - "startTime": "1683084765", - "endTime": "1683171165", - "zone": "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", - "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "salt": "24446860302761739304752683030156737591518664810215442929802117345480319970273", - "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", - "totalOriginalConsiderationItems": "3" - }, - "signature": "0xa0cfc9291bb705f32a7d4bea77e9ef4dece18d4424864abad4ea26c81a9e9d144a9dbb7fd18f6819be34a7ed4d6714ddf402255cc5d59e5789c8afef80b7380a" - }, - "protocol_address": "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC" - }, - { - "protocol_data": { - "parameters": { - "offerer": "0xbadb011bea1305f52f85664a755ed5921bf818ea", - "offer": [ - { - "itemType": 3, - "token": "0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c", - "identifierOrCriteria": "580862000334041957131980454886028336955392", - "startAmount": "1", - "endAmount": "1" - } - ], - "consideration": [ - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "95375000000000000", - "endAmount": "95375000000000000", - "recipient": "0xbadb011bea1305f52f85664a755ed5921bf818ea" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "2725000000000000", - "endAmount": "2725000000000000", - "recipient": "0x0000a26b00c1f0df003000390027140000faa719" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "10900000000000000", - "endAmount": "10900000000000000", - "recipient": "0x9cfb24366131c42d041139c8abbea45f6527a9b2" - } - ], - "orderType": 1, - "startTime": "1683052606", - "endTime": "1685731006", - "zone": "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", - "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "salt": "24446860302761739304752683030156737591518664810215442929811933767042559172667", - "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", - "totalOriginalConsiderationItems": "3" - }, - "signature": "0x8a73c1158a78eee531d4a8dd4be4b33edbf64a1cfa65020c9108102c17bc9a7c159ddaca7223478b0aaf3cfe113c3a3448dcd86996efe72d574b1c02c5ed83cf" - }, - "protocol_address": "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC" - } -] diff --git a/test/integration-tests/shared/orders/X2Y2.json b/test/integration-tests/shared/orders/X2Y2.json deleted file mode 100644 index 64a5225c..00000000 --- a/test/integration-tests/shared/orders/X2Y2.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "input": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000003a1a1daa06cd900000000000000000000000000000000000000000000000000000000635ff21b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987e855776c03a4682639eeb14e65b3089ee6310000000000000000000000000000000000000000000000000000000000000000018b88e60ccfe565bdb2f5a1c516dfd2ac454e36365d121b4791f62b7547cf8366e0e9e399902e1b5ba4750bf071e8650dfae542897923acebb0083229e5eb1a5000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000047f67d1e1552334d404c80f5cbef820000000000000000000000000d4f16530fbcd336b4f0d4d1717487a65098be7cd0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000063ea6d9e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c09d2388f6e390220f9d861cf0d793c77ef44bd8d97897b12c36ace9931acd72a5395fc36d3f1c5500e050ed703f7112a01e99295a093424a1a2fa66930e24a995000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000494654067e10000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000057f1887a8bf19b14fc0df6fd9b2acc9af147ea850e2d28628b4a177f893b3c31e4f64a3fefc6cfa562ec3c71867725934686fb26000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000494654067e10000fe98b8bf04d1bc66dd380d36c901fa2c5b2ec3097b59729fcdb29dee694c6fbe000000000000000000000000f849de01b080adc3a814fabe1e2087475cf2e35400000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001388000000000000000000000000d823c605807cc5e6bd6fc0d7e4eea50d3e2d66cd", - "order_id": 9002704, - "token_id": "6412166724678289871743717335944131474762824270526419296462192909998329625382", - "price": "330000000000000000" - }, - { - "input": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000003b41ca5886c0d00000000000000000000000000000000000000000000000000000000635ff41d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c32609c91d6b6b51d48f2611308fef121b02041f000000000000000000000000000000000000000000000000000000000000000034117536b41e7f4d267fa5b35c513db850efc42908b5311f7b4601e78900e2da48c8655a6cfa9c0610a1187c8f13b64ec73cb9f7a9a6c7d610ff183631337865000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000006eea9a96b66513fd634c399565f53fc90000000000000000000000008a3acc2d82c9a19efdbddb803add7bf7713c45450000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000063fb0888000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c0fc596083a0132d07cfd02dce0f55ce677b91cc59ebf6d57927593f02b18017192e9379735ef777f756112207f5d6edbba91efb5cdf062c7488a29d961f518418000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000001df9dc8e4ad8000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000093317e87a3a47821803caadc54ae418af80603da000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001df9dc8e4ad80009c48d3d0911702d217e91c00db0e8b5ef4dd6db0fa6d66837460bfb6a230cc71000000000000000000000000024ac22acdb367a3ae52a3d94ac6649fdc1f077900000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001388000000000000000000000000d823c605807cc5e6bd6fc0d7e4eea50d3e2d66cd", - "order_id": 10637561, - "token_id": "0", - "price": "135000000000000000" - } -] diff --git a/test/integration-tests/shared/planner.ts b/test/integration-tests/shared/planner.ts index 64316e00..c61b04cb 100644 --- a/test/integration-tests/shared/planner.ts +++ b/test/integration-tests/shared/planner.ts @@ -22,45 +22,12 @@ export enum CommandType { PERMIT2_TRANSFER_FROM_BATCH = 0x0d, BALANCE_CHECK_ERC20 = 0x0e, - // NFT-related command types - SEAPORT_V1_5 = 0x10, - LOOKS_RARE_V2 = 0x11, - NFTX = 0x12, - CRYPTOPUNKS = 0x13, - // 0x14, - OWNER_CHECK_721 = 0x15, - OWNER_CHECK_1155 = 0x16, - SWEEP_ERC721 = 0x17, - - X2Y2_721 = 0x18, - SUDOSWAP = 0x19, - NFT20 = 0x1a, - X2Y2_1155 = 0x1b, - FOUNDATION = 0x1c, - SWEEP_ERC1155 = 0x1d, - ELEMENT_MARKET = 0x1e, - - SEAPORT_V1_4 = 0x20, EXECUTE_SUB_PLAN = 0x21, - APPROVE_ERC20 = 0x22, } const ALLOW_REVERT_FLAG = 0x80 -const REVERTIBLE_COMMANDS = new Set([ - CommandType.SEAPORT_V1_5, - CommandType.SEAPORT_V1_4, - CommandType.NFTX, - CommandType.LOOKS_RARE_V2, - CommandType.X2Y2_721, - CommandType.X2Y2_1155, - CommandType.FOUNDATION, - CommandType.SUDOSWAP, - CommandType.NFT20, - CommandType.EXECUTE_SUB_PLAN, - CommandType.CRYPTOPUNKS, - CommandType.ELEMENT_MARKET, -]) +const REVERTIBLE_COMMANDS = new Set([CommandType.EXECUTE_SUB_PLAN]) const PERMIT_STRUCT = '((address token,uint160 amount,uint48 expiration,uint48 nonce) details, address spender, uint256 sigDeadline)' @@ -91,27 +58,9 @@ const ABI_DEFINITION: { [key in CommandType]: string[] } = { [CommandType.WRAP_ETH]: ['address', 'uint256'], [CommandType.UNWRAP_WETH]: ['address', 'uint256'], [CommandType.SWEEP]: ['address', 'address', 'uint256'], - [CommandType.SWEEP_ERC721]: ['address', 'address', 'uint256'], - [CommandType.SWEEP_ERC1155]: ['address', 'address', 'uint256', 'uint256'], [CommandType.TRANSFER]: ['address', 'address', 'uint256'], [CommandType.PAY_PORTION]: ['address', 'address', 'uint256'], [CommandType.BALANCE_CHECK_ERC20]: ['address', 'address', 'uint256'], - [CommandType.OWNER_CHECK_721]: ['address', 'address', 'uint256'], - [CommandType.OWNER_CHECK_1155]: ['address', 'address', 'uint256', 'uint256'], - [CommandType.APPROVE_ERC20]: ['address', 'uint256'], - - // NFT Markets - [CommandType.SEAPORT_V1_5]: ['uint256', 'bytes'], - [CommandType.SEAPORT_V1_4]: ['uint256', 'bytes'], - [CommandType.NFTX]: ['uint256', 'bytes'], - [CommandType.LOOKS_RARE_V2]: ['uint256', 'bytes'], - [CommandType.X2Y2_721]: ['uint256', 'bytes', 'address', 'address', 'uint256'], - [CommandType.X2Y2_1155]: ['uint256', 'bytes', 'address', 'address', 'uint256', 'uint256'], - [CommandType.FOUNDATION]: ['uint256', 'bytes', 'address', 'address', 'uint256'], - [CommandType.SUDOSWAP]: ['uint256', 'bytes'], - [CommandType.NFT20]: ['uint256', 'bytes'], - [CommandType.CRYPTOPUNKS]: ['uint256', 'address', 'uint256'], - [CommandType.ELEMENT_MARKET]: ['uint256', 'bytes'], } export class RoutePlanner { diff --git a/test/integration-tests/shared/protocolHelpers/element.ts b/test/integration-tests/shared/protocolHelpers/element.ts deleted file mode 100644 index b32b3712..00000000 --- a/test/integration-tests/shared/protocolHelpers/element.ts +++ /dev/null @@ -1,123 +0,0 @@ -import ELEMENT_721_ABI from '../abis/Element.json' -import { BigNumber } from 'ethers' -import fs from 'fs' -import hre from 'hardhat' -const { ethers } = hre - -export const element721Orders = JSON.parse( - fs.readFileSync('test/integration-tests/shared/orders/Element.json', { encoding: 'utf8' }) -).data.orders - -export const element721Interface = new ethers.utils.Interface(ELEMENT_721_ABI) - -export type ElementOrderSignature = { - signatureType: number // 0 for 721 and 1 for presigned - v: number - r: string - s: string -} - -export interface Fee { - recipient: string - amount: string - feeData: string -} - -export const EXAMPLE_ETH_SELL_ORDER: NFTSellOrder = { - maker: '0xABd6a19345943dD175026Cdb52902FD3392a3262', - taker: '0x75B6568025f463a98fB01082eEb6dCe04efA3Ae4', - expiry: '7199994275163324196', - nonce: '3', - erc20Token: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - erc20TokenAmount: '55000000000000000', - fees: [], - nft: '0x4C69dBc3a2Aa3476c3F7a1227ab70950DB1F4858', - nftId: '998', -} - -export const EXAMPLE_ETH_SELL_ORDER_SIG: ElementOrderSignature = { - signatureType: 0, - v: 27, - r: '0x59ceb2bc0e21029209e6cfa872b1224631b01da3e19d25fad9b929b8be4e6f60', - s: '0x72cadb8ed8a5bf5938829f888ff60c9ebe163954dc15af3e5d6014e8f6801b83', -} - -export interface NFTSellOrder { - maker: string - taker: string - expiry: string - nonce: string - erc20Token: string - erc20TokenAmount: string - fees: Fee[] - nft: string - nftId: string -} - -export interface ERC1155SellOrder { - maker: string - taker: string - expiry: string - nonce: string - erc20Token: string - erc20TokenAmount: string - fees: Fee[] - erc1155Token: string - erc1155TokenId: string - erc1155TokenAmount: string -} - -export type ExchangeData = { - basicCollections: [ - { - nftAddress: string - platformFee: number - royaltyFeeRecipient: string - royaltyFee: number - items: [ - { - erc20TokenAmount: string - nftId: string - } - ] - } - ] - collections: null - startNonce: number - nonce: number - hashNonce: string - platformFeeRecipient: string - v: number - r: string - s: string - listingTime: number - expirationTime: number - maker: string - hash: string - paymentToken: string -} - -export function getOrder(apiOrder: any): { order: NFTSellOrder; signature: ElementOrderSignature; value: BigNumber } { - const exchangeData: ExchangeData = JSON.parse(apiOrder.exchangeData) - - const value = BigNumber.from(exchangeData.basicCollections[0].items[0].erc20TokenAmount) - - const order = { - maker: apiOrder.maker, - taker: apiOrder.taker, - expiry: apiOrder.expirationTime, - nonce: String(exchangeData.nonce), - erc20Token: apiOrder.paymentToken, - erc20TokenAmount: value.toString(), - fees: [], // TODO: add support for calculating fees from api order - nft: apiOrder.contractAddress, - nftId: apiOrder.tokenId, - } - const signature = { - signatureType: 0, - v: exchangeData.v, - r: exchangeData.r, - s: exchangeData.s, - } - return { order, signature, value } -} diff --git a/test/integration-tests/shared/protocolHelpers/looksRareV2.ts b/test/integration-tests/shared/protocolHelpers/looksRareV2.ts deleted file mode 100644 index 1b4f6d6f..00000000 --- a/test/integration-tests/shared/protocolHelpers/looksRareV2.ts +++ /dev/null @@ -1,120 +0,0 @@ -import hre from 'hardhat' -import { BigNumber } from 'ethers' -const { ethers } = hre -import LOOKS_RARE_V2_ABI from '../abis/LooksRareV2.json' -import fs from 'fs' - -export const looksRareV2Orders = JSON.parse( - fs.readFileSync('test/integration-tests/shared/orders/LooksRareV2.json', { encoding: 'utf8' }) -) -export const looksRareV2Interface = new ethers.utils.Interface(LOOKS_RARE_V2_ABI) - -// CollectionType -export const LOOKS_RARE_V2_721_ORDER = 0 -export const LOOKS_RARE_V2_1155_ORDER = 1 - -// QuoteType -export const LOOKS_RARE_V2_BID = 0 -export const LOOKS_RARE_V2_ASK = 1 - -export type MakerOrder = { - quoteType: number - globalNonce: string - subsetNonce: string - orderNonce: string - strategyId: number - collectionType: number - collection: string - currency: string - signer: string - startTime: number - endTime: number - price: string - itemIds: string[] - amounts: string[] - additionalParameters: string -} - -export type TakerOrder = { - recipient: string - additionalParameters: string -} - -export const LOOKS_RARE_V2_TREE_LEFT = 0 -export const LOOKS_RARE_V2_TREE_RIGHT = 1 - -export type MerkleProof = { - value: string - position: number -} - -export type MerkleTree = { - root: string - proof: MerkleProof[] -} - -export type LRV2APIOrder = MakerOrder & { - id: string - hash: string - signature: string - createdAt: string - merkleRoot: string - merkleProof: MerkleProof[] -} - -export function createLooksRareV2Order( - apiOrder: LRV2APIOrder, - taker: string -): { - takerBid: TakerOrder - makerOrder: MakerOrder - makerSignature: string - value: BigNumber - merkleTree: MerkleTree -} { - const makerOrder: MakerOrder = { ...apiOrder } - - const makerSignature: string = apiOrder.signature - - const takerBid: TakerOrder = { - recipient: taker, - additionalParameters: '0x', - } - - const value: BigNumber = BigNumber.from(apiOrder.price) - - const merkleTree: MerkleTree = { - root: apiOrder.merkleRoot, - proof: apiOrder.merkleProof, - } - - return { takerBid, makerOrder, makerSignature, value, merkleTree } -} - -export function createLooksRareV2Orders( - apiOrders: LRV2APIOrder[], - taker: string -): { - takerBids: TakerOrder[] - makerOrders: MakerOrder[] - makerSignatures: string[] - totalValue: BigNumber - merkleTrees: MerkleTree[] -} { - let takerBids: TakerOrder[] = [] - let makerOrders: MakerOrder[] = [] - let makerSignatures: string[] = [] - let totalValue: BigNumber = BigNumber.from(0) - let merkleTrees: MerkleTree[] = [] - - apiOrders.forEach((apiOrder) => { - const { takerBid, makerOrder, makerSignature, value, merkleTree } = createLooksRareV2Order(apiOrder, taker) - takerBids.push(takerBid) - makerOrders.push(makerOrder) - makerSignatures.push(makerSignature) - totalValue = totalValue.add(value) - merkleTrees.push(merkleTree) - }) - - return { takerBids, makerOrders, makerSignatures, totalValue, merkleTrees } -} diff --git a/test/integration-tests/shared/protocolHelpers/permit2.ts b/test/integration-tests/shared/protocolHelpers/permit2.ts index 100c4578..c922428e 100644 --- a/test/integration-tests/shared/protocolHelpers/permit2.ts +++ b/test/integration-tests/shared/protocolHelpers/permit2.ts @@ -1,10 +1,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { BigNumber } from 'ethers' import hre from 'hardhat' -import PERMIT2_COMPILE from '../../../../artifacts/permit2/src/Permit2.sol/Permit2.json' -import { Permit2 } from '../../../../typechain' - -const { ethers } = hre +import { IPermit2 } from '../../../../typechain' const chainId: number = hre.network.config.chainId ? hre.network.config.chainId : 1 @@ -62,8 +59,6 @@ export const PERMIT2_PERMIT_BATCH_TYPE = { ], } -export const PERMIT2_INTERFACE = new ethers.utils.Interface(PERMIT2_COMPILE.abi) - export function getEip712Domain(chainId: number, verifyingContract: string) { return { name: 'Permit2', @@ -86,7 +81,7 @@ export async function signPermit( export async function getPermitSignature( permit: PermitSingle, signer: SignerWithAddress, - permit2: Permit2 + permit2: IPermit2 ): Promise { // look up the correct nonce for this permit const nextNonce = (await permit2.allowance(signer.address, permit.details.token, permit.spender)).nonce @@ -97,7 +92,7 @@ export async function getPermitSignature( export async function getPermitBatchSignature( permit: PermitBatch, signer: SignerWithAddress, - permit2: Permit2 + permit2: IPermit2 ): Promise { for (const i in permit.details) { const nextNonce = (await permit2.allowance(signer.address, permit.details[i].token, permit.spender)).nonce diff --git a/test/integration-tests/shared/protocolHelpers/seaport.ts b/test/integration-tests/shared/protocolHelpers/seaport.ts deleted file mode 100644 index 4d3766fd..00000000 --- a/test/integration-tests/shared/protocolHelpers/seaport.ts +++ /dev/null @@ -1,120 +0,0 @@ -import SEAPORT_V1_4_AND_V1_5_ABI from '../abis/Seaport.json' -import { BigNumber } from 'ethers' -import { expandTo18DecimalsBN } from '../helpers' -import { OPENSEA_CONDUIT_KEY } from '../constants' -import fs from 'fs' -import hre from 'hardhat' -const { ethers } = hre - -export const seaportV1_5Orders = JSON.parse( - fs.readFileSync('test/integration-tests/shared/orders/SeaportV1_5.json', { encoding: 'utf8' }) -) -export const seaportV1_4Orders = JSON.parse( - fs.readFileSync('test/integration-tests/shared/orders/SeaportV1_4.json', { encoding: 'utf8' }) -) -export const seaportInterface = new ethers.utils.Interface(SEAPORT_V1_4_AND_V1_5_ABI) -// @dev 0 bytes conduit key for an order that was not sent through the OpenSea conduit -export const ZERO_CONDUIT_KEY = '0x0000000000000000000000000000000000000000000000000000000000000000' - -export type OfferItem = { - itemType: BigNumber // enum - token: string // address - identifierOrCriteria: BigNumber - startAmount: BigNumber - endAmount: BigNumber -} - -export type ConsiderationItem = OfferItem & { - recipient: string -} - -export type OrderParameters = { - offerer: string // address, - offer: OfferItem[] - consideration: ConsiderationItem[] - orderType: BigNumber // enum - startTime: BigNumber - endTime: BigNumber - zoneHash: string // bytes32 - salt: BigNumber - conduitKey: string // bytes32, - totalOriginalConsiderationItems: BigNumber -} - -export type Order = { - parameters: OrderParameters - signature: string -} - -export type AdvancedOrder = Order & { - numerator: BigNumber // uint120 - denominator: BigNumber // uint120 - extraData: string // bytes -} - -export function getOrderParams(apiOrder: any): { order: Order; value: BigNumber } { - delete apiOrder.protocol_data.parameters.counter - const order = { - parameters: apiOrder.protocol_data.parameters, - signature: apiOrder.protocol_data.signature, - } - const value = calculateValue(apiOrder.protocol_data.parameters.consideration) - return { order, value } -} - -export function getAdvancedOrderParams(apiOrder: any): { advancedOrder: AdvancedOrder; value: BigNumber } { - delete apiOrder.protocol_data.parameters.counter - const advancedOrder = { - parameters: apiOrder.protocol_data.parameters, - numerator: BigNumber.from('1'), - denominator: BigNumber.from('1'), - signature: apiOrder.protocol_data.signature, - extraData: '0x00', - } - const value = calculateValue(apiOrder.protocol_data.parameters.consideration) - return { advancedOrder, value } -} - -export function calculateValue(considerations: ConsiderationItem[]): BigNumber { - return considerations.reduce( - (amt: BigNumber, consideration: ConsiderationItem) => amt.add(consideration.startAmount), - expandTo18DecimalsBN(0) - ) -} - -type BuyTownStarsReturnData = { - calldata: string - advancedOrder0: AdvancedOrder - advancedOrder1: AdvancedOrder - value: BigNumber -} - -export function purchaseDataForTwoTownstarsSeaport(receipient: string): BuyTownStarsReturnData { - const { advancedOrder: advancedOrder0, value: value1 } = getAdvancedOrderParams(seaportV1_5Orders[1]) - const { advancedOrder: advancedOrder1, value: value2 } = getAdvancedOrderParams(seaportV1_5Orders[2]) - const value = value1.add(value2) - const orderFulFillment = [[{ orderIndex: '0', itemIndex: '0' }], [{ orderIndex: '1', itemIndex: '0' }]] - const considerationFulfillment = [ - [{ orderIndex: '0', itemIndex: '0' }], - [ - { orderIndex: '0', itemIndex: '1' }, - { orderIndex: '1', itemIndex: '1' }, - ], - [ - { orderIndex: '0', itemIndex: '2' }, - { orderIndex: '1', itemIndex: '2' }, - ], - [{ orderIndex: '1', itemIndex: '0' }], - ] - - const calldata = seaportInterface.encodeFunctionData('fulfillAvailableAdvancedOrders', [ - [advancedOrder0, advancedOrder1], - [], - orderFulFillment, - considerationFulfillment, - OPENSEA_CONDUIT_KEY, - receipient, - 100, - ]) - return { calldata, advancedOrder0, advancedOrder1, value } -} diff --git a/test/integration-tests/shared/protocolHelpers/x2y2.ts b/test/integration-tests/shared/protocolHelpers/x2y2.ts deleted file mode 100644 index f12e8f69..00000000 --- a/test/integration-tests/shared/protocolHelpers/x2y2.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { BigNumber } from 'ethers' -import X2Y2_ABI from './../../shared/abis/X2Y2.json' -import fs from 'fs' -import hre from 'hardhat' -const { ethers } = hre - -export const X2Y2_INTERFACE = new ethers.utils.Interface(X2Y2_ABI) -export const x2y2Orders = JSON.parse( - fs.readFileSync('test/integration-tests/shared/orders/X2Y2.json', { encoding: 'utf8' }) -) - -export type X2Y2Order = { - input: string - order_id: number - token_id: BigNumber - price: BigNumber -} diff --git a/test/integration-tests/shared/swapRouter02Helpers.ts b/test/integration-tests/shared/swapRouter02Helpers.ts index c5e23228..2e50195c 100644 --- a/test/integration-tests/shared/swapRouter02Helpers.ts +++ b/test/integration-tests/shared/swapRouter02Helpers.ts @@ -6,30 +6,43 @@ import { encodeSqrtRatioX96, FeeAmount, nearestUsableTick, Pool, TickMath, TICK_ import { getV2PoolReserves, WETH, DAI, USDC, USDT } from './mainnetForkHelpers' import { BigNumber } from 'ethers' -const feeAmount = FeeAmount.MEDIUM const sqrtRatioX96 = encodeSqrtRatioX96(1, 1) const liquidity = 1_000_000 // v3 export const makePool = (token0: Token, token1: Token, liquidity: number) => { - return new Pool(token0, token1, feeAmount, sqrtRatioX96, liquidity, TickMath.getTickAtSqrtRatio(sqrtRatioX96), [ + const feeTier = getFeeTier(token0.address, token1.address) + return new Pool(token0, token1, feeTier, sqrtRatioX96, liquidity, TickMath.getTickAtSqrtRatio(sqrtRatioX96), [ { - index: nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeAmount]), + index: nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeTier]), liquidityNet: liquidity, liquidityGross: liquidity, }, { - index: nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount]), + index: nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeTier]), liquidityNet: -liquidity, liquidityGross: liquidity, }, ]) } +export function getFeeTier(tokenA: string, tokenB: string): FeeAmount { + const [token0, token1] = tokenA < tokenB ? [tokenA, tokenB] : [tokenB, tokenA] + + if (token0 == DAI.address && token1 == WETH.address) return FeeAmount.MEDIUM + if (token0 == USDC.address && token1 == WETH.address) return FeeAmount.LOW + if (token0 == WETH.address && token1 == USDT.address) return FeeAmount.LOW + if (token0 == DAI.address && token1 == USDC.address) return FeeAmount.LOWEST + if (token0 == DAI.address && token1 == USDT.address) return FeeAmount.LOWEST + if (token0 == USDC.address && token1 == USDT.address) return FeeAmount.LOWEST + else return FeeAmount.MEDIUM +} + export const pool_DAI_WETH = makePool(DAI, WETH, liquidity) export const pool_DAI_USDC = makePool(USDC, DAI, liquidity) export const pool_USDC_WETH = makePool(USDC, WETH, liquidity) export const pool_USDC_USDT = makePool(USDC, USDT, liquidity) +export const pool_DAI_USDT = makePool(DAI, USDT, liquidity) export const pool_WETH_USDT = makePool(USDT, WETH, liquidity) // v2 @@ -44,17 +57,15 @@ export const makePair = async (alice: SignerWithAddress, token0: Token, token1: const FEE_SIZE = 3 // v3 -export function encodePath(path: string[], fees: FeeAmount[]): string { - if (path.length != fees.length + 1) { - throw new Error('path/fee lengths do not match') - } - +export function encodePath(path: string[]): string { let encoded = '0x' - for (let i = 0; i < fees.length; i++) { + for (let i = 0; i < path.length - 1; i++) { // 20 byte encoding of the address encoded += path[i].slice(2) // 3 byte encoding of the fee - encoded += fees[i].toString(16).padStart(2 * FEE_SIZE, '0') + encoded += getFeeTier(path[i], path[i + 1]) + .toString(16) + .padStart(2 * FEE_SIZE, '0') } // encode the final token encoded += path[path.length - 1].slice(2) @@ -62,6 +73,14 @@ export function encodePath(path: string[], fees: FeeAmount[]): string { return encoded.toLowerCase() } +export function encodePathExactInput(tokens: string[]): string { + return encodePath(tokens) +} + +export function encodePathExactOutput(tokens: string[]): string { + return encodePath(tokens.slice().reverse()) +} + export function expandTo18Decimals(n: number): BigintIsh { return JSBI.BigInt(BigNumber.from(n).mul(BigNumber.from(10).pow(18)).toString()) } diff --git a/yarn.lock b/yarn.lock index e1c947b8..f1f02d00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -821,6 +821,11 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -967,137 +972,83 @@ resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz" integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== -"@nomicfoundation/ethereumjs-block@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz#fdd5c045e7baa5169abeed0e1202bf94e4481c49" - integrity sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA== - dependencies: - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-trie" "^5.0.0" - "@nomicfoundation/ethereumjs-tx" "^4.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-blockchain@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz#1a8c243a46d4d3691631f139bfb3a4a157187b0c" - integrity sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw== - dependencies: - "@nomicfoundation/ethereumjs-block" "^4.0.0" - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-ethash" "^2.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-trie" "^5.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - abstract-level "^1.0.3" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - level "^8.0.0" - lru-cache "^5.1.1" - memory-level "^1.0.0" +"@nomicfoundation/edr-darwin-arm64@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.0.tgz#bbb43f0e01f40839b0bd38c2c443cb6910ae955f" + integrity sha512-7+rraFk9tCqvfemv9Ita5vTlSBAeO/S5aDKOgGRgYt0JEKZlrX161nDW6UfzMPxWl9GOLEDUzCEaYuNmXseUlg== -"@nomicfoundation/ethereumjs-common@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz#f6bcc7753994555e49ab3aa517fc8bcf89c280b9" - integrity sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA== - dependencies: - "@nomicfoundation/ethereumjs-util" "^8.0.0" - crc-32 "^1.2.0" +"@nomicfoundation/edr-darwin-x64@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.0.tgz#b1ffcd9142418fd8498de34a7336b3f977907c86" + integrity sha512-+Hrc0mP9L6vhICJSfyGo/2taOToy1AIzVZawO3lU8Lf7oDQXfhQ4UkZnkWAs9SVu1eUwHUGGGE0qB8644piYgg== -"@nomicfoundation/ethereumjs-ethash@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz#11539c32fe0990e1122ff987d1b84cfa34774e81" - integrity sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew== - dependencies: - "@nomicfoundation/ethereumjs-block" "^4.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - abstract-level "^1.0.3" - bigint-crypto-utils "^3.0.23" - ethereum-cryptography "0.1.3" +"@nomicfoundation/edr-linux-arm64-gnu@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.0.tgz#8173d16d4f6f2b3e82ba7096d2a1ea3619d8bfa7" + integrity sha512-4HUDMchNClQrVRfVTqBeSX92hM/3khCgpZkXP52qrnJPqgbdCxosOehlQYZ65wu0b/kaaZSyvACgvCLSQ5oSzQ== -"@nomicfoundation/ethereumjs-evm@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz#99cd173c03b59107c156a69c5e215409098a370b" - integrity sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q== - dependencies: - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - "@types/async-eventemitter" "^0.2.1" - async-eventemitter "^0.2.4" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - mcl-wasm "^0.7.1" - rustbn.js "~0.2.0" +"@nomicfoundation/edr-linux-arm64-musl@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.0.tgz#b1ce293a7c3e0d9f70391e1aef1a82b83b997567" + integrity sha512-D4J935ZRL8xfnP3zIFlCI9jXInJ0loDUkCTLeCEbOf2uuDumWDghKNQlF1itUS+EHaR1pFVBbuwqq8hVK0dASg== -"@nomicfoundation/ethereumjs-rlp@^4.0.0", "@nomicfoundation/ethereumjs-rlp@^4.0.0-beta.2": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz#d9a9c5f0f10310c8849b6525101de455a53e771d" - integrity sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw== +"@nomicfoundation/edr-linux-x64-gnu@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.0.tgz#4c12c4e4bfd3d837f5663ad7cbf7cb6d5634ef83" + integrity sha512-6x7HPy+uN5Cb9N77e2XMmT6+QSJ+7mRbHnhkGJ8jm4cZvWuj2Io7npOaeHQ3YHK+TiQpTnlbkjoOIpEwpY3XZA== -"@nomicfoundation/ethereumjs-statemanager@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz#14a9d4e1c828230368f7ab520c144c34d8721e4b" - integrity sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ== - dependencies: - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-trie" "^5.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - functional-red-black-tree "^1.0.1" +"@nomicfoundation/edr-linux-x64-musl@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.0.tgz#8842004aa1a47c504f10863687da28b65dca7baa" + integrity sha512-3HFIJSXgyubOiaN4MWGXx2xhTnhwlJk0PiSYNf9+L/fjBtcRkb2nM910ZJHTvqCb6OT98cUnaKuAYdXIW2amgw== -"@nomicfoundation/ethereumjs-trie@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz#dcfbe3be53a94bc061c9767a396c16702bc2f5b7" - integrity sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A== - dependencies: - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - ethereum-cryptography "0.1.3" - readable-stream "^3.6.0" +"@nomicfoundation/edr-win32-x64-msvc@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.0.tgz#29d8bbb2edf9912a95f5453855cf17cdcb269957" + integrity sha512-CP4GsllEfXEz+lidcGYxKe5rDJ60TM5/blB5z/04ELVvw6/CK9eLcYeku7HV0jvV7VE6dADYKSdQyUkvd0El+A== -"@nomicfoundation/ethereumjs-tx@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz#59dc7452b0862b30342966f7052ab9a1f7802f52" - integrity sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w== - dependencies: - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" +"@nomicfoundation/edr@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.4.0.tgz#4895ecb6ef321136db837458949c37cce4a29459" + integrity sha512-T96DMSogO8TCdbKKctvxfsDljbhFOUKWc9fHJhSeUh71EEho2qR4951LKQF7t7UWEzguVYh/idQr5L/E3QeaMw== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.4.0" + "@nomicfoundation/edr-darwin-x64" "0.4.0" + "@nomicfoundation/edr-linux-arm64-gnu" "0.4.0" + "@nomicfoundation/edr-linux-arm64-musl" "0.4.0" + "@nomicfoundation/edr-linux-x64-gnu" "0.4.0" + "@nomicfoundation/edr-linux-x64-musl" "0.4.0" + "@nomicfoundation/edr-win32-x64-msvc" "0.4.0" + +"@nomicfoundation/ethereumjs-common@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" + integrity sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg== + dependencies: + "@nomicfoundation/ethereumjs-util" "9.0.4" + +"@nomicfoundation/ethereumjs-rlp@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" + integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== + +"@nomicfoundation/ethereumjs-tx@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" + integrity sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" ethereum-cryptography "0.1.3" -"@nomicfoundation/ethereumjs-util@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz#deb2b15d2c308a731e82977aefc4e61ca0ece6c5" - integrity sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A== +"@nomicfoundation/ethereumjs-util@9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz#84c5274e82018b154244c877b76bc049a4ed7b38" + integrity sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q== dependencies: - "@nomicfoundation/ethereumjs-rlp" "^4.0.0-beta.2" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-vm@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz#2bb50d332bf41790b01a3767ffec3987585d1de6" - integrity sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w== - dependencies: - "@nomicfoundation/ethereumjs-block" "^4.0.0" - "@nomicfoundation/ethereumjs-blockchain" "^6.0.0" - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-evm" "^1.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-statemanager" "^1.0.0" - "@nomicfoundation/ethereumjs-trie" "^5.0.0" - "@nomicfoundation/ethereumjs-tx" "^4.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - "@types/async-eventemitter" "^0.2.1" - async-eventemitter "^0.2.4" - debug "^4.3.3" + "@nomicfoundation/ethereumjs-rlp" "5.0.4" ethereum-cryptography "0.1.3" - functional-red-black-tree "^1.0.1" - mcl-wasm "^0.7.1" - rustbn.js "~0.2.0" "@nomicfoundation/hardhat-chai-matchers@1.0.4": version "1.0.4" @@ -1111,6 +1062,13 @@ deep-eql "^4.0.1" ordinal "^1.0.3" +"@nomicfoundation/hardhat-foundry@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.1.2.tgz#4f5aaa1803b8f5d974dcbc361beb72d49c815562" + integrity sha512-f5Vhj3m2qvKGpr6NAINYwNgILDsai8dVCsFb1rAVLkJxOmD2pAtfCmOH5SBVr9yUI5B1z9rbTwPBJVrqnb+PXQ== + dependencies: + chalk "^2.4.2" + "@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz#83a7367342bd053a76d04bbcf4f373fef07cf760" @@ -1304,11 +1262,6 @@ resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-4.0.0.tgz#2a8be5e108d23f3b8e6354d1618fdc2abcb00b07" integrity sha512-Rw4WHPIuwTXWcHfmn9ICQISQhmJa6Ug5IjqPYLpsKqlED2L4W2JgQ6S9hYt4IKVmT//1yxIxD+iaa5tSQEEx1A== -"@types/async-eventemitter@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz#f8e6280e87e8c60b2b938624b0a3530fb3e24712" - integrity sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg== - "@types/babel__traverse@^7.0.6": version "7.18.0" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.0.tgz#8134fd78cb39567465be65b9fdc16d378095f41f" @@ -1562,26 +1515,6 @@ "@uniswap/v3-core" "1.0.0" "@uniswap/v3-periphery" "^1.0.1" -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" - integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== - dependencies: - buffer "^6.0.3" - catering "^2.1.0" - is-buffer "^2.0.5" - level-supports "^4.0.0" - level-transcoder "^1.0.1" - module-error "^1.0.1" - queue-microtask "^1.2.3" - adm-zip@^0.4.16: version "0.4.16" resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz" @@ -1607,6 +1540,13 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" @@ -1697,20 +1637,6 @@ assertion-error@^1.1.0: resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -async-eventemitter@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" - integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== - dependencies: - async "^2.4.0" - -async@^2.4.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" - babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -1752,11 +1678,6 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - base64-sol@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/base64-sol/-/base64-sol-1.0.1.tgz" @@ -1772,18 +1693,6 @@ big.js@^5.2.2: resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bigint-crypto-utils@^3.0.23: - version "3.1.8" - resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.1.8.tgz#e2e0f40cf45488f9d7f0e32ff84152aa73819d5d" - integrity sha512-+VMV9Laq8pXLBKKKK49nOoq9bfR3j7NNQAtbA617a4nw9bVLo8rsqkKMBgM2AJWlNX9fEIyYaYX+d0laqYV4tw== - dependencies: - bigint-mod-arith "^3.1.0" - -bigint-mod-arith@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz#658e416bc593a463d97b59766226d0a3021a76b1" - integrity sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ== - bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" @@ -1809,6 +1718,20 @@ bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -1836,16 +1759,6 @@ brorand@^1.1.0: resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browser-level@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" - integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== - dependencies: - abstract-level "^1.0.2" - catering "^2.1.1" - module-error "^1.0.2" - run-parallel-limit "^1.1.0" - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" @@ -1906,33 +1819,17 @@ buffer-xor@^1.0.3: resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - bytes@3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: +camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -1942,11 +1839,6 @@ caniuse-lite@^1.0.30001370: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001382.tgz#4d37f0d0b6fffb826c8e5e1c0f4bf8ce592db949" integrity sha512-2rtJwDmSZ716Pxm1wCtbPvHtbDWAreTPxXbkc5RkKglow3Ig/4GNGazDI9/BVnXbG/wnv6r3B5FEbkfg9OcTGg== -catering@^2.1.0, catering@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" - integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== - chai-as-promised@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" @@ -2022,22 +1914,16 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -classic-level@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.2.0.tgz#2d52bdec8e7a27f534e67fdeb890abef3e643c27" - integrity sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg== - dependencies: - abstract-level "^1.0.2" - catering "^2.1.0" - module-error "^1.0.1" - napi-macros "~2.0.0" - node-gyp-build "^4.3.0" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" @@ -2112,11 +1998,6 @@ cookie@^0.4.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -crc-32@^1.2.0: - version "1.2.2" - resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" - integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== - create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" @@ -2140,7 +2021,7 @@ create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2357,11 +2238,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" @@ -2491,11 +2367,6 @@ function-bind@^1.1.1: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -2511,15 +2382,6 @@ get-func-name@^2.0.0: resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== -get-intrinsic@^1.0.2: - version "1.1.2" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2578,31 +2440,25 @@ hardhat-watcher@^2.1.1: dependencies: chokidar "^3.5.3" -hardhat@2.12.2: - version "2.12.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.12.2.tgz#6ae985007b20c1f381c6573799d66c1438c4c802" - integrity sha512-f3ZhzXy1uyQv0UXnAQ8GCBOWjzv++WJNb7bnm10SsyC3dB7vlPpsMWBNhq7aoRxKrNhX9tCev81KFV3i5BTeMQ== +hardhat@2.22.5: + version "2.22.5" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.5.tgz#7e1a4311fa9e34a1cfe337784eae06706f6469a5" + integrity sha512-9Zq+HonbXCSy6/a13GY1cgHglQRfh4qkzmj1tpPlhxJDwNVnhxlReV6K7hCWFKlOrV13EQwsdcD0rjcaQKWRZw== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/ethereumjs-block" "^4.0.0" - "@nomicfoundation/ethereumjs-blockchain" "^6.0.0" - "@nomicfoundation/ethereumjs-common" "^3.0.0" - "@nomicfoundation/ethereumjs-evm" "^1.0.0" - "@nomicfoundation/ethereumjs-rlp" "^4.0.0" - "@nomicfoundation/ethereumjs-statemanager" "^1.0.0" - "@nomicfoundation/ethereumjs-trie" "^5.0.0" - "@nomicfoundation/ethereumjs-tx" "^4.0.0" - "@nomicfoundation/ethereumjs-util" "^8.0.0" - "@nomicfoundation/ethereumjs-vm" "^6.0.0" + "@nomicfoundation/edr" "^0.4.0" + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-tx" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" "@types/bn.js" "^5.1.0" "@types/lru-cache" "^5.1.0" - abort-controller "^3.0.0" adm-zip "^0.4.16" aggregate-error "^3.0.0" ansi-escapes "^4.3.0" + boxen "^5.1.2" chalk "^2.4.2" chokidar "^3.4.0" ci-info "^2.0.0" @@ -2622,7 +2478,6 @@ hardhat@2.12.2: mnemonist "^0.38.0" mocha "^10.0.0" p-map "^4.0.0" - qs "^6.7.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" @@ -2630,7 +2485,7 @@ hardhat@2.12.2: source-map-support "^0.5.13" stacktrace-parser "^0.1.10" tsort "0.0.1" - undici "^5.4.0" + undici "^5.14.0" uuid "^8.3.2" ws "^7.4.6" @@ -2644,11 +2499,6 @@ has-flag@^4.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - has@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" @@ -2713,11 +2563,6 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - immutable@^4.0.0-rc.12: version "4.1.0" resolved "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz" @@ -2760,11 +2605,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-buffer@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - is-core-module@^2.9.0: version "2.10.0" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz" @@ -3018,27 +2858,6 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" -level-supports@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" - integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== - -level-transcoder@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" - integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== - dependencies: - buffer "^6.0.3" - module-error "^1.0.1" - -level@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394" - integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ== - dependencies: - browser-level "^1.0.1" - classic-level "^1.2.0" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" @@ -3061,7 +2880,7 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15: +lodash@^4.17.11, lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3081,13 +2900,6 @@ loupe@^2.3.1: dependencies: get-func-name "^2.0.0" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3112,11 +2924,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -mcl-wasm@^0.7.1: - version "0.7.9" - resolved "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz" - integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" @@ -3126,15 +2933,6 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -memory-level@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" - integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== - dependencies: - abstract-level "^1.0.0" - functional-red-black-tree "^1.0.1" - module-error "^1.0.1" - memorystream@^0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz" @@ -3237,11 +3035,6 @@ mocha@^10.0.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" -module-error@^1.0.1, module-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" - integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== - ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -3257,11 +3050,6 @@ nanoid@3.3.3: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== -napi-macros@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" - integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3272,7 +3060,7 @@ node-addon-api@^2.0.0: resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: +node-gyp-build@^4.2.0: version "4.5.0" resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== @@ -3292,11 +3080,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - obliterator@^2.0.0: version "2.0.4" resolved "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz" @@ -3456,18 +3239,6 @@ pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" -qs@^6.7.0: - version "6.11.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -queue-microtask@^1.2.2, queue-microtask@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -3559,18 +3330,6 @@ rlp@^2.2.3: dependencies: bn.js "^5.2.0" -run-parallel-limit@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" - integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== - dependencies: - queue-microtask "^1.2.2" - -rustbn.js@~0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz" - integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== - safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -3642,15 +3401,6 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -3718,7 +3468,7 @@ statuses@2.0.1: resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3901,6 +3651,11 @@ type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" @@ -3934,10 +3689,12 @@ typical@^2.6.0, typical@^2.6.1: resolved "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz" integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg== -undici@^5.4.0: - version "5.8.2" - resolved "https://registry.npmjs.org/undici/-/undici-5.8.2.tgz" - integrity sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A== +undici@^5.14.0: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" universalify@^0.1.0: version "0.1.2" @@ -3974,6 +3731,13 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + workerpool@6.2.1: version "6.2.1" resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" @@ -4016,11 +3780,6 @@ y18n@^5.0.5: resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" From d573cb756302cf140a55e3847b78e207d018486e Mon Sep 17 00:00:00 2001 From: diana Date: Tue, 23 Jul 2024 14:13:07 -0400 Subject: [PATCH 02/13] V3 liquidity commands (#355) * import with dif version of OZ * fix remapping issue * duplicate exports and v3 version error * remove else statement and fix format * fix duplicate IERC165 in typechain * permit, decrease, collect, burn commands * all v3 commands separately * add structs * remove mint and increaseLiquidity on v3 * test change * fix yarn.lock * pass foundry tests for now * nit - change name * spelling error * v3pm address not needed * some gas tests * check msg.sender * format * transient storage * unauthorized tests * transient storage * v3 multicall with tests * v3 call + transient storage * no decode * clean up tests * some changes * comment changes * suggestion fixes * regenerate yarn.lock file * revert yarn.lock * some changes * separate migration tests * bignumber to fix test * fix remappings for forge compile * remove command placeholder * remove transient storage * format * use v3 periphery 0.8 instead * fix gas snapshots * v3 position manager addresses * name change * remove v4 in this pr * remove unnecessary stuff * change test names * fix erc721 permit * more tests * name changes, comments, test --------- Co-authored-by: gretzke --- .gitmodules | 3 + contracts/UniversalRouter.sol | 2 + contracts/base/Dispatcher.sol | 353 ++++--- contracts/base/RouterImmutables.sol | 1 + contracts/libraries/Commands.sol | 3 + contracts/modules/MigratorImmutables.sol | 17 + contracts/modules/PaymentsImmutables.sol | 4 +- contracts/modules/V3ToV4Migrator.sol | 19 + foundry.toml | 5 - lib/v3-periphery | 1 + package.json | 6 +- remappings.txt | 6 +- script/DeployUniversalRouter.s.sol | 4 +- script/deployParameters/DeployArbitrum.s.sol | 3 +- .../DeployArbitrumGoerli.s.sol | 3 +- script/deployParameters/DeployAvalanche.s.sol | 3 +- script/deployParameters/DeployBSC.s.sol | 3 +- script/deployParameters/DeployBase.s.sol | 3 +- .../deployParameters/DeployBaseGoerli.s.sol | 3 +- script/deployParameters/DeployBlast.s.sol | 3 +- script/deployParameters/DeployCelo.s.sol | 3 +- .../DeployCeloAlfajores.s.sol | 3 +- script/deployParameters/DeployGoerli.s.sol | 3 +- script/deployParameters/DeployMainnet.s.sol | 3 +- script/deployParameters/DeployOptimism.s.sol | 3 +- .../DeployOptimismGoerli.s.sol | 3 +- script/deployParameters/DeployPolygon.s.sol | 3 +- .../DeployPolygonMumbai.s.sol | 3 +- script/deployParameters/DeploySepolia.s.sol | 3 +- test/foundry-tests/UniswapV2.t.sol | 3 +- test/foundry-tests/UniversalRouter.t.sol | 3 +- test/integration-tests/Uniswap.test.ts | 340 +++++-- .../integration-tests/UniversalRouter.test.ts | 3 - .../integration-tests/V3ToV4Migration.test.ts | 943 ++++++++++++++++++ .../gas-tests/V3ToV4Migration.gas.test.ts | 233 +++++ .../CheckOwnership.gas.test.ts.snap | 2 +- .../__snapshots__/Migrator.gas.ts.snap | 29 + .../__snapshots__/Payments.gas.test.ts.snap | 14 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 +- .../V3ToV4Migration.gas.test.ts.snap | 29 + test/integration-tests/shared/constants.ts | 2 + .../shared/deployUniversalRouter.ts | 2 + test/integration-tests/shared/encodeCall.ts | 73 ++ .../integration-tests/shared/executeRouter.ts | 81 ++ .../shared/getPermitNFTSignature.ts | 58 ++ .../shared/mainnetForkHelpers.ts | 10 +- test/integration-tests/shared/planner.ts | 14 +- yarn.lock | 78 +- 50 files changed, 2098 insertions(+), 391 deletions(-) create mode 100644 contracts/modules/MigratorImmutables.sol create mode 100644 contracts/modules/V3ToV4Migrator.sol create mode 160000 lib/v3-periphery create mode 100644 test/integration-tests/V3ToV4Migration.test.ts create mode 100644 test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts create mode 100644 test/integration-tests/gas-tests/__snapshots__/Migrator.gas.ts.snap create mode 100644 test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap create mode 100644 test/integration-tests/shared/encodeCall.ts create mode 100644 test/integration-tests/shared/executeRouter.ts create mode 100644 test/integration-tests/shared/getPermitNFTSignature.ts diff --git a/.gitmodules b/.gitmodules index e67b7ebd..79436fdb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/permit2"] path = lib/permit2 url = git@github.com:Uniswap/permit2.git +[submodule "lib/v3-periphery"] + path = lib/v3-periphery + url = https://github.com/uniswap/v3-periphery diff --git a/contracts/UniversalRouter.sol b/contracts/UniversalRouter.sol index d8212e9e..d5cf74c6 100644 --- a/contracts/UniversalRouter.sol +++ b/contracts/UniversalRouter.sol @@ -8,6 +8,7 @@ import {PaymentsImmutables, PaymentsParameters} from './modules/PaymentsImmutabl import {UniswapImmutables, UniswapParameters} from './modules/uniswap/UniswapImmutables.sol'; import {Commands} from './libraries/Commands.sol'; import {IUniversalRouter} from './interfaces/IUniversalRouter.sol'; +import {MigratorImmutables, MigratorParameters} from './modules/MigratorImmutables.sol'; contract UniversalRouter is IUniversalRouter, Dispatcher { modifier checkDeadline(uint256 deadline) { @@ -20,6 +21,7 @@ contract UniversalRouter is IUniversalRouter, Dispatcher { UniswapParameters(params.v2Factory, params.v3Factory, params.pairInitCodeHash, params.poolInitCodeHash) ) PaymentsImmutables(PaymentsParameters(params.permit2, params.weth9)) + MigratorImmutables(MigratorParameters(params.v3NFTPositionManager)) {} /// @inheritdoc IUniversalRouter diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 8f80c589..27978dff 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -6,19 +6,23 @@ import {V3SwapRouter} from '../modules/uniswap/v3/V3SwapRouter.sol'; import {BytesLib} from '../modules/uniswap/v3/BytesLib.sol'; import {Payments} from '../modules/Payments.sol'; import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol'; +import {V3ToV4Migrator} from '../modules/V3ToV4Migrator.sol'; import {Callbacks} from '../base/Callbacks.sol'; import {Commands} from '../libraries/Commands.sol'; import {LockAndMsgSender} from './LockAndMsgSender.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; +import {IERC721Permit} from '@uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol'; /// @title Decodes and Executes Commands /// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command -abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, Callbacks, LockAndMsgSender { +abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migrator, Callbacks, LockAndMsgSender { using BytesLib for bytes; error InvalidCommandType(uint256 commandType); error BalanceTooLow(); + error InvalidAction(bytes4 action); + error NotAuthorizedForToken(uint256 tokenId); /// @notice Decodes and executes the given command with the given inputs /// @param commandType The command type to execute @@ -33,171 +37,212 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, Callbacks, // 0x00 <= command < 0x20 if (command < Commands.FOURTH_IF_BOUNDARY) { - // 0x00 <= command < 0x08 - if (command < Commands.FIRST_IF_BOUNDARY) { - if (command == Commands.V3_SWAP_EXACT_IN) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountIn; - uint256 amountOutMin; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountIn := calldataload(add(inputs.offset, 0x20)) - amountOutMin := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? lockedBy : address(this); - v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); - } else if (command == Commands.V3_SWAP_EXACT_OUT) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountOut; - uint256 amountInMax; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountOut := calldataload(add(inputs.offset, 0x20)) - amountInMax := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? lockedBy : address(this); - v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); - } else if (command == Commands.PERMIT2_TRANSFER_FROM) { - // equivalent: abi.decode(inputs, (address, address, uint160)) - address token; - address recipient; - uint160 amount; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - amount := calldataload(add(inputs.offset, 0x40)) - } - permit2TransferFrom(token, lockedBy, map(recipient), amount); - } else if (command == Commands.PERMIT2_PERMIT_BATCH) { - (IAllowanceTransfer.PermitBatch memory permitBatch,) = - abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); - bytes calldata data = inputs.toBytes(1); - PERMIT2.permit(lockedBy, permitBatch, data); - } else if (command == Commands.SWEEP) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint160 amountMin; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - amountMin := calldataload(add(inputs.offset, 0x40)) - } - Payments.sweep(token, map(recipient), amountMin); - } else if (command == Commands.TRANSFER) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint256 value; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - value := calldataload(add(inputs.offset, 0x40)) - } - Payments.pay(token, map(recipient), value); - } else if (command == Commands.PAY_PORTION) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address token; - address recipient; - uint256 bips; - assembly { - token := calldataload(inputs.offset) - recipient := calldataload(add(inputs.offset, 0x20)) - bips := calldataload(add(inputs.offset, 0x40)) + // 0x00 <= command < 0x10 + if (command < Commands.SECOND_IF_BOUNDARY) { + // 0x00 <= command < 0x08 + if (command < Commands.FIRST_IF_BOUNDARY) { + if (command == Commands.V3_SWAP_EXACT_IN) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountIn; + uint256 amountOutMin; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountIn := calldataload(add(inputs.offset, 0x20)) + amountOutMin := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) + } + bytes calldata path = inputs.toBytes(3); + address payer = payerIsUser ? lockedBy : address(this); + v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); + } else if (command == Commands.V3_SWAP_EXACT_OUT) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountOut; + uint256 amountInMax; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountOut := calldataload(add(inputs.offset, 0x20)) + amountInMax := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) + } + bytes calldata path = inputs.toBytes(3); + address payer = payerIsUser ? lockedBy : address(this); + v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); + } else if (command == Commands.PERMIT2_TRANSFER_FROM) { + // equivalent: abi.decode(inputs, (address, address, uint160)) + address token; + address recipient; + uint160 amount; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + amount := calldataload(add(inputs.offset, 0x40)) + } + permit2TransferFrom(token, lockedBy, map(recipient), amount); + } else if (command == Commands.PERMIT2_PERMIT_BATCH) { + (IAllowanceTransfer.PermitBatch memory permitBatch,) = + abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); + bytes calldata data = inputs.toBytes(1); + PERMIT2.permit(lockedBy, permitBatch, data); + } else if (command == Commands.SWEEP) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address token; + address recipient; + uint160 amountMin; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + amountMin := calldataload(add(inputs.offset, 0x40)) + } + Payments.sweep(token, map(recipient), amountMin); + } else if (command == Commands.TRANSFER) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address token; + address recipient; + uint256 value; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + value := calldataload(add(inputs.offset, 0x40)) + } + Payments.pay(token, map(recipient), value); + } else if (command == Commands.PAY_PORTION) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address token; + address recipient; + uint256 bips; + assembly { + token := calldataload(inputs.offset) + recipient := calldataload(add(inputs.offset, 0x20)) + bips := calldataload(add(inputs.offset, 0x40)) + } + Payments.payPortion(token, map(recipient), bips); + } else { + // placeholder area for command 0x07 + revert InvalidCommandType(command); } - Payments.payPortion(token, map(recipient), bips); + // 0x08 <= command < 0x10 } else { - // placeholder area for command 0x07 - revert InvalidCommandType(command); + if (command == Commands.V2_SWAP_EXACT_IN) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountIn; + uint256 amountOutMin; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountIn := calldataload(add(inputs.offset, 0x20)) + amountOutMin := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) + } + address[] calldata path = inputs.toAddressArray(3); + address payer = payerIsUser ? lockedBy : address(this); + v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); + } else if (command == Commands.V2_SWAP_EXACT_OUT) { + // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) + address recipient; + uint256 amountOut; + uint256 amountInMax; + bool payerIsUser; + assembly { + recipient := calldataload(inputs.offset) + amountOut := calldataload(add(inputs.offset, 0x20)) + amountInMax := calldataload(add(inputs.offset, 0x40)) + // 0x60 offset is the path, decoded below + payerIsUser := calldataload(add(inputs.offset, 0x80)) + } + address[] calldata path = inputs.toAddressArray(3); + address payer = payerIsUser ? lockedBy : address(this); + v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); + } else if (command == Commands.PERMIT2_PERMIT) { + // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes)) + IAllowanceTransfer.PermitSingle calldata permitSingle; + assembly { + permitSingle := inputs.offset + } + bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) + PERMIT2.permit(lockedBy, permitSingle, data); + } else if (command == Commands.WRAP_ETH) { + // equivalent: abi.decode(inputs, (address, uint256)) + address recipient; + uint256 amountMin; + assembly { + recipient := calldataload(inputs.offset) + amountMin := calldataload(add(inputs.offset, 0x20)) + } + Payments.wrapETH(map(recipient), amountMin); + } else if (command == Commands.UNWRAP_WETH) { + // equivalent: abi.decode(inputs, (address, uint256)) + address recipient; + uint256 amountMin; + assembly { + recipient := calldataload(inputs.offset) + amountMin := calldataload(add(inputs.offset, 0x20)) + } + Payments.unwrapWETH9(map(recipient), amountMin); + } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { + (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = + abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); + permit2TransferFrom(batchDetails, lockedBy); + } else if (command == Commands.BALANCE_CHECK_ERC20) { + // equivalent: abi.decode(inputs, (address, address, uint256)) + address owner; + address token; + uint256 minBalance; + assembly { + owner := calldataload(inputs.offset) + token := calldataload(add(inputs.offset, 0x20)) + minBalance := calldataload(add(inputs.offset, 0x40)) + } + success = (ERC20(token).balanceOf(owner) >= minBalance); + if (!success) output = abi.encodePacked(BalanceTooLow.selector); + } else { + // placeholder area for command 0x0f + revert InvalidCommandType(command); + } } - // 0x08 <= command < 0x10 + // 0x10 <= command < 0x18 } else { - if (command == Commands.V2_SWAP_EXACT_IN) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountIn; - uint256 amountOutMin; - bool payerIsUser; + // This contract MUST be approved to spend the token since its going to be doing the call on the position manager + if (command == Commands.V3_POSITION_MANAGER_PERMIT) { + bytes4 selector; assembly { - recipient := calldataload(inputs.offset) - amountIn := calldataload(add(inputs.offset, 0x20)) - amountOutMin := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) + selector := calldataload(inputs.offset) } - address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? lockedBy : address(this); - v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); - } else if (command == Commands.V2_SWAP_EXACT_OUT) { - // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) - address recipient; - uint256 amountOut; - uint256 amountInMax; - bool payerIsUser; - assembly { - recipient := calldataload(inputs.offset) - amountOut := calldataload(add(inputs.offset, 0x20)) - amountInMax := calldataload(add(inputs.offset, 0x40)) - // 0x60 offset is the path, decoded below - payerIsUser := calldataload(add(inputs.offset, 0x80)) - } - address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? lockedBy : address(this); - v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); - } else if (command == Commands.PERMIT2_PERMIT) { - // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes)) - IAllowanceTransfer.PermitSingle calldata permitSingle; - assembly { - permitSingle := inputs.offset + if (selector != IERC721Permit.permit.selector) { + revert InvalidAction(selector); } - bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) - PERMIT2.permit(lockedBy, permitSingle, data); - } else if (command == Commands.WRAP_ETH) { - // equivalent: abi.decode(inputs, (address, uint256)) - address recipient; - uint256 amountMin; + + (success, output) = address(V3_POSITION_MANAGER).call(inputs); + } else if (command == Commands.V3_POSITION_MANAGER_CALL) { + bytes4 selector; + uint256 tokenId; assembly { - recipient := calldataload(inputs.offset) - amountMin := calldataload(add(inputs.offset, 0x20)) + selector := calldataload(inputs.offset) + // tokenId is always the first parameter in the valid actions + tokenId := calldataload(add(inputs.offset, 0x04)) } - Payments.wrapETH(map(recipient), amountMin); - } else if (command == Commands.UNWRAP_WETH) { - // equivalent: abi.decode(inputs, (address, uint256)) - address recipient; - uint256 amountMin; - assembly { - recipient := calldataload(inputs.offset) - amountMin := calldataload(add(inputs.offset, 0x20)) + + if (!isValidAction(selector)) { + revert InvalidAction(selector); } - Payments.unwrapWETH9(map(recipient), amountMin); - } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { - (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = - abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); - permit2TransferFrom(batchDetails, lockedBy); - } else if (command == Commands.BALANCE_CHECK_ERC20) { - // equivalent: abi.decode(inputs, (address, address, uint256)) - address owner; - address token; - uint256 minBalance; - assembly { - owner := calldataload(inputs.offset) - token := calldataload(add(inputs.offset, 0x20)) - minBalance := calldataload(add(inputs.offset, 0x40)) + // If any other address that is not the owner wants to call this function, it also needs to be approved (in addition to this contract) + // This can be done in 2 ways: + // 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens + // 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token + if (!isAuthorizedForToken(lockedBy, tokenId)) { + revert NotAuthorizedForToken(tokenId); } - success = (ERC20(token).balanceOf(owner) >= minBalance); - if (!success) output = abi.encodePacked(BalanceTooLow.selector); + + (success, output) = address(V3_POSITION_MANAGER).call(inputs); } else { - // placeholder area for command 0x0f + // placeholder area for command revert InvalidCommandType(command); } } diff --git a/contracts/base/RouterImmutables.sol b/contracts/base/RouterImmutables.sol index 3a13b107..cb46ef7e 100644 --- a/contracts/base/RouterImmutables.sol +++ b/contracts/base/RouterImmutables.sol @@ -8,4 +8,5 @@ struct RouterParameters { address v3Factory; bytes32 pairInitCodeHash; bytes32 poolInitCodeHash; + address v3NFTPositionManager; } diff --git a/contracts/libraries/Commands.sol b/contracts/libraries/Commands.sol index 0ba72d82..e598793d 100644 --- a/contracts/libraries/Commands.sol +++ b/contracts/libraries/Commands.sol @@ -38,6 +38,9 @@ library Commands { // The following constant defines one of the boundaries where the if blocks split commands uint256 constant SECOND_IF_BOUNDARY = 0x10; + uint256 constant V3_POSITION_MANAGER_PERMIT = 0x10; + uint256 constant V3_POSITION_MANAGER_CALL = 0x11; + // The commands are executed in nested if blocks to minimise gas consumption // The following constant defines one of the boundaries where the if blocks split commands uint256 constant THIRD_IF_BOUNDARY = 0x18; diff --git a/contracts/modules/MigratorImmutables.sol b/contracts/modules/MigratorImmutables.sol new file mode 100644 index 00000000..61989f26 --- /dev/null +++ b/contracts/modules/MigratorImmutables.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +import {INonfungiblePositionManager} from '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol'; + +struct MigratorParameters { + address v3PositionManager; +} + +contract MigratorImmutables { + /// @notice v3PositionManager address + INonfungiblePositionManager internal immutable V3_POSITION_MANAGER; + + constructor(MigratorParameters memory params) { + V3_POSITION_MANAGER = INonfungiblePositionManager(params.v3PositionManager); + } +} diff --git a/contracts/modules/PaymentsImmutables.sol b/contracts/modules/PaymentsImmutables.sol index 0e00c30d..30dc2843 100644 --- a/contracts/modules/PaymentsImmutables.sol +++ b/contracts/modules/PaymentsImmutables.sol @@ -10,10 +10,10 @@ struct PaymentsParameters { } contract PaymentsImmutables { - /// @dev WETH9 address + /// @notice WETH9 address IWETH9 internal immutable WETH9; - /// @dev Permit2 address + /// @notice Permit2 address IPermit2 internal immutable PERMIT2; enum Spenders { diff --git a/contracts/modules/V3ToV4Migrator.sol b/contracts/modules/V3ToV4Migrator.sol new file mode 100644 index 00000000..023d0366 --- /dev/null +++ b/contracts/modules/V3ToV4Migrator.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +import {MigratorImmutables} from '../modules/MigratorImmutables.sol'; +import {INonfungiblePositionManager} from '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol'; + +abstract contract V3ToV4Migrator is MigratorImmutables { + function isValidAction(bytes4 selector) internal pure returns (bool) { + return selector == INonfungiblePositionManager.decreaseLiquidity.selector + || selector == INonfungiblePositionManager.collect.selector + || selector == INonfungiblePositionManager.burn.selector; + } + + function isAuthorizedForToken(address caller, uint256 tokenId) internal view returns (bool) { + address owner = V3_POSITION_MANAGER.ownerOf(tokenId); + return caller == owner || V3_POSITION_MANAGER.getApproved(tokenId) == caller + || V3_POSITION_MANAGER.isApprovedForAll(owner, caller); + } +} diff --git a/foundry.toml b/foundry.toml index 57d8d57c..4fb734a4 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,11 +9,6 @@ evm_version = "cancun" optimizer_runs = 1_000_000 fs_permissions = [{ access = "read", path = "./script/deployParameters/"}] -remappings = [ - "solmate/=lib/solmate/", - "permit2/=lib/permit2/", -] - [fmt] line_length = 120 quote_style = 'single' diff --git a/lib/v3-periphery b/lib/v3-periphery new file mode 160000 index 00000000..b325bb09 --- /dev/null +++ b/lib/v3-periphery @@ -0,0 +1 @@ +Subproject commit b325bb0905d922ae61fcc7df85ee802e8df5e96c diff --git a/package.json b/package.json index 75eae10f..0da6296d 100644 --- a/package.json +++ b/package.json @@ -31,14 +31,14 @@ "node": ">=14" }, "dependencies": { + "@openzeppelin/contracts": "4.7.0", "@uniswap/v2-core": "1.0.1", - "@uniswap/v3-core": "1.0.0", - "@openzeppelin/contracts": "4.7.0" + "@uniswap/v3-core": "1.0.0" }, "devDependencies": { "@nomicfoundation/hardhat-chai-matchers": "1.0.4", - "@nomiclabs/hardhat-ethers": "^2.2.2", "@nomicfoundation/hardhat-foundry": "1.1.2", + "@nomiclabs/hardhat-ethers": "^2.2.2", "@typechain/ethers-v5": "^4.0.0", "@types/chai": "^4.2.6", "@types/mocha": "^5.2.7", diff --git a/remappings.txt b/remappings.txt index 0e066c42..2fa5bde4 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,7 @@ solmate/=lib/solmate/ permit2/=lib/permit2/ forge-std/=lib/forge-std/src/ -@openzeppelin/=node_modules/@openzeppelin -@uniswap=node_modules/@uniswap +@openzeppelin/contracts=node_modules/@openzeppelin/contracts +@uniswap/v3-core/=node_modules/@uniswap/v3-core/ +@uniswap/v2-core/=node_modules/@uniswap/v2-core/ +@uniswap/v3-periphery/=lib/v3-periphery/ diff --git a/script/DeployUniversalRouter.s.sol b/script/DeployUniversalRouter.s.sol index fea277d7..6f0d396b 100644 --- a/script/DeployUniversalRouter.s.sol +++ b/script/DeployUniversalRouter.s.sol @@ -39,7 +39,8 @@ abstract contract DeployUniversalRouter is Script { v2Factory: mapUnsupported(params.v2Factory), v3Factory: mapUnsupported(params.v3Factory), pairInitCodeHash: params.pairInitCodeHash, - poolInitCodeHash: params.poolInitCodeHash + poolInitCodeHash: params.poolInitCodeHash, + v3NFTPositionManager: mapUnsupported(params.v3NFTPositionManager) }); logParams(); @@ -54,6 +55,7 @@ abstract contract DeployUniversalRouter is Script { console2.log('weth9:', params.weth9); console2.log('v2Factory:', params.v2Factory); console2.log('v3Factory:', params.v3Factory); + console2.log('v3NFTPositionManager:', params.v3NFTPositionManager); } function mapUnsupported(address protocol) internal view returns (address) { diff --git a/script/deployParameters/DeployArbitrum.s.sol b/script/deployParameters/DeployArbitrum.s.sol index 873e7a2d..02cf7b5b 100644 --- a/script/deployParameters/DeployArbitrum.s.sol +++ b/script/deployParameters/DeployArbitrum.s.sol @@ -12,7 +12,8 @@ contract DeployArbitrum is DeployUniversalRouter { v2Factory: 0xf1D7CC64Fb4452F05c498126312eBE29f30Fbcf9, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 }); unsupported = 0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B; diff --git a/script/deployParameters/DeployArbitrumGoerli.s.sol b/script/deployParameters/DeployArbitrumGoerli.s.sol index d9da02d9..fa0bc42d 100644 --- a/script/deployParameters/DeployArbitrumGoerli.s.sol +++ b/script/deployParameters/DeployArbitrumGoerli.s.sol @@ -12,7 +12,8 @@ contract DeployArbitrumGoerli is DeployUniversalRouter { v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0x4893376342d5D7b3e31d4184c08b265e5aB2A3f6, pairInitCodeHash: BYTES32_ZERO, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployAvalanche.s.sol b/script/deployParameters/DeployAvalanche.s.sol index 69585595..9ab12a00 100644 --- a/script/deployParameters/DeployAvalanche.s.sol +++ b/script/deployParameters/DeployAvalanche.s.sol @@ -12,7 +12,8 @@ contract DeployAvalanche is DeployUniversalRouter { v2Factory: 0x9e5A52f57b3038F1B8EeE45F28b3C1967e22799C, v3Factory: 0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0x655C406EBFa14EE2006250925e54ec43AD184f8B }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployBSC.s.sol b/script/deployParameters/DeployBSC.s.sol index 3b7dba3a..4f5c3833 100644 --- a/script/deployParameters/DeployBSC.s.sol +++ b/script/deployParameters/DeployBSC.s.sol @@ -12,7 +12,8 @@ contract DeployBSC is DeployUniversalRouter { v2Factory: 0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6, v3Factory: 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613 }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployBase.s.sol b/script/deployParameters/DeployBase.s.sol index 43ddc254..f650533b 100644 --- a/script/deployParameters/DeployBase.s.sol +++ b/script/deployParameters/DeployBase.s.sol @@ -12,7 +12,8 @@ contract DeployBase is DeployUniversalRouter { v2Factory: 0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6, v3Factory: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1 }); unsupported = 0x9E18Efb3BE848940b0C92D300504Fb08C287FE85; diff --git a/script/deployParameters/DeployBaseGoerli.s.sol b/script/deployParameters/DeployBaseGoerli.s.sol index 2713613e..3d459bc8 100644 --- a/script/deployParameters/DeployBaseGoerli.s.sol +++ b/script/deployParameters/DeployBaseGoerli.s.sol @@ -12,7 +12,8 @@ contract DeployBaseGoerli is DeployUniversalRouter { v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0x9323c1d6D800ed51Bd7C6B216cfBec678B7d0BC2, pairInitCodeHash: BYTES32_ZERO, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: address(0) }); unsupported = 0x7B46ee9BaB49bd5b37117494689A035b0F187B59; diff --git a/script/deployParameters/DeployBlast.s.sol b/script/deployParameters/DeployBlast.s.sol index fecc843b..2e789b59 100644 --- a/script/deployParameters/DeployBlast.s.sol +++ b/script/deployParameters/DeployBlast.s.sol @@ -12,7 +12,8 @@ contract DeployBlast is DeployUniversalRouter { v2Factory: 0x5C346464d33F90bABaf70dB6388507CC889C1070, v3Factory: 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0xB218e4f7cF0533d4696fDfC419A0023D33345F28 }); unsupported = 0x5ab1B56FB16238dB874258FB7847EFe248eb8496; diff --git a/script/deployParameters/DeployCelo.s.sol b/script/deployParameters/DeployCelo.s.sol index fe0396ad..b5148247 100644 --- a/script/deployParameters/DeployCelo.s.sol +++ b/script/deployParameters/DeployCelo.s.sol @@ -12,7 +12,8 @@ contract DeployCelo is DeployUniversalRouter { v2Factory: 0x79a530c8e2fA8748B7B40dd3629C0520c2cCf03f, v3Factory: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A }); unsupported = 0x5Dc88340E1c5c6366864Ee415d6034cadd1A9897; diff --git a/script/deployParameters/DeployCeloAlfajores.s.sol b/script/deployParameters/DeployCeloAlfajores.s.sol index 12696c88..9447b4d8 100644 --- a/script/deployParameters/DeployCeloAlfajores.s.sol +++ b/script/deployParameters/DeployCeloAlfajores.s.sol @@ -12,7 +12,8 @@ contract DeployCeloAlfajores is DeployUniversalRouter { v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc, pairInitCodeHash: BYTES32_ZERO, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployGoerli.s.sol b/script/deployParameters/DeployGoerli.s.sol index b4bf6541..85f83f7f 100644 --- a/script/deployParameters/DeployGoerli.s.sol +++ b/script/deployParameters/DeployGoerli.s.sol @@ -12,7 +12,8 @@ contract DeployGoerli is DeployUniversalRouter { v2Factory: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployMainnet.s.sol b/script/deployParameters/DeployMainnet.s.sol index d706dd89..70f204c9 100644 --- a/script/deployParameters/DeployMainnet.s.sol +++ b/script/deployParameters/DeployMainnet.s.sol @@ -12,7 +12,8 @@ contract DeployMainnet is DeployUniversalRouter { v2Factory: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 }); unsupported = 0x76D631990d505E4e5b432EEDB852A60897824D68; diff --git a/script/deployParameters/DeployOptimism.s.sol b/script/deployParameters/DeployOptimism.s.sol index 1443d6de..c3097057 100644 --- a/script/deployParameters/DeployOptimism.s.sol +++ b/script/deployParameters/DeployOptimism.s.sol @@ -12,7 +12,8 @@ contract DeployOptimism is DeployUniversalRouter { v2Factory: 0x0c3c1c532F1e39EdF36BE9Fe0bE1410313E074Bf, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 }); unsupported = 0x40d51104Da22E3e77b683894E7e3E12e8FC61E65; diff --git a/script/deployParameters/DeployOptimismGoerli.s.sol b/script/deployParameters/DeployOptimismGoerli.s.sol index 8f0d993b..35125b1c 100644 --- a/script/deployParameters/DeployOptimismGoerli.s.sol +++ b/script/deployParameters/DeployOptimismGoerli.s.sol @@ -12,7 +12,8 @@ contract DeployOptimismGoerli is DeployUniversalRouter { v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0xB656dA17129e7EB733A557f4EBc57B76CFbB5d10, pairInitCodeHash: BYTES32_ZERO, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployPolygon.s.sol b/script/deployParameters/DeployPolygon.s.sol index 0189fc6c..d786990c 100644 --- a/script/deployParameters/DeployPolygon.s.sol +++ b/script/deployParameters/DeployPolygon.s.sol @@ -12,7 +12,8 @@ contract DeployPolygon is DeployUniversalRouter { v2Factory: 0x9e5A52f57b3038F1B8EeE45F28b3C1967e22799C, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 }); unsupported = 0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B; diff --git a/script/deployParameters/DeployPolygonMumbai.s.sol b/script/deployParameters/DeployPolygonMumbai.s.sol index e090513e..c26cc3b3 100644 --- a/script/deployParameters/DeployPolygonMumbai.s.sol +++ b/script/deployParameters/DeployPolygonMumbai.s.sol @@ -12,7 +12,8 @@ contract DeployPolygonMumbai is DeployUniversalRouter { v2Factory: UNSUPPORTED_PROTOCOL, v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: BYTES32_ZERO, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeploySepolia.s.sol b/script/deployParameters/DeploySepolia.s.sol index 43804e74..a1f69aa2 100644 --- a/script/deployParameters/DeploySepolia.s.sol +++ b/script/deployParameters/DeploySepolia.s.sol @@ -12,7 +12,8 @@ contract DeploySepolia is DeployUniversalRouter { v2Factory: 0xB7f907f7A9eBC822a80BD25E224be42Ce0A698A0, v3Factory: 0x0227628f3F023bb0B980b67D528571c95c6DaC1c, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, - poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 + poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, + v3NFTPositionManager: 0x1238536071E1c677A632429e3655c799b22cDA52 }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/test/foundry-tests/UniswapV2.t.sol b/test/foundry-tests/UniswapV2.t.sol index 43f5663c..eeaa324e 100644 --- a/test/foundry-tests/UniswapV2.t.sol +++ b/test/foundry-tests/UniswapV2.t.sol @@ -33,7 +33,8 @@ abstract contract UniswapV2Test is Test { v2Factory: address(FACTORY), v3Factory: address(0), pairInitCodeHash: bytes32(0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f), - poolInitCodeHash: bytes32(0) + poolInitCodeHash: bytes32(0), + v3NFTPositionManager: address(0) }); router = new UniversalRouter(params); diff --git a/test/foundry-tests/UniversalRouter.t.sol b/test/foundry-tests/UniversalRouter.t.sol index e1d3b612..bbad7eac 100644 --- a/test/foundry-tests/UniversalRouter.t.sol +++ b/test/foundry-tests/UniversalRouter.t.sol @@ -30,7 +30,8 @@ contract UniversalRouterTest is Test { v2Factory: address(0), v3Factory: address(0), pairInitCodeHash: bytes32(0), - poolInitCodeHash: bytes32(0) + poolInitCodeHash: bytes32(0), + v3NFTPositionManager: address(0) }); router = new UniversalRouter(params); testModule = new ExampleModule(); diff --git a/test/integration-tests/Uniswap.test.ts b/test/integration-tests/Uniswap.test.ts index eaf5c840..29026680 100644 --- a/test/integration-tests/Uniswap.test.ts +++ b/test/integration-tests/Uniswap.test.ts @@ -1,7 +1,5 @@ import type { Contract } from '@ethersproject/contracts' -import { TransactionReceipt } from '@ethersproject/abstract-provider' import { Pair } from '@uniswap/v2-sdk' -import { parseEvents, V2_EVENTS, V3_EVENTS } from './shared/parseEvents' import { expect } from './shared/expect' import { BigNumber, BigNumberish } from 'ethers' import { IPermit2, UniversalRouter } from '../../typechain' @@ -27,6 +25,7 @@ import { RoutePlanner, CommandType } from './shared/planner' import hre from 'hardhat' import { getPermitSignature, getPermitBatchSignature, PermitSingle } from './shared/protocolHelpers/permit2' import { encodePathExactInput, encodePathExactOutput } from './shared/swapRouter02Helpers' +import { executeRouter } from './shared/executeRouter' const { ethers } = hre describe('Uniswap V2 and V3 Tests:', () => { @@ -51,7 +50,7 @@ describe('Uniswap V2 and V3 Tests:', () => { wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) permit2 = PERMIT2.connect(bob) as IPermit2 - router = (await deployUniversalRouter()).connect(bob) as UniversalRouter + router = (await deployUniversalRouter()) as UniversalRouter planner = new RoutePlanner() // alice gives bob some tokens @@ -95,7 +94,14 @@ describe('Uniswap V2 and V3 Tests:', () => { [DAI.address, WETH.address], SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOutWETH) expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) }) @@ -126,7 +132,14 @@ describe('Uniswap V2 and V3 Tests:', () => { [DAI.address, WETH.address], SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.eq(amountOutWETH) expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.lte(maxAmountInDAI) }) @@ -159,7 +172,9 @@ describe('Uniswap V2 and V3 Tests:', () => { ]) const testCustomErrors = await (await ethers.getContractFactory('TestCustomErrors')).deploy() - await expect(executeRouter(planner)).to.be.revertedWithCustomError(testCustomErrors, 'UnsafeCast') + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(testCustomErrors, 'UnsafeCast') }) it('V3 exactIn, permiting the exact amount', async () => { @@ -193,7 +208,14 @@ describe('Uniswap V2 and V3 Tests:', () => { path, SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOutWETH) expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) }) @@ -229,7 +251,14 @@ describe('Uniswap V2 and V3 Tests:', () => { path, SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.eq(amountOutWETH) expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.lte(maxAmountInDAI) }) @@ -254,7 +283,14 @@ describe('Uniswap V2 and V3 Tests:', () => { [DAI.address, WETH.address], SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gt(minAmountOut) }) @@ -268,7 +304,14 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_MSG_SENDER, ]) planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, 0]) - const { daiBalanceBefore, daiBalanceAfter } = await executeRouter(planner) + const { daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gt(amountOut) }) @@ -288,7 +331,7 @@ describe('Uniswap V2 and V3 Tests:', () => { const wethBalanceBeforeAlice = await wethContract.balanceOf(alice.address) const wethBalanceBeforeBob = await wethContract.balanceOf(bob.address) - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) + await router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) const wethBalanceAfterAlice = await wethContract.balanceOf(alice.address) const wethBalanceAfterBob = await wethContract.balanceOf(bob.address) @@ -313,7 +356,14 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gt(minAmountOut) }) }) @@ -329,7 +379,14 @@ describe('Uniswap V2 and V3 Tests:', () => { ]) planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) - const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter(planner) + const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1Out: wethTraded } = v2SwapEventArgs! expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethTraded.sub(gasSpent)) @@ -347,7 +404,14 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, amountOut]) planner.addCommand(CommandType.SWEEP, [DAI.address, MSG_SENDER, 0]) - const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter(planner) + const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1Out: wethTraded } = v2SwapEventArgs! expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(amountOut.sub(gasSpent)) expect(wethTraded).to.eq(amountOut) @@ -371,10 +435,9 @@ describe('Uniswap V2 and V3 Tests:', () => { const { commands, inputs } = planner - await expect(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)).to.changeEtherBalances( - [alice, bob], - [totalPortion, actualAmountOut] - ) + await expect( + router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) + ).to.changeEtherBalances([alice, bob], [totalPortion, actualAmountOut]) }) }) @@ -392,7 +455,15 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_MSG_SENDER, ]) - const { daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs } = await executeRouter(planner, amountIn) + const { daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract, + amountIn + ) const { amount0Out: daiTraded } = v2SwapEventArgs! expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gt(minAmountOut) @@ -414,7 +485,7 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs, gasSpent } = - await executeRouter(planner, value) + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, value) const { amount0Out: daiTraded, amount1In: wethTraded } = v2SwapEventArgs! expect(daiBalanceAfter.sub(daiBalanceBefore)).gt(amountOut) // rounding expect(daiBalanceAfter.sub(daiBalanceBefore)).eq(daiTraded) @@ -459,7 +530,14 @@ describe('Uniswap V2 and V3 Tests:', () => { const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) addV3ExactInTrades(planner, 1, amountOutMin) - const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1: wethTraded } = v3SwapEventArgs! expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(amountOutMin) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1)) @@ -483,7 +561,7 @@ describe('Uniswap V2 and V3 Tests:', () => { wethBalanceAfter, usdcBalanceBefore, usdcBalanceAfter, - } = await executeRouter(planner) + } = await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) expect(daiBalanceBefore.sub(amountIn)).to.eq(daiBalanceAfter) expect(wethBalanceAfter).to.eq(wethBalanceBefore) @@ -497,7 +575,14 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) - const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount0: daiTraded } = v3SwapEventArgs! expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(amountOut) expect(daiTraded).to.be.lt(amountInMax) @@ -512,7 +597,7 @@ describe('Uniswap V2 and V3 Tests:', () => { const { commands, inputs } = planner const balanceWethBefore = await wethContract.balanceOf(bob.address) - await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) + await router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) const balanceWethAfter = await wethContract.balanceOf(bob.address) expect(balanceWethAfter.sub(balanceWethBefore)).to.eq(amountOut) }) @@ -524,7 +609,14 @@ describe('Uniswap V2 and V3 Tests:', () => { addV3ExactInTrades(planner, 1, amountOutMin, ADDRESS_THIS) planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) - const { ethBalanceBefore, ethBalanceAfter, v3SwapEventArgs, gasSpent } = await executeRouter(planner) + const { ethBalanceBefore, ethBalanceAfter, v3SwapEventArgs, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1: wethTraded } = v3SwapEventArgs! expect(ethBalanceAfter.sub(ethBalanceBefore)).to.be.gte(amountOutMin.sub(gasSpent)) @@ -545,7 +637,14 @@ describe('Uniswap V2 and V3 Tests:', () => { ]) planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, amountOut]) - const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter(planner) + const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(amountOut.sub(gasSpent)) }) @@ -561,6 +660,11 @@ describe('Uniswap V2 and V3 Tests:', () => { const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, gasSpent } = await executeRouter( planner, + bob, + router, + wethContract, + daiContract, + usdcContract, amountIn ) @@ -577,7 +681,7 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, gasSpent, v3SwapEventArgs } = - await executeRouter(planner, amountInMax) + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, amountInMax) const { amount0: daiTraded, amount1: wethTraded } = v3SwapEventArgs! expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(daiTraded) @@ -612,7 +716,14 @@ describe('Uniswap V2 and V3 Tests:', () => { // amountIn of 0 because the USDC is already in the pair planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, v2AmountOutMin, v2Tokens, SOURCE_MSG_SENDER]) - const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1Out: wethTraded } = v2SwapEventArgs! expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded) }) @@ -639,7 +750,14 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_ROUTER, ]) - const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1: wethTraded } = v3SwapEventArgs! expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1)) }) @@ -663,7 +781,14 @@ describe('Uniswap V2 and V3 Tests:', () => { // 3) trade route2 and return tokens to bob planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut2, route2, SOURCE_MSG_SENDER]) - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) }) @@ -698,7 +823,14 @@ describe('Uniswap V2 and V3 Tests:', () => { // 3) trade route2 and return tokens to bob planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut2, route2, SOURCE_MSG_SENDER]) - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) }) @@ -727,7 +859,14 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_MSG_SENDER, ]) - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) }) @@ -780,7 +919,14 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_MSG_SENDER, ]) - const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter(planner) + const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) }) @@ -854,7 +1000,14 @@ describe('Uniswap V2 and V3 Tests:', () => { SOURCE_ROUTER, ]) - const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter(planner) + const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(minAmountOut1USDC.add(minAmountOut2USDC)) }) @@ -877,7 +1030,14 @@ describe('Uniswap V2 and V3 Tests:', () => { // aggregate slippage check planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, minAmountOut]) - const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter(planner) + const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) const { amount1Out: wethOutV2 } = v2SwapEventArgs! let { amount1: wethOutV3 } = v3SwapEventArgs! @@ -905,6 +1065,11 @@ describe('Uniswap V2 and V3 Tests:', () => { const { usdcBalanceBefore, usdcBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( planner, + bob, + router, + wethContract, + daiContract, + usdcContract, value ) const { amount0Out: usdcOutV2 } = v2SwapEventArgs! @@ -930,7 +1095,12 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, expandTo18DecimalsBN(0.0005)]) const { ethBalanceBefore, ethBalanceAfter, gasSpent, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( - planner + planner, + bob, + router, + wethContract, + daiContract, + usdcContract ) const { amount1Out: wethOutV2 } = v2SwapEventArgs! let { amount1: wethOutV3 } = v3SwapEventArgs! @@ -964,7 +1134,14 @@ describe('Uniswap V2 and V3 Tests:', () => { // aggregate slippage check planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, fullAmountOut]) - const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter(planner) + const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) // TODO: permit2 test alice doesn't send more than maxAmountIn DAI expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(fullAmountOut.sub(gasSpent)) @@ -1025,7 +1202,12 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addSubPlan(subplan) const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner + planner, + bob, + router, + wethContract, + daiContract, + usdcContract ) expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(planOneV2AmountIn.add(planOneV3AmountIn)) @@ -1076,7 +1258,12 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addSubPlan(subplan) const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner + planner, + bob, + router, + wethContract, + daiContract, + usdcContract ) // dai balance should be unchanged as the weth sweep failed @@ -1131,7 +1318,12 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addSubPlan(subplan) const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner + planner, + bob, + router, + wethContract, + daiContract, + usdcContract ) // dai and usdc balances both unchanged because both trades failed @@ -1183,7 +1375,12 @@ describe('Uniswap V2 and V3 Tests:', () => { planner.addSubPlan(subplan) const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner + planner, + bob, + router, + wethContract, + daiContract, + usdcContract ) // dai balance has changed as this trade should succeed @@ -1195,65 +1392,4 @@ describe('Uniswap V2 and V3 Tests:', () => { }) }) }) - - type V2SwapEventArgs = { - amount0In: BigNumber - amount0Out: BigNumber - amount1In: BigNumber - amount1Out: BigNumber - } - - type V3SwapEventArgs = { - amount0: BigNumber - amount1: BigNumber - } - - type ExecutionParams = { - wethBalanceBefore: BigNumber - wethBalanceAfter: BigNumber - daiBalanceBefore: BigNumber - daiBalanceAfter: BigNumber - usdcBalanceBefore: BigNumber - usdcBalanceAfter: BigNumber - ethBalanceBefore: BigNumber - ethBalanceAfter: BigNumber - v2SwapEventArgs: V2SwapEventArgs | undefined - v3SwapEventArgs: V3SwapEventArgs | undefined - receipt: TransactionReceipt - gasSpent: BigNumber - } - - async function executeRouter(planner: RoutePlanner, value?: BigNumberish): Promise { - const ethBalanceBefore: BigNumber = await ethers.provider.getBalance(bob.address) - const wethBalanceBefore: BigNumber = await wethContract.balanceOf(bob.address) - const daiBalanceBefore: BigNumber = await daiContract.balanceOf(bob.address) - const usdcBalanceBefore: BigNumber = await usdcContract.balanceOf(bob.address) - - const { commands, inputs } = planner - - const receipt = await (await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value })).wait() - const gasSpent = receipt.gasUsed.mul(receipt.effectiveGasPrice) - const v2SwapEventArgs = parseEvents(V2_EVENTS, receipt)[0]?.args as unknown as V2SwapEventArgs - const v3SwapEventArgs = parseEvents(V3_EVENTS, receipt)[0]?.args as unknown as V3SwapEventArgs - - const ethBalanceAfter: BigNumber = await ethers.provider.getBalance(bob.address) - const wethBalanceAfter: BigNumber = await wethContract.balanceOf(bob.address) - const daiBalanceAfter: BigNumber = await daiContract.balanceOf(bob.address) - const usdcBalanceAfter: BigNumber = await usdcContract.balanceOf(bob.address) - - return { - wethBalanceBefore, - wethBalanceAfter, - daiBalanceBefore, - daiBalanceAfter, - usdcBalanceBefore, - usdcBalanceAfter, - ethBalanceBefore, - ethBalanceAfter, - v2SwapEventArgs, - v3SwapEventArgs, - receipt, - gasSpent, - } - } }) diff --git a/test/integration-tests/UniversalRouter.test.ts b/test/integration-tests/UniversalRouter.test.ts index 62c91aac..f7d58a15 100644 --- a/test/integration-tests/UniversalRouter.test.ts +++ b/test/integration-tests/UniversalRouter.test.ts @@ -1,5 +1,4 @@ import { UniversalRouter, ERC20, IWETH9, IPermit2 } from '../../typechain' -import { BigNumber } 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' @@ -22,7 +21,6 @@ import { makePair } from './shared/swapRouter02Helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expandTo18DecimalsBN } from './shared/helpers' import hre from 'hardhat' -import { findCustomErrorSelector } from './shared/parseEvents' const { ethers } = hre const routerInterface = new ethers.utils.Interface(ROUTER_ABI) @@ -33,7 +31,6 @@ describe('UniversalRouter', () => { let permit2: IPermit2 let daiContract: ERC20 let wethContract: IWETH9 - let mockLooksRareToken: ERC20 let pair_DAI_WETH: Pair beforeEach(async () => { diff --git a/test/integration-tests/V3ToV4Migration.test.ts b/test/integration-tests/V3ToV4Migration.test.ts new file mode 100644 index 00000000..b305e886 --- /dev/null +++ b/test/integration-tests/V3ToV4Migration.test.ts @@ -0,0 +1,943 @@ +import type { Contract } from '@ethersproject/contracts' +import { expect } from './shared/expect' +import { BigNumber } from 'ethers' +import { UniversalRouter, INonfungiblePositionManager } from '../../typechain' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { resetFork, WETH, DAI, USDC, V3_NFT_POSITION_MANAGER } from './shared/mainnetForkHelpers' +import { ZERO_ADDRESS, ALICE_ADDRESS, MAX_UINT, MAX_UINT128 } from './shared/constants' +import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import deployUniversalRouter from './shared/deployUniversalRouter' +import { RoutePlanner, CommandType } from './shared/planner' +import hre from 'hardhat' +import getPermitNFTSignature from './shared/getPermitNFTSignature' +import { FeeAmount } from '@uniswap/v3-sdk' +import { encodeERC721Permit, encodeDecreaseLiquidity, encodeCollect, encodeBurn } from './shared/encodeCall' +import { executeRouter } from './shared/executeRouter' +const { ethers } = hre + +describe('V3 to V4 Migration Tests:', () => { + let alice: SignerWithAddress + let bob: SignerWithAddress + let eve: SignerWithAddress + let router: UniversalRouter + let daiContract: Contract + let wethContract: Contract + let usdcContract: Contract + let planner: RoutePlanner + let v3NFTPositionManager: INonfungiblePositionManager + + let tokenIdv3: BigNumber + + beforeEach(async () => { + await resetFork() + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ALICE_ADDRESS], + }) + alice = await ethers.getSigner(ALICE_ADDRESS) + bob = (await ethers.getSigners())[1] + eve = (await ethers.getSigners())[2] + daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) + wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) + usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) + v3NFTPositionManager = V3_NFT_POSITION_MANAGER.connect(bob) as INonfungiblePositionManager + router = (await deployUniversalRouter()) as UniversalRouter + planner = new RoutePlanner() + + // alice gives bob some tokens + await daiContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100000)) + await wethContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100)) + await usdcContract.connect(alice).transfer(bob.address, expandTo6DecimalsBN(100000)) + }) + + describe('Migrator', () => { + beforeEach(async () => { + // Bob max-approves the v3PM to access his USDC and WETH + await usdcContract.connect(bob).approve(v3NFTPositionManager.address, MAX_UINT) + await wethContract.connect(bob).approve(v3NFTPositionManager.address, MAX_UINT) + + let bobUSDCBalanceBefore = await usdcContract.balanceOf(bob.address) + let bobWETHBalanceBefore = await wethContract.balanceOf(bob.address) + + // need to mint the nft to bob + const tx = await v3NFTPositionManager.mint({ + token0: USDC.address, + token1: WETH.address, + fee: FeeAmount.LOW, + tickLower: 0, + tickUpper: 194980, + amount0Desired: expandTo6DecimalsBN(2500), + amount1Desired: expandTo18DecimalsBN(1), + amount0Min: 0, + amount1Min: 0, + recipient: bob.address, + deadline: MAX_UINT, + }) + + let bobUSDCBalanceAfter = await usdcContract.balanceOf(bob.address) + let bobWETHBalanceAfter = await wethContract.balanceOf(bob.address) + + let usdcSpent = bobUSDCBalanceBefore.sub(bobUSDCBalanceAfter) + let wethSpent = bobWETHBalanceBefore.sub(bobWETHBalanceAfter) + + // check that the USDC and WETH were spent + expect(usdcSpent > 0 || wethSpent > 0) + const receipt = await tx.wait() + + const transferEvent = receipt.events?.find((event) => event.event === 'IncreaseLiquidity') + + tokenIdv3 = transferEvent?.args?.tokenId + }) + + describe('erc721permit', () => { + it('erc721 permit succeeds', async () => { + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + expect((await v3NFTPositionManager.positions(tokenIdv3)).operator).to.eq(ZERO_ADDRESS) + + // bob permits the router to spend token + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + expect((await v3NFTPositionManager.positions(tokenIdv3)).operator).to.eq(router.address) + }) + + it('need to call permit when executing V3_POSITION_MANAGER_PERMIT command', async () => { + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedDecreaseCall]) + + // trying to execute the permit commmand by calling decrease liquidity + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'InvalidAction') + }) + + it('only owner of the token can generate a signature to permit another address', async () => { + // eve is not the owner of the token + const { v, r, s } = await getPermitNFTSignature(eve, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + // eve generated a signature for bob's token - fails since eve is not the owner + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'ExecutionFailed') + }) + + it('other address can call permit on behalf of someone as long as owner of the token generated the signature properly', async () => { + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + expect((await v3NFTPositionManager.positions(tokenIdv3)).operator).to.eq(ZERO_ADDRESS) + + // eve can permit the router for bob using bob's signature + await executeRouter(planner, eve, router, wethContract, daiContract, usdcContract) + + expect((await v3NFTPositionManager.positions(tokenIdv3)).operator).to.eq(router.address) + }) + }) + + describe('decrease liquidity', () => { + it('decrease liquidity succeeds', async () => { + // first we need to permit the router to spend the nft + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + expect(liquidity).to.be.gt(0) + let owed0Before = position.tokensOwed0 + let owed1Before = position.tokensOwed1 + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + position = await v3NFTPositionManager.positions(tokenIdv3) + liquidity = position.liquidity + let owed0After = position.tokensOwed0 + let owed1After = position.tokensOwed1 + + expect(liquidity).to.eq(0) + expect(owed0After).to.be.gt(owed0Before) + expect(owed1After).to.be.gt(owed1Before) + }) + + it('cannot decrease liquidity without permiting the router', async () => { + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'ExecutionFailed') + }) + + it('cannot call decrease liquidity with improper function selector', async () => { + // first we need to permit the router to spend the nft + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const BAD_DECREASE_LIQUIDITY_STRUCT = + '(uint256 tokenId,uint256 liquidity,uint256 amount0Min,uint256 amount1Min,uint256 deadline)' + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const abi = new ethers.utils.AbiCoder() + const encodedParams = abi.encode([BAD_DECREASE_LIQUIDITY_STRUCT], [decreaseParams]) + const functionSignature = ethers.utils + .id('decreaseLiquidity((uint256,uint128,uint256,uint256))') + .substring(0, 10) + const encodedCall = functionSignature + encodedParams.substring(2) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCall]) + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'InvalidAction') + }) + + it('fails if decrease liquidity call fails', async () => { + // first we need to permit the router to spend the nft + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + // set the deadline to 0 + const decreaseParams = { tokenId: tokenIdv3, liquidity: liquidity, amount0Min: 0, amount1Min: 0, deadline: '0' } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + // call to decrease liquidity fails since the deadline is set to 0 + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'ExecutionFailed') + }) + + it('cannot call decrease liquidity if not authorized', async () => { + // bob creates a signature for the router to spend the token + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + planner = new RoutePlanner() + + // transfer the token to eve + await v3NFTPositionManager.transferFrom(bob.address, eve.address, tokenIdv3) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + // bob is trying to use the token that is now owned by eve. he is not authorized to do so + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'NotAuthorizedForToken') + }) + + it('eve permits bob for all tokens - he can call decrease even though he is not the owner', async () => { + // transfer the token to eve + await v3NFTPositionManager.transferFrom(bob.address, eve.address, tokenIdv3) + + // eve permits bob to spend all of her tokens + await v3NFTPositionManager.connect(eve).setApprovalForAll(bob.address, true) + + // eve creates a signature for the router to spend the token + let { v, r, s } = await getPermitNFTSignature(eve, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const params = { tokenId: tokenIdv3, liquidity: liquidity, amount0Min: 0, amount1Min: 0, deadline: MAX_UINT } + + const encodedDecreaseCall = encodeDecreaseLiquidity(params) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + }) + + it('eve permits bob for the token and approves router for all her tokens - he can call decrease even though he is not the owner', async () => { + // transfer the token to eve + await v3NFTPositionManager.transferFrom(bob.address, eve.address, tokenIdv3) + + // eve approves the router to spend all of her tokens + await v3NFTPositionManager.connect(eve).setApprovalForAll(router.address, true) + + // eve creates a signature for bob to spend the token + let { v, r, s } = await getPermitNFTSignature(eve, v3NFTPositionManager, bob.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: bob.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const params = { tokenId: tokenIdv3, liquidity: liquidity, amount0Min: 0, amount1Min: 0, deadline: MAX_UINT } + + const encodedDecreaseCall = encodeDecreaseLiquidity(params) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + }) + }) + + describe('collect liquidity', () => { + it('collect succeeds', async () => { + let bobToken0BalanceBefore = await usdcContract.balanceOf(bob.address) + let bobToken1BalanceBefore = await wethContract.balanceOf(bob.address) + + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + position = await v3NFTPositionManager.positions(tokenIdv3) + let owed0 = position.tokensOwed0 + let owed1 = position.tokensOwed1 + + expect(owed0).to.eq(0) + expect(owed1).to.eq(0) + + let bobToken0BalanceAfter = await usdcContract.balanceOf(bob.address) + let bobToken1BalanceAfter = await wethContract.balanceOf(bob.address) + + // bob is the recipient - he should have received the owed tokens + expect(bobToken0BalanceAfter).to.be.gt(bobToken0BalanceBefore) + expect(bobToken1BalanceAfter).to.be.gt(bobToken1BalanceBefore) + }) + + it('collecting the correct amount', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + let bobToken0BalanceBefore: BigNumber = await usdcContract.balanceOf(bob.address) + let bobToken1BalanceBefore: BigNumber = await wethContract.balanceOf(bob.address) + + position = await v3NFTPositionManager.positions(tokenIdv3) + let owed0Before = position.tokensOwed0 + let owed1Before = position.tokensOwed1 + let liquidityBefore = position.liquidity + + expect(liquidityBefore).to.be.eq(0) + + planner = new RoutePlanner() + + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + await v3NFTPositionManager.setApprovalForAll(eve.address, true) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + await executeRouter(planner, eve, router, wethContract, daiContract, usdcContract) + + position = await v3NFTPositionManager.positions(tokenIdv3) + let owed0After = position.tokensOwed0 + let owed1After = position.tokensOwed1 + + expect(owed0After).to.eq(0) + expect(owed1After).to.eq(0) + + let bobToken0BalanceAfter: BigNumber = await usdcContract.balanceOf(bob.address) + let bobToken1BalanceAfter: BigNumber = await wethContract.balanceOf(bob.address) + + // bob is the recipient - he should have received the owed tokens + expect(bobToken0BalanceAfter.sub(bobToken0BalanceBefore)).to.be.eq(owed0Before) + expect(bobToken1BalanceAfter.sub(bobToken1BalanceBefore)).to.be.eq(owed1Before) + }) + + it('collect succeeds with router as recipient', async () => { + let routerToken0BalanceBefore = await usdcContract.balanceOf(router.address) + let routerToken1BalanceBefore = await wethContract.balanceOf(router.address) + + // router should have no balance of the tokens + expect(routerToken0BalanceBefore).to.be.eq(0) + expect(routerToken1BalanceBefore).to.be.eq(0) + + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const collectParams = { + tokenId: tokenIdv3, + recipient: router.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + let routerToken0BalanceAfter = await usdcContract.balanceOf(router.address) + let routerToken1BalanceAfter = await wethContract.balanceOf(router.address) + + // router is the recipient - router should have received the owed tokens + // (there is sweep function if necessary) + expect(routerToken0BalanceAfter).to.be.gt(routerToken0BalanceBefore) + expect(routerToken1BalanceAfter).to.be.gt(routerToken1BalanceBefore) + }) + + it('cannot call collect with improper signature', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const COLLECT_STRUCT = '(uint256 tokenId,address recipient,uint256 amount0Max,uint256 amount1Max)' + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const abi = new ethers.utils.AbiCoder() + const encodedCollectParams = abi.encode([COLLECT_STRUCT], [collectParams]) + const functionSignatureCollect = ethers.utils.id('collect((uint256,address,uint128))').substring(0, 10) + const encodedCollectCall = functionSignatureCollect + encodedCollectParams.substring(2) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'InvalidAction') + }) + + it('cannot call collect with improper params', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const COLLECT_STRUCT = '(uint256 tokenId,address recipient,uint256 amount0Max)' + const collectParams = { tokenId: tokenIdv3, recipient: bob.address, amount0Max: MAX_UINT128 } + + const abi = new ethers.utils.AbiCoder() + const encodedCollectParams = abi.encode([COLLECT_STRUCT], [collectParams]) + const functionSignatureCollect = ethers.utils.id('collect((uint256,address,uint128,uint128))').substring(0, 10) + const encodedCollectCall = functionSignatureCollect + encodedCollectParams.substring(2) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'ExecutionFailed') + }) + + it('cannot call collect if the router is not approved for that tokenid', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + planner = new RoutePlanner() + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + // approved on the decrease call + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + // not approved on the collect call + const collectParams = { + tokenId: BigNumber.from(1), + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'NotAuthorizedForToken') + }) + + it('address cannot call collect if unapproved for that tokenid', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + planner = new RoutePlanner() + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + // approved on the decrease call + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + const collectParams = { + tokenId: tokenIdv3, + recipient: eve.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + planner = new RoutePlanner() + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + // not approved on the collect call + await expect( + executeRouter(planner, eve, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'NotAuthorizedForToken') + }) + }) + + describe('burn liquidity', () => { + it('burn succeeds', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + const encodedBurnCall = encodeBurn(tokenIdv3) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedBurnCall]) + + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + expect(await v3NFTPositionManager.balanceOf(bob.address)).to.eq(0) + }) + + it('burn fails if you arent approved spender of nft', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + // bob decreases and collects the liquidity + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + planner = new RoutePlanner() + + const encodedBurnCall = encodeBurn(tokenIdv3) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedBurnCall]) + + // eve tries to burn the token - she is not approved to do so + await expect( + executeRouter(planner, eve, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(router, 'NotAuthorizedForToken') + }) + }) + }) +}) diff --git a/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts b/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts new file mode 100644 index 00000000..e1610bee --- /dev/null +++ b/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts @@ -0,0 +1,233 @@ +import type { Contract } from '@ethersproject/contracts' +import snapshotGasCost from '@uniswap/snapshot-gas-cost' +import deployUniversalRouter from '../shared/deployUniversalRouter' +import { BigNumber } from 'ethers' +import { UniversalRouter, INonfungiblePositionManager } from '../../../typechain' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { resetFork, WETH, DAI, USDC, V3_NFT_POSITION_MANAGER } from '../shared/mainnetForkHelpers' +import { ALICE_ADDRESS, DEADLINE, MAX_UINT, MAX_UINT128 } from '../shared/constants' +import { expandTo18DecimalsBN, expandTo6DecimalsBN } from '../shared/helpers' +import getPermitNFTSignature from '../shared/getPermitNFTSignature' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import hre from 'hardhat' +import { RoutePlanner, CommandType } from '../shared/planner' +import { FeeAmount } from '@uniswap/v3-sdk' +import { encodeERC721Permit, encodeDecreaseLiquidity, encodeCollect, encodeBurn } from '../shared/encodeCall' +const { ethers } = hre + +describe('V3 to V4 Migration Gas Tests', () => { + let alice: SignerWithAddress + let bob: SignerWithAddress + let router: UniversalRouter + let daiContract: Contract + let wethContract: Contract + let usdcContract: Contract + let planner: RoutePlanner + let v3NFTPositionManager: INonfungiblePositionManager + + let tokenIdv3: BigNumber + + beforeEach(async () => { + await resetFork() + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ALICE_ADDRESS], + }) + alice = await ethers.getSigner(ALICE_ADDRESS) + bob = (await ethers.getSigners())[1] + daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) + wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) + usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) + v3NFTPositionManager = V3_NFT_POSITION_MANAGER.connect(bob) as INonfungiblePositionManager + router = (await deployUniversalRouter()).connect(bob) as UniversalRouter + planner = new RoutePlanner() + + // alice gives bob some tokens + await daiContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100000)) + await wethContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100)) + await usdcContract.connect(alice).transfer(bob.address, expandTo6DecimalsBN(100000)) + }) + + describe('Migrator', () => { + beforeEach(async () => { + // Bob max-approves the v3PM to access his USDC and WETH + await usdcContract.connect(bob).approve(v3NFTPositionManager.address, MAX_UINT) + await wethContract.connect(bob).approve(v3NFTPositionManager.address, MAX_UINT) + + // need to mint the nft to bob + const tx = await v3NFTPositionManager.mint({ + token0: USDC.address, + token1: WETH.address, + fee: FeeAmount.LOW, + tickLower: 0, + tickUpper: 194980, + amount0Desired: expandTo6DecimalsBN(2500), + amount1Desired: expandTo18DecimalsBN(1), + amount0Min: 0, + amount1Min: 0, + recipient: bob.address, + deadline: MAX_UINT, + }) + + const receipt = await tx.wait() + + const transferEvent = receipt.events?.find((event) => event.event === 'IncreaseLiquidity') + + tokenIdv3 = transferEvent?.args?.tokenId + }) + + describe('erc721permit', () => { + it('gas: erc721permit', async () => { + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + const { commands, inputs } = planner + await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) + }) + }) + describe('decrease liquidity', () => { + it('gas: erc721permit + decreaseLiquidity', async () => { + // first we need to permit the router to spend the nft + const { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + + const { commands, inputs } = planner + await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) + }) + }) + describe('collect', () => { + it('gas: erc721permit + decreaseLiquidity + collect', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + + const { commands, inputs } = planner + await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) + }) + }) + + describe('burn', () => { + it('gas: erc721permit + decreaseLiquidity + collect + burn', async () => { + // first we need to permit the router to spend the nft + let { v, r, s } = await getPermitNFTSignature(bob, v3NFTPositionManager, router.address, tokenIdv3, MAX_UINT) + const erc721PermitParams = { + spender: router.address, + tokenId: tokenIdv3, + deadline: MAX_UINT, + v: v, + r: r, + s: s, + } + + const encodedErc721PermitCall = encodeERC721Permit(erc721PermitParams) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_PERMIT, [encodedErc721PermitCall]) + + let position = await v3NFTPositionManager.positions(tokenIdv3) + let liquidity = position.liquidity + + const decreaseParams = { + tokenId: tokenIdv3, + liquidity: liquidity, + amount0Min: 0, + amount1Min: 0, + deadline: MAX_UINT, + } + + const encodedDecreaseCall = encodeDecreaseLiquidity(decreaseParams) + + const collectParams = { + tokenId: tokenIdv3, + recipient: bob.address, + amount0Max: MAX_UINT128, + amount1Max: MAX_UINT128, + } + + const encodedCollectCall = encodeCollect(collectParams) + + const encodedBurnCall = encodeBurn(tokenIdv3) + + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedDecreaseCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedCollectCall]) + planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [encodedBurnCall]) + + const { commands, inputs } = planner + await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)) + }) + }) + }) +}) diff --git a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap index 51121507..b3a5b59a 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Check Ownership Gas gas: balance check ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39759, + "gasUsed": 39784, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Migrator.gas.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Migrator.gas.ts.snap new file mode 100644 index 00000000..36d72292 --- /dev/null +++ b/test/integration-tests/gas-tests/__snapshots__/Migrator.gas.ts.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Migrator Gas Tests Migrator burn gas: erc721permit + decreaseLiquidity + collect + burn 1`] = ` +Object { + "calldataByteLength": 1092, + "gasUsed": 259654, +} +`; + +exports[`Migrator Gas Tests Migrator collect gas: erc721permit + decreaseLiquidity + collect 1`] = ` +Object { + "calldataByteLength": 964, + "gasUsed": 225528, +} +`; + +exports[`Migrator Gas Tests Migrator decrease liquidity gas: erc721permit + decreaseLiquidity 1`] = ` +Object { + "calldataByteLength": 740, + "gasUsed": 203978, +} +`; + +exports[`Migrator Gas Tests Migrator erc721permit gas: erc721permit 1`] = ` +Object { + "calldataByteLength": 484, + "gasUsed": 68000, +} +`; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 56251cf0..aedf30cd 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -3,48 +3,48 @@ exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39128, + "gasUsed": 39153, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67780, + "gasUsed": 67830, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38153, + "gasUsed": 38178, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33724, + "gasUsed": 33749, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46692, + "gasUsed": 46717, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 52970, + "gasUsed": 53045, } `; exports[`Payments Gas Tests Individual Command Tests gas: WRAP_ETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 57301, + "gasUsed": 57354, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 4705592f..afa44400 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271359, + "gasUsed": 271503, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247093, + "gasUsed": 247230, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247093, + "gasUsed": 247230, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271359, + "gasUsed": 271503, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 191528, + "gasUsed": 191578, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 179118, + "gasUsed": 179168, } `; 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": 300577, + "gasUsed": 300652, } `; 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": 309796, + "gasUsed": 309896, } `; 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": 311874, + "gasUsed": 311949, } `; 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": 306382, + "gasUsed": 306432, } `; 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": 178884, + "gasUsed": 178959, } `; 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": 178659, + "gasUsed": 178734, } `; 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": 196684, + "gasUsed": 196759, } `; 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": 186755, + "gasUsed": 186830, } `; 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": 193725, + "gasUsed": 193825, } `; @@ -143,98 +143,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": 128642, + "gasUsed": 128717, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108947, + "gasUsed": 108972, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 243553, + "gasUsed": 243578, } `; 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": 243295, + "gasUsed": 243320, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 176296, + "gasUsed": 176321, } `; 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": 176296, + "gasUsed": 176321, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 109053, + "gasUsed": 109078, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 248954, + "gasUsed": 248979, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179077, + "gasUsed": 179102, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 125148, + "gasUsed": 125198, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 130412, + "gasUsed": 130487, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 138353, + "gasUsed": 138453, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108718, + "gasUsed": 108809, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127611, + "gasUsed": 127686, } `; @@ -283,69 +283,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": 107624, + "gasUsed": 107649, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 256009, + "gasUsed": 256034, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179203, + "gasUsed": 179228, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117154, + "gasUsed": 117179, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 253167, + "gasUsed": 253192, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 176865, + "gasUsed": 176890, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 123873, + "gasUsed": 123923, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 133475, + "gasUsed": 133525, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 217436, + "gasUsed": 217486, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128593, + "gasUsed": 128668, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 60180095..9bc1a6e3 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `12879`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `13937`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index 8cd90000..aa520e2e 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1110322`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1110522`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1144704`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1144954`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3101911`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3102436`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3255320`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3256070`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4134291`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4134991`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4338065`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4339065`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `510754`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `510854`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `511084`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `511184`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `301726`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `301776`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `301674`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `301724`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; diff --git a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap new file mode 100644 index 00000000..a63e70f6 --- /dev/null +++ b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`V3 to V4 Migration Gas Tests Migrator burn gas: erc721permit + decreaseLiquidity + collect + burn 1`] = ` +Object { + "calldataByteLength": 1092, + "gasUsed": 259688, +} +`; + +exports[`V3 to V4 Migration Gas Tests Migrator collect gas: erc721permit + decreaseLiquidity + collect 1`] = ` +Object { + "calldataByteLength": 964, + "gasUsed": 225570, +} +`; + +exports[`V3 to V4 Migration Gas Tests Migrator decrease liquidity gas: erc721permit + decreaseLiquidity 1`] = ` +Object { + "calldataByteLength": 740, + "gasUsed": 204020, +} +`; + +exports[`V3 to V4 Migration Gas Tests Migrator erc721permit gas: erc721permit 1`] = ` +Object { + "calldataByteLength": 484, + "gasUsed": 68042, +} +`; diff --git a/test/integration-tests/shared/constants.ts b/test/integration-tests/shared/constants.ts index 158efe52..88633d84 100644 --- a/test/integration-tests/shared/constants.ts +++ b/test/integration-tests/shared/constants.ts @@ -3,6 +3,7 @@ const { ethers } = hre // Router Helpers export const MAX_UINT = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +export const MAX_UINT128 = '0xffffffffffffffffffffffffffffffff' export const MAX_UINT160 = '0xffffffffffffffffffffffffffffffffffffffff' export const DEADLINE = 2000000000 export const CONTRACT_BALANCE = '0x8000000000000000000000000000000000000000000000000000000000000000' @@ -22,3 +23,4 @@ export const V2_FACTORY_MAINNET = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f' export const V3_FACTORY_MAINNET = '0x1F98431c8aD98523631AE4a59f267346ea31F984' export const V3_INIT_CODE_HASH_MAINNET = '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54' export const V2_INIT_CODE_HASH_MAINNET = '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' +export const V3_NFT_POSITION_MANAGER_MAINNET = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' diff --git a/test/integration-tests/shared/deployUniversalRouter.ts b/test/integration-tests/shared/deployUniversalRouter.ts index 8fe3fdf0..3d15949c 100644 --- a/test/integration-tests/shared/deployUniversalRouter.ts +++ b/test/integration-tests/shared/deployUniversalRouter.ts @@ -7,6 +7,7 @@ import { V2_INIT_CODE_HASH_MAINNET, V3_INIT_CODE_HASH_MAINNET, PERMIT2_ADDRESS, + V3_NFT_POSITION_MANAGER_MAINNET, } from './constants' export async function deployRouter(mockReentrantWETH?: string): Promise { @@ -17,6 +18,7 @@ export async function deployRouter(mockReentrantWETH?: string): Promise { + const abi = new ethers.utils.AbiCoder() + const { spender, tokenId, deadline, v, r, s } = params + const encodedParams = abi.encode( + ['address', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32'], + [spender, tokenId, deadline, v, r, s] + ) + const functionSignature = ethers.utils.id(permitSignature).substring(0, 10) + const encodedCall = functionSignature + encodedParams.substring(2) + return encodedCall +} + +const encodeDecreaseLiquidity = (params: DecreaseLiquidityParams): string => { + const abi = new ethers.utils.AbiCoder() + const encodedParams = abi.encode([DECREASE_LIQUIDITY_STRUCT], [params]) + const functionSignature = ethers.utils.id(decreaseLiquidityFunctionSignature).substring(0, 10) + const encodedCall = functionSignature + encodedParams.substring(2) + return encodedCall +} + +const encodeCollect = (params: CollectParams): string => { + const abi = new ethers.utils.AbiCoder() + const encodedCollectParams = abi.encode([COLLECT_STRUCT], [params]) + const functionSignatureCollect = ethers.utils.id(collectFunctionSignature).substring(0, 10) + const encodedCollectCall = functionSignatureCollect + encodedCollectParams.substring(2) + return encodedCollectCall +} + +const encodeBurn = (params: BigNumber): string => { + const abi = new ethers.utils.AbiCoder() + const encodedBurnParams = abi.encode(['uint256'], [params]) + const functionSignatureBurn = ethers.utils.id(burnFunctionSignature).substring(0, 10) + const encodedBurnCall = functionSignatureBurn + encodedBurnParams.substring(2) + return encodedBurnCall +} + +export { encodeERC721Permit, encodeDecreaseLiquidity, encodeCollect, encodeBurn } diff --git a/test/integration-tests/shared/executeRouter.ts b/test/integration-tests/shared/executeRouter.ts new file mode 100644 index 00000000..a2c382d4 --- /dev/null +++ b/test/integration-tests/shared/executeRouter.ts @@ -0,0 +1,81 @@ +import type { Contract } from '@ethersproject/contracts' +import { TransactionReceipt } from '@ethersproject/abstract-provider' +import { parseEvents, V2_EVENTS, V3_EVENTS } from './parseEvents' +import { BigNumber, BigNumberish } from 'ethers' +import { UniversalRouter } from '../../../typechain' +import { DEADLINE } from './constants' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { RoutePlanner } from './planner' +import hre from 'hardhat' +const { ethers } = hre + +type V2SwapEventArgs = { + amount0In: BigNumber + amount0Out: BigNumber + amount1In: BigNumber + amount1Out: BigNumber +} + +type V3SwapEventArgs = { + amount0: BigNumber + amount1: BigNumber +} + +type ExecutionParams = { + wethBalanceBefore: BigNumber + wethBalanceAfter: BigNumber + daiBalanceBefore: BigNumber + daiBalanceAfter: BigNumber + usdcBalanceBefore: BigNumber + usdcBalanceAfter: BigNumber + ethBalanceBefore: BigNumber + ethBalanceAfter: BigNumber + v2SwapEventArgs: V2SwapEventArgs | undefined + v3SwapEventArgs: V3SwapEventArgs | undefined + receipt: TransactionReceipt + gasSpent: BigNumber +} + +export async function executeRouter( + planner: RoutePlanner, + caller: SignerWithAddress, + router: UniversalRouter, + wethContract: Contract, + daiContract: Contract, + usdcContract: Contract, + value?: BigNumberish +): Promise { + const ethBalanceBefore: BigNumber = await ethers.provider.getBalance(caller.address) + const wethBalanceBefore: BigNumber = await wethContract.balanceOf(caller.address) + const daiBalanceBefore: BigNumber = await daiContract.balanceOf(caller.address) + const usdcBalanceBefore: BigNumber = await usdcContract.balanceOf(caller.address) + + const { commands, inputs } = planner + + const receipt = await ( + await router.connect(caller)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }) + ).wait() + const gasSpent = receipt.gasUsed.mul(receipt.effectiveGasPrice) + const v2SwapEventArgs = parseEvents(V2_EVENTS, receipt)[0]?.args as unknown as V2SwapEventArgs + const v3SwapEventArgs = parseEvents(V3_EVENTS, receipt)[0]?.args as unknown as V3SwapEventArgs + + const ethBalanceAfter: BigNumber = await ethers.provider.getBalance(caller.address) + const wethBalanceAfter: BigNumber = await wethContract.balanceOf(caller.address) + const daiBalanceAfter: BigNumber = await daiContract.balanceOf(caller.address) + const usdcBalanceAfter: BigNumber = await usdcContract.balanceOf(caller.address) + + return { + wethBalanceBefore, + wethBalanceAfter, + daiBalanceBefore, + daiBalanceAfter, + usdcBalanceBefore, + usdcBalanceAfter, + ethBalanceBefore, + ethBalanceAfter, + v2SwapEventArgs, + v3SwapEventArgs, + receipt, + gasSpent, + } +} diff --git a/test/integration-tests/shared/getPermitNFTSignature.ts b/test/integration-tests/shared/getPermitNFTSignature.ts new file mode 100644 index 00000000..050c9089 --- /dev/null +++ b/test/integration-tests/shared/getPermitNFTSignature.ts @@ -0,0 +1,58 @@ +import { BigNumberish, constants, Signature } from 'ethers' +import { splitSignature } from 'ethers/lib/utils' +import { INonfungiblePositionManager } from '../../../typechain' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' + +export default async function getPermitNFTSignature( + wallet: SignerWithAddress, + positionManager: INonfungiblePositionManager, + spender: string, + tokenId: BigNumberish, + deadline: BigNumberish = constants.MaxUint256, + permitConfig?: { nonce?: BigNumberish; name?: string; chainId?: number; version?: string } +): Promise { + const [nonce, name, version, chainId] = await Promise.all([ + permitConfig?.nonce ?? positionManager.positions(tokenId).then((p) => p.nonce), + permitConfig?.name ?? positionManager.name(), + permitConfig?.version ?? '1', + permitConfig?.chainId ?? wallet.getChainId(), + ]) + + return splitSignature( + await wallet._signTypedData( + { + name, + version, + chainId, + verifyingContract: positionManager.address, + }, + { + Permit: [ + { + name: 'spender', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + { + name: 'nonce', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + ], + }, + { + owner: wallet.address, + spender, + tokenId, + nonce, + deadline, + } + ) + ) +} diff --git a/test/integration-tests/shared/mainnetForkHelpers.ts b/test/integration-tests/shared/mainnetForkHelpers.ts index 2c52ba2f..3987c87c 100644 --- a/test/integration-tests/shared/mainnetForkHelpers.ts +++ b/test/integration-tests/shared/mainnetForkHelpers.ts @@ -1,6 +1,7 @@ -import { ERC20, ERC20__factory, IPermit2 } from '../../../typechain' +import { ERC20, ERC20__factory, IPermit2, INonfungiblePositionManager } from '../../../typechain' import { abi as PERMIT2_ABI } from '../../../artifacts/permit2/src/interfaces/IPermit2.sol/IPermit2.json' -import { PERMIT2_ADDRESS } from './constants' +import { abi as INonfungiblePositionManager_ABI } from '../../../artifacts/@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol/INonfungiblePositionManager.json' +import { PERMIT2_ADDRESS, V3_NFT_POSITION_MANAGER_MAINNET } from './constants' import { abi as V2_PAIR_ABI } from '../../../artifacts/@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol/IUniswapV2Pair.json' import { Currency, Token, WETH9 } from '@uniswap/sdk-core' import { TransactionResponse } from '@ethersproject/abstract-provider' @@ -104,3 +105,8 @@ export const resetFork = async () => { } export const PERMIT2 = new ethers.Contract(PERMIT2_ADDRESS, PERMIT2_ABI) as IPermit2 + +export const V3_NFT_POSITION_MANAGER = new ethers.Contract( + V3_NFT_POSITION_MANAGER_MAINNET, + INonfungiblePositionManager_ABI +) as INonfungiblePositionManager diff --git a/test/integration-tests/shared/planner.ts b/test/integration-tests/shared/planner.ts index c61b04cb..b2a14783 100644 --- a/test/integration-tests/shared/planner.ts +++ b/test/integration-tests/shared/planner.ts @@ -22,6 +22,9 @@ export enum CommandType { PERMIT2_TRANSFER_FROM_BATCH = 0x0d, BALANCE_CHECK_ERC20 = 0x0e, + V3_POSITION_MANAGER_PERMIT = 0x10, + V3_POSITION_MANAGER_CALL = 0x11, + EXECUTE_SUB_PLAN = 0x21, } @@ -61,6 +64,9 @@ const ABI_DEFINITION: { [key in CommandType]: string[] } = { [CommandType.TRANSFER]: ['address', 'address', 'uint256'], [CommandType.PAY_PORTION]: ['address', 'address', 'uint256'], [CommandType.BALANCE_CHECK_ERC20]: ['address', 'address', 'uint256'], + + [CommandType.V3_POSITION_MANAGER_PERMIT]: ['bytes'], + [CommandType.V3_POSITION_MANAGER_CALL]: ['bytes'], } export class RoutePlanner { @@ -96,6 +102,10 @@ export type RouterCommand = { } export function createCommand(type: CommandType, parameters: any[]): RouterCommand { - const encodedInput = defaultAbiCoder.encode(ABI_DEFINITION[type], parameters) - return { type, encodedInput } + if (type === CommandType.V3_POSITION_MANAGER_CALL || type === CommandType.V3_POSITION_MANAGER_PERMIT) { + return { type, encodedInput: parameters[0] } + } else { + const encodedInput = defaultAbiCoder.encode(ABI_DEFINITION[type], parameters) + return { type, encodedInput } + } } diff --git a/yarn.lock b/yarn.lock index f1f02d00..2cbe4208 100644 --- a/yarn.lock +++ b/yarn.lock @@ -972,53 +972,53 @@ resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz" integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== -"@nomicfoundation/edr-darwin-arm64@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.0.tgz#bbb43f0e01f40839b0bd38c2c443cb6910ae955f" - integrity sha512-7+rraFk9tCqvfemv9Ita5vTlSBAeO/S5aDKOgGRgYt0JEKZlrX161nDW6UfzMPxWl9GOLEDUzCEaYuNmXseUlg== +"@nomicfoundation/edr-darwin-arm64@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.2.tgz#2ff98535f272c9f2a7d06eeda93fe7b207a348a4" + integrity sha512-S+hhepupfqpBvMa9M1PVS08sVjGXsLnjyAsjhrrsjsNuTHVLhKzhkguvBD5g4If5skrwgOaVqpag4wnQbd15kQ== -"@nomicfoundation/edr-darwin-x64@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.0.tgz#b1ffcd9142418fd8498de34a7336b3f977907c86" - integrity sha512-+Hrc0mP9L6vhICJSfyGo/2taOToy1AIzVZawO3lU8Lf7oDQXfhQ4UkZnkWAs9SVu1eUwHUGGGE0qB8644piYgg== +"@nomicfoundation/edr-darwin-x64@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.2.tgz#001dcd0e7fa4c52046d283b0dc61e63a60c614dd" + integrity sha512-/zM94AUrXz6CmcsecRNHJ50jABDUFafmGc4iBmkfX/mTp4tVZj7XTyIogrQIt0FnTaeb4CgZoLap2+8tW/Uldg== -"@nomicfoundation/edr-linux-arm64-gnu@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.0.tgz#8173d16d4f6f2b3e82ba7096d2a1ea3619d8bfa7" - integrity sha512-4HUDMchNClQrVRfVTqBeSX92hM/3khCgpZkXP52qrnJPqgbdCxosOehlQYZ65wu0b/kaaZSyvACgvCLSQ5oSzQ== +"@nomicfoundation/edr-linux-arm64-gnu@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.2.tgz#6d19f8265c8ffb22e29bc5bbbb5d1913fe4b306b" + integrity sha512-TV3Pr2tFvvmCfPCi9PaCGLtqn+oLaPKfL2NWpnoCeFFdzDQXi2L930yP1oUPY5RXd78NLdVHMkEkbhb2b6Wuvg== -"@nomicfoundation/edr-linux-arm64-musl@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.0.tgz#b1ce293a7c3e0d9f70391e1aef1a82b83b997567" - integrity sha512-D4J935ZRL8xfnP3zIFlCI9jXInJ0loDUkCTLeCEbOf2uuDumWDghKNQlF1itUS+EHaR1pFVBbuwqq8hVK0dASg== +"@nomicfoundation/edr-linux-arm64-musl@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.2.tgz#0b01aa405fdc8048c7a8e95c737f29b437536a30" + integrity sha512-PALwrLBk1M9rolXyhSX8xdhe5jL0qf/PgiCIF7W7lUyVKrI/I0oiU0EHDk/Xw7yi2UJg4WRyhhZoHYa0g4g8Qg== -"@nomicfoundation/edr-linux-x64-gnu@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.0.tgz#4c12c4e4bfd3d837f5663ad7cbf7cb6d5634ef83" - integrity sha512-6x7HPy+uN5Cb9N77e2XMmT6+QSJ+7mRbHnhkGJ8jm4cZvWuj2Io7npOaeHQ3YHK+TiQpTnlbkjoOIpEwpY3XZA== +"@nomicfoundation/edr-linux-x64-gnu@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.2.tgz#10959fd4db9b333d3e0559cb893e109611889af0" + integrity sha512-5svkftypDjAZ1LxV1onojlaqPRxrTEjJLkrUwLL+Fao5ZMe7aTnk5QQ1Jv76gW6WYZnMXNgjPhRcnw3oSNrqFA== -"@nomicfoundation/edr-linux-x64-musl@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.0.tgz#8842004aa1a47c504f10863687da28b65dca7baa" - integrity sha512-3HFIJSXgyubOiaN4MWGXx2xhTnhwlJk0PiSYNf9+L/fjBtcRkb2nM910ZJHTvqCb6OT98cUnaKuAYdXIW2amgw== +"@nomicfoundation/edr-linux-x64-musl@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.2.tgz#8de64a2dfd869dad930dd0eb9572a0593d382379" + integrity sha512-qiMlXQTggdH9zfOB4Eil4rQ95z8s7QdLJcOfz5Aym12qJNkCyF9hi4cc4dDCWA0CdI3x3oLbuf8qb81SF8R45w== -"@nomicfoundation/edr-win32-x64-msvc@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.0.tgz#29d8bbb2edf9912a95f5453855cf17cdcb269957" - integrity sha512-CP4GsllEfXEz+lidcGYxKe5rDJ60TM5/blB5z/04ELVvw6/CK9eLcYeku7HV0jvV7VE6dADYKSdQyUkvd0El+A== +"@nomicfoundation/edr-win32-x64-msvc@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.2.tgz#13ad4bab9fd68853930e1a3d87c78d69d1d0e2ef" + integrity sha512-hDkAb0iaMmGYwBY/rA1oCX8VpsezfQcHPEPIEGXEcWC3WbnOgIZo0Qkpu/g0OMtFOJSQlWLXvKZuV7blhnrQag== "@nomicfoundation/edr@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.4.0.tgz#4895ecb6ef321136db837458949c37cce4a29459" - integrity sha512-T96DMSogO8TCdbKKctvxfsDljbhFOUKWc9fHJhSeUh71EEho2qR4951LKQF7t7UWEzguVYh/idQr5L/E3QeaMw== - dependencies: - "@nomicfoundation/edr-darwin-arm64" "0.4.0" - "@nomicfoundation/edr-darwin-x64" "0.4.0" - "@nomicfoundation/edr-linux-arm64-gnu" "0.4.0" - "@nomicfoundation/edr-linux-arm64-musl" "0.4.0" - "@nomicfoundation/edr-linux-x64-gnu" "0.4.0" - "@nomicfoundation/edr-linux-x64-musl" "0.4.0" - "@nomicfoundation/edr-win32-x64-msvc" "0.4.0" + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.4.2.tgz#9d7550182d4f75d7510e265ebd3474c4f6fcb62a" + integrity sha512-U7v0HuZHfrsl/5FpUzuB2FYA0+FUglHHwiO6NhvLtNYKMZcPzdS6iUriMp/7GWs0SVxW3bAht9GinZPxdhVwWg== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.4.2" + "@nomicfoundation/edr-darwin-x64" "0.4.2" + "@nomicfoundation/edr-linux-arm64-gnu" "0.4.2" + "@nomicfoundation/edr-linux-arm64-musl" "0.4.2" + "@nomicfoundation/edr-linux-x64-gnu" "0.4.2" + "@nomicfoundation/edr-linux-x64-musl" "0.4.2" + "@nomicfoundation/edr-win32-x64-msvc" "0.4.2" "@nomicfoundation/ethereumjs-common@4.0.4": version "4.0.4" From 8757822a4e62ce7225cd2d013fd2c32815d526d6 Mon Sep 17 00:00:00 2001 From: Alice <34962750+hensha256@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:47:33 +0100 Subject: [PATCH 03/13] Reentrancy guard transient (#354) * Make reentrancy guard transient * locker tests * abstract the locker library away * Updated locker tests * move map, and fix CI * Update lint.yml * update comment --- .github/workflows/forge.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- contracts/base/Dispatcher.sol | 32 +++++--- contracts/base/LockAndMsgSender.sol | 29 +++---- contracts/libraries/Locker.sol | 27 +++++++ package.json | 10 +-- test/foundry-tests/Locker.t.sol | 41 ++++++++++ .../CheckOwnership.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 14 ++-- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- .../V3ToV4Migration.gas.test.ts.snap | 8 +- 14 files changed, 172 insertions(+), 97 deletions(-) create mode 100644 contracts/libraries/Locker.sol create mode 100644 test/foundry-tests/Locker.t.sol diff --git a/.github/workflows/forge.yml b/.github/workflows/forge.yml index 7deb0df6..7ac8fecd 100644 --- a/.github/workflows/forge.yml +++ b/.github/workflows/forge.yml @@ -56,7 +56,7 @@ jobs: - name: Run Forge tests run: | - forge test -vvv + forge test --isolate -vvv id: test env: FORK_URL: https://mainnet.infura.io/v3/${{ secrets.INFURA_API_KEY }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1573cf2f..f9ab6491 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -40,4 +40,4 @@ jobs: id: build - name: Run linter - run: yarn run prettier + run: yarn run lint:check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cdb27f1..318e9734 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,6 +47,6 @@ jobs: run: yarn compile - name: Run Integration tests - run: yarn test + run: yarn test:hardhat env: INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }} diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 27978dff..84869d06 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -13,6 +13,7 @@ import {LockAndMsgSender} from './LockAndMsgSender.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; import {IERC721Permit} from '@uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol'; +import {Constants} from '../libraries/Constants.sol'; /// @title Decodes and Executes Commands /// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command @@ -55,7 +56,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? lockedBy : address(this); + address payer = payerIsUser ? _msgSender() : address(this); v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); } else if (command == Commands.V3_SWAP_EXACT_OUT) { // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) @@ -71,7 +72,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? lockedBy : address(this); + address payer = payerIsUser ? _msgSender() : address(this); v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); } else if (command == Commands.PERMIT2_TRANSFER_FROM) { // equivalent: abi.decode(inputs, (address, address, uint160)) @@ -83,12 +84,12 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr recipient := calldataload(add(inputs.offset, 0x20)) amount := calldataload(add(inputs.offset, 0x40)) } - permit2TransferFrom(token, lockedBy, map(recipient), amount); + permit2TransferFrom(token, _msgSender(), map(recipient), amount); } else if (command == Commands.PERMIT2_PERMIT_BATCH) { (IAllowanceTransfer.PermitBatch memory permitBatch,) = abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); bytes calldata data = inputs.toBytes(1); - PERMIT2.permit(lockedBy, permitBatch, data); + PERMIT2.permit(_msgSender(), permitBatch, data); } else if (command == Commands.SWEEP) { // equivalent: abi.decode(inputs, (address, address, uint256)) address token; @@ -142,7 +143,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? lockedBy : address(this); + address payer = payerIsUser ? _msgSender() : address(this); v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); } else if (command == Commands.V2_SWAP_EXACT_OUT) { // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) @@ -158,7 +159,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? lockedBy : address(this); + address payer = payerIsUser ? _msgSender() : address(this); v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); } else if (command == Commands.PERMIT2_PERMIT) { // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes)) @@ -167,7 +168,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr permitSingle := inputs.offset } bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) - PERMIT2.permit(lockedBy, permitSingle, data); + PERMIT2.permit(_msgSender(), permitSingle, data); } else if (command == Commands.WRAP_ETH) { // equivalent: abi.decode(inputs, (address, uint256)) address recipient; @@ -189,7 +190,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); - permit2TransferFrom(batchDetails, lockedBy); + permit2TransferFrom(batchDetails, _msgSender()); } else if (command == Commands.BALANCE_CHECK_ERC20) { // equivalent: abi.decode(inputs, (address, address, uint256)) address owner; @@ -236,7 +237,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr // This can be done in 2 ways: // 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens // 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token - if (!isAuthorizedForToken(lockedBy, tokenId)) { + if (!isAuthorizedForToken(_msgSender(), tokenId)) { revert NotAuthorizedForToken(tokenId); } @@ -259,6 +260,19 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr } } + /// @notice Calculates the recipient address for a command + /// @param recipient The recipient or recipient-flag for the command + /// @return output The resultant recipient for the command + function map(address recipient) internal view returns (address) { + if (recipient == Constants.MSG_SENDER) { + return _msgSender(); + } else if (recipient == Constants.ADDRESS_THIS) { + return address(this); + } else { + return recipient; + } + } + /// @notice Executes encoded commands along with provided inputs. /// @param commands A set of concatenated commands, each 1 byte in length /// @param inputs An array of byte strings containing abi encoded inputs for each command diff --git a/contracts/base/LockAndMsgSender.sol b/contracts/base/LockAndMsgSender.sol index 472c4fe3..c9adbf86 100644 --- a/contracts/base/LockAndMsgSender.sol +++ b/contracts/base/LockAndMsgSender.sol @@ -1,35 +1,28 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.24; -import {Constants} from '../libraries/Constants.sol'; +import {Locker} from '../libraries/Locker.sol'; contract LockAndMsgSender { error ContractLocked(); - address internal constant NOT_LOCKED_FLAG = address(1); - address internal lockedBy = NOT_LOCKED_FLAG; - + /// @notice Modifier enforcing a reentrancy lock that allows self-reentrancy modifier isNotLocked() { + // Apply a reentrancy lock for all external callers if (msg.sender != address(this)) { - if (lockedBy != NOT_LOCKED_FLAG) revert ContractLocked(); - lockedBy = msg.sender; + if (Locker.isLocked()) revert ContractLocked(); + Locker.set(msg.sender); _; - lockedBy = NOT_LOCKED_FLAG; + Locker.set(address(0)); } else { + // The contract is allowed to reenter itself, so the lock is not checked _; } } - /// @notice Calculates the recipient address for a command - /// @param recipient The recipient or recipient-flag for the command - /// @return output The resultant recipient for the command - function map(address recipient) internal view returns (address) { - if (recipient == Constants.MSG_SENDER) { - return lockedBy; - } else if (recipient == Constants.ADDRESS_THIS) { - return address(this); - } else { - return recipient; - } + /// @notice Function to be used instead of msg.sender, as the contract performs self-reentrancy and at + /// times msg.sender == address(this). Instead _msgSender() returns the initiator of the lock + function _msgSender() internal view returns (address) { + return Locker.get(); } } diff --git a/contracts/libraries/Locker.sol b/contracts/libraries/Locker.sol new file mode 100644 index 00000000..aa506545 --- /dev/null +++ b/contracts/libraries/Locker.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +/// @notice A library to implement a reentrancy lock in transient storage. +/// @dev Instead of storing a boolean, the locker's address is stored to allow the contract to know who locked the contract +/// TODO: This library can be deleted when we have the transient keyword support in solidity. +library Locker { + // The slot holding the locker state, transiently. bytes32(uint256(keccak256("Locker")) - 1) + bytes32 constant LOCKER_SLOT = 0x0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a708; + + function set(address locker) internal { + // The locker is always msg.sender or address(0) so does not need to be cleaned + assembly ("memory-safe") { + tstore(LOCKER_SLOT, locker) + } + } + + function get() internal view returns (address locker) { + assembly ("memory-safe") { + locker := tload(LOCKER_SLOT) + } + } + + function isLocked() internal view returns (bool) { + return Locker.get() != address(0); + } +} diff --git a/package.json b/package.json index 0da6296d..a4fd2fe4 100644 --- a/package.json +++ b/package.json @@ -61,12 +61,12 @@ "typescript": "^3.7.3" }, "scripts": { - "compile": "hardhat compile", - "test": "hardhat test", + "compile": "hardhat compile && forge build", + "test:hardhat": "yarn compile && hardhat test", "test:gas": "UPDATE_SNAPSHOT=1 yarn test --grep gas", - "test:all": "UPDATE_SNAPSHOT=1 yarn test", + "test:all": "UPDATE_SNAPSHOT=1 yarn test:hardhat && forge test --isolate", "prettier:fix": "prettier --write '**/*.ts' && prettier --write '**/*.json'", - "lint:fix": "yarn prettier:fix && forge fmt", - "prettier": "prettier --check '**/*.ts' && forge fmt --check" + "lint": "yarn prettier:fix && forge fmt", + "lint:check": "prettier --check '**/*.ts' && forge fmt --check" } } diff --git a/test/foundry-tests/Locker.t.sol b/test/foundry-tests/Locker.t.sol new file mode 100644 index 00000000..a7ffd454 --- /dev/null +++ b/test/foundry-tests/Locker.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import 'forge-std/Test.sol'; +import {Constants} from '../../contracts/libraries/Constants.sol'; +import {Locker} from '../../contracts/libraries/Locker.sol'; + +contract LockerTest is Test { + function test_fuzz_set_get(address locker1, address locker2, address locker3) public { + assertEq(Locker.get(), address(0)); + + Locker.set(locker1); + assertEq(Locker.get(), locker1); + + Locker.set(locker2); + assertEq(Locker.get(), locker2); + + Locker.set(locker3); + assertEq(Locker.get(), locker3); + + Locker.set(address(0)); + assertEq(Locker.get(), address(0)); + } + + function test_fuzz_isLocked(address locker) public { + assertEq(Locker.get(), address(0)); + assertEq(Locker.isLocked(), false); + + Locker.set(locker); + // the contract is locked when the locker is not address(0) + assertEq(Locker.isLocked(), locker != address(0)); + + Locker.set(address(0)); + assertEq(Locker.isLocked(), false); + } + + function test_lockerSlot() public { + bytes32 expectedSlot = bytes32(uint256(keccak256('Locker')) - 1); + assertEq(expectedSlot, Locker.LOCKER_SLOT); + } +} diff --git a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap index b3a5b59a..361b3d6d 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Check Ownership Gas gas: balance check ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39784, + "gasUsed": 37644, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index aedf30cd..84b16c78 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -3,48 +3,48 @@ exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39153, + "gasUsed": 37013, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67830, + "gasUsed": 65690, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38178, + "gasUsed": 36038, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33749, + "gasUsed": 31609, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46717, + "gasUsed": 44577, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53045, + "gasUsed": 50905, } `; exports[`Payments Gas Tests Individual Command Tests gas: WRAP_ETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 57354, + "gasUsed": 53402, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index afa44400..f733f85c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271503, + "gasUsed": 269333, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247230, + "gasUsed": 245060, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247230, + "gasUsed": 245060, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271503, + "gasUsed": 269333, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 191578, + "gasUsed": 189426, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 179168, + "gasUsed": 177010, } `; 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": 300652, + "gasUsed": 298488, } `; 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": 309896, + "gasUsed": 307720, } `; 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": 311949, + "gasUsed": 309785, } `; 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": 306432, + "gasUsed": 304268, } `; 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": 178959, + "gasUsed": 176801, } `; 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": 178734, + "gasUsed": 176576, } `; 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": 196759, + "gasUsed": 194601, } `; 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": 186830, + "gasUsed": 184672, } `; 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": 193825, + "gasUsed": 191679, } `; @@ -143,98 +143,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": 128717, + "gasUsed": 126565, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108972, + "gasUsed": 106820, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 243578, + "gasUsed": 241426, } `; 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": 243320, + "gasUsed": 241168, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 176321, + "gasUsed": 174169, } `; 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": 176321, + "gasUsed": 174169, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 109078, + "gasUsed": 106926, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 248979, + "gasUsed": 246827, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179102, + "gasUsed": 176950, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 125198, + "gasUsed": 123046, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 130487, + "gasUsed": 128329, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 138453, + "gasUsed": 136301, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108809, + "gasUsed": 106657, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127686, + "gasUsed": 125534, } `; @@ -283,69 +283,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": 107649, + "gasUsed": 105497, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 256034, + "gasUsed": 253882, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179228, + "gasUsed": 177076, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117179, + "gasUsed": 115027, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 253192, + "gasUsed": 251040, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 176890, + "gasUsed": 174738, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 123923, + "gasUsed": 121771, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 133525, + "gasUsed": 131373, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 217486, + "gasUsed": 215340, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128668, + "gasUsed": 126516, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 9bc1a6e3..60b77b7c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `13937`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14113`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index aa520e2e..b1127840 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1110522`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104057`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1144954`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138483`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3102436`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3080913`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3256070`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3234520`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4134991`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102732`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4339065`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4306770`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `510854`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508693`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `511184`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509023`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `301776`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299627`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `301724`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299575`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; diff --git a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap index a63e70f6..c07cc408 100644 --- a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap @@ -3,27 +3,27 @@ exports[`V3 to V4 Migration Gas Tests Migrator burn gas: erc721permit + decreaseLiquidity + collect + burn 1`] = ` Object { "calldataByteLength": 1092, - "gasUsed": 259688, + "gasUsed": 255764, } `; exports[`V3 to V4 Migration Gas Tests Migrator collect gas: erc721permit + decreaseLiquidity + collect 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 225570, + "gasUsed": 223454, } `; exports[`V3 to V4 Migration Gas Tests Migrator decrease liquidity gas: erc721permit + decreaseLiquidity 1`] = ` Object { "calldataByteLength": 740, - "gasUsed": 204020, + "gasUsed": 201892, } `; exports[`V3 to V4 Migration Gas Tests Migrator erc721permit gas: erc721permit 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 68042, + "gasUsed": 65902, } `; From cc0e7c00cea10ff0c2e74319e5ea30dce202bcd3 Mon Sep 17 00:00:00 2001 From: diana Date: Wed, 24 Jul 2024 11:58:12 -0400 Subject: [PATCH 04/13] V4 liquidity commands (#359) * v4 periphery git submodule * deploy v4 * prettier * point v4 periphery to main * use solmate/src and deploy v4 * format * update v4-periphery and future proof deployRouter * prettier --- .gitmodules | 3 +++ contracts/UniversalRouter.sol | 2 +- contracts/base/Dispatcher.sol | 2 +- contracts/base/RouterImmutables.sol | 1 + contracts/modules/MigratorImmutables.sol | 4 ++++ contracts/modules/Payments.sol | 4 ++-- contracts/modules/uniswap/v2/V2SwapRouter.sol | 2 +- contracts/modules/uniswap/v3/V3SwapRouter.sol | 2 +- contracts/test/ImportsForTypechain.sol | 15 +++++++++++++++ contracts/test/MintableERC20.sol | 2 +- contracts/test/ReenteringWETH.sol | 2 +- lib/v4-periphery | 1 + remappings.txt | 3 ++- script/DeployUniversalRouter.s.sol | 4 +++- script/deployParameters/DeployArbitrum.s.sol | 3 ++- .../deployParameters/DeployArbitrumGoerli.s.sol | 3 ++- script/deployParameters/DeployAvalanche.s.sol | 3 ++- script/deployParameters/DeployBSC.s.sol | 3 ++- script/deployParameters/DeployBase.s.sol | 3 ++- script/deployParameters/DeployBaseGoerli.s.sol | 3 ++- script/deployParameters/DeployBlast.s.sol | 3 ++- script/deployParameters/DeployCelo.s.sol | 3 ++- .../deployParameters/DeployCeloAlfajores.s.sol | 3 ++- script/deployParameters/DeployGoerli.s.sol | 3 ++- script/deployParameters/DeployMainnet.s.sol | 3 ++- script/deployParameters/DeployOptimism.s.sol | 3 ++- .../deployParameters/DeployOptimismGoerli.s.sol | 3 ++- script/deployParameters/DeployPolygon.s.sol | 3 ++- .../deployParameters/DeployPolygonMumbai.s.sol | 3 ++- script/deployParameters/DeploySepolia.s.sol | 3 ++- test/foundry-tests/UniswapV2.t.sol | 5 +++-- test/foundry-tests/UniversalRouter.t.sol | 5 +++-- test/foundry-tests/mock/MockERC20.sol | 2 +- test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol | 2 +- test/foundry-tests/uniswapTokens/v2WethApe.t.sol | 2 +- test/integration-tests/CheckOwnership.test.ts | 2 +- test/integration-tests/Uniswap.test.ts | 2 +- test/integration-tests/UniversalRouter.test.ts | 2 +- test/integration-tests/V3ToV4Migration.test.ts | 2 +- .../gas-tests/CheckOwnership.gas.test.ts | 2 +- .../gas-tests/Payments.gas.test.ts | 2 +- .../gas-tests/Uniswap.gas.test.ts | 2 +- .../gas-tests/UniversalRouter.gas.test.ts | 2 +- .../gas-tests/V3ToV4Migration.gas.test.ts | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 14 +++++++------- .../V3ToV4Migration.gas.test.ts.snap | 8 ++++---- .../shared/deployUniversalRouter.ts | 3 +++ test/integration-tests/shared/deployV4.ts | 16 ++++++++++++++++ 48 files changed, 117 insertions(+), 53 deletions(-) create mode 100644 contracts/test/ImportsForTypechain.sol create mode 160000 lib/v4-periphery create mode 100644 test/integration-tests/shared/deployV4.ts diff --git a/.gitmodules b/.gitmodules index 79436fdb..b21c696d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/v3-periphery"] path = lib/v3-periphery url = https://github.com/uniswap/v3-periphery +[submodule "lib/v4-periphery"] + path = lib/v4-periphery + url = https://github.com/Uniswap/v4-periphery diff --git a/contracts/UniversalRouter.sol b/contracts/UniversalRouter.sol index d5cf74c6..c80d12bf 100644 --- a/contracts/UniversalRouter.sol +++ b/contracts/UniversalRouter.sol @@ -21,7 +21,7 @@ contract UniversalRouter is IUniversalRouter, Dispatcher { UniswapParameters(params.v2Factory, params.v3Factory, params.pairInitCodeHash, params.poolInitCodeHash) ) PaymentsImmutables(PaymentsParameters(params.permit2, params.weth9)) - MigratorImmutables(MigratorParameters(params.v3NFTPositionManager)) + MigratorImmutables(MigratorParameters(params.v3NFTPositionManager, params.v4PositionManager)) {} /// @inheritdoc IUniversalRouter diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 84869d06..5992167f 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -10,7 +10,7 @@ import {V3ToV4Migrator} from '../modules/V3ToV4Migrator.sol'; import {Callbacks} from '../base/Callbacks.sol'; import {Commands} from '../libraries/Commands.sol'; import {LockAndMsgSender} from './LockAndMsgSender.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; import {IERC721Permit} from '@uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol'; import {Constants} from '../libraries/Constants.sol'; diff --git a/contracts/base/RouterImmutables.sol b/contracts/base/RouterImmutables.sol index cb46ef7e..c44b0f02 100644 --- a/contracts/base/RouterImmutables.sol +++ b/contracts/base/RouterImmutables.sol @@ -9,4 +9,5 @@ struct RouterParameters { bytes32 pairInitCodeHash; bytes32 poolInitCodeHash; address v3NFTPositionManager; + address v4PositionManager; } diff --git a/contracts/modules/MigratorImmutables.sol b/contracts/modules/MigratorImmutables.sol index 61989f26..d900b2f2 100644 --- a/contracts/modules/MigratorImmutables.sol +++ b/contracts/modules/MigratorImmutables.sol @@ -2,16 +2,20 @@ pragma solidity ^0.8.24; import {INonfungiblePositionManager} from '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol'; +import {IPositionManager} from '@uniswap/v4-periphery/src/interfaces/IPositionManager.sol'; struct MigratorParameters { address v3PositionManager; + address v4PositionManager; } contract MigratorImmutables { /// @notice v3PositionManager address INonfungiblePositionManager internal immutable V3_POSITION_MANAGER; + IPositionManager internal immutable V4_POSITION_MANAGER; constructor(MigratorParameters memory params) { V3_POSITION_MANAGER = INonfungiblePositionManager(params.v3PositionManager); + V4_POSITION_MANAGER = IPositionManager(params.v4PositionManager); } } diff --git a/contracts/modules/Payments.sol b/contracts/modules/Payments.sol index 2c4fb485..d9fc7c49 100644 --- a/contracts/modules/Payments.sol +++ b/contracts/modules/Payments.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.24; import {Constants} from '../libraries/Constants.sol'; import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol'; -import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {SafeTransferLib} from 'solmate/utils/SafeTransferLib.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; /// @title Payments contract /// @notice Performs various operations around the payment of ETH and tokens diff --git a/contracts/modules/uniswap/v2/V2SwapRouter.sol b/contracts/modules/uniswap/v2/V2SwapRouter.sol index 61a1128f..e6f11f82 100644 --- a/contracts/modules/uniswap/v2/V2SwapRouter.sol +++ b/contracts/modules/uniswap/v2/V2SwapRouter.sol @@ -7,7 +7,7 @@ import {UniswapImmutables} from '../UniswapImmutables.sol'; import {Payments} from '../../Payments.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {Constants} from '../../../libraries/Constants.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; /// @title Router for Uniswap v2 Trades abstract contract V2SwapRouter is UniswapImmutables, Permit2Payments { diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 0384a1db..36b3d6ae 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -10,7 +10,7 @@ import {Constants} from '../../../libraries/Constants.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {UniswapImmutables} from '../UniswapImmutables.sol'; import {Constants} from '../../../libraries/Constants.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; /// @title Router for Uniswap v3 Trades abstract contract V3SwapRouter is UniswapImmutables, Permit2Payments, IUniswapV3SwapCallback { diff --git a/contracts/test/ImportsForTypechain.sol b/contracts/test/ImportsForTypechain.sol new file mode 100644 index 00000000..9078cc59 --- /dev/null +++ b/contracts/test/ImportsForTypechain.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.17; + +import {PositionManager} from '@uniswap/v4-periphery/src/PositionManager.sol'; +import {PoolManager} from '@uniswap/v4-core/src/PoolManager.sol'; +import {ERC721} from 'solmate/tokens/ERC721.sol'; +import {ERC6909} from '@uniswap/v4-core/src/ERC6909.sol'; + +// this contract only exists to pull PositionManager and PoolManager into the hardhat build pipeline +// so that typechain artifacts are generated for it +abstract contract ImportsForTypechain is PositionManager, PoolManager { + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC6909, ERC721) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/contracts/test/MintableERC20.sol b/contracts/test/MintableERC20.sol index e5b52b5d..333470c3 100644 --- a/contracts/test/MintableERC20.sol +++ b/contracts/test/MintableERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.15; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; contract MintableERC20 is ERC20 { constructor(uint256 amountToMint) ERC20('test', 'TEST', 18) { diff --git a/contracts/test/ReenteringWETH.sol b/contracts/test/ReenteringWETH.sol index 699f77a4..c786ad3f 100644 --- a/contracts/test/ReenteringWETH.sol +++ b/contracts/test/ReenteringWETH.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.15; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; contract ReenteringWETH is ERC20 { error NotAllowedReenter(); diff --git a/lib/v4-periphery b/lib/v4-periphery new file mode 160000 index 00000000..6f094c3f --- /dev/null +++ b/lib/v4-periphery @@ -0,0 +1 @@ +Subproject commit 6f094c3f246537d9b6ce71c2711546910e64c757 diff --git a/remappings.txt b/remappings.txt index 2fa5bde4..055a4d0b 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,7 +1,8 @@ -solmate/=lib/solmate/ +solmate/=lib/solmate/src/ permit2/=lib/permit2/ forge-std/=lib/forge-std/src/ @openzeppelin/contracts=node_modules/@openzeppelin/contracts @uniswap/v3-core/=node_modules/@uniswap/v3-core/ @uniswap/v2-core/=node_modules/@uniswap/v2-core/ @uniswap/v3-periphery/=lib/v3-periphery/ +@uniswap/v4-periphery/=lib/v4-periphery/ diff --git a/script/DeployUniversalRouter.s.sol b/script/DeployUniversalRouter.s.sol index 6f0d396b..14984ddd 100644 --- a/script/DeployUniversalRouter.s.sol +++ b/script/DeployUniversalRouter.s.sol @@ -40,7 +40,8 @@ abstract contract DeployUniversalRouter is Script { v3Factory: mapUnsupported(params.v3Factory), pairInitCodeHash: params.pairInitCodeHash, poolInitCodeHash: params.poolInitCodeHash, - v3NFTPositionManager: mapUnsupported(params.v3NFTPositionManager) + v3NFTPositionManager: mapUnsupported(params.v3NFTPositionManager), + v4PositionManager: mapUnsupported(params.v4PositionManager) }); logParams(); @@ -56,6 +57,7 @@ abstract contract DeployUniversalRouter is Script { console2.log('v2Factory:', params.v2Factory); console2.log('v3Factory:', params.v3Factory); console2.log('v3NFTPositionManager:', params.v3NFTPositionManager); + console2.log('v4PositionManager:', params.v4PositionManager); } function mapUnsupported(address protocol) internal view returns (address) { diff --git a/script/deployParameters/DeployArbitrum.s.sol b/script/deployParameters/DeployArbitrum.s.sol index 02cf7b5b..1b66c3d5 100644 --- a/script/deployParameters/DeployArbitrum.s.sol +++ b/script/deployParameters/DeployArbitrum.s.sol @@ -13,7 +13,8 @@ contract DeployArbitrum is DeployUniversalRouter { v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88, + v4PositionManager: address(0) }); unsupported = 0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B; diff --git a/script/deployParameters/DeployArbitrumGoerli.s.sol b/script/deployParameters/DeployArbitrumGoerli.s.sol index fa0bc42d..5e9a62e8 100644 --- a/script/deployParameters/DeployArbitrumGoerli.s.sol +++ b/script/deployParameters/DeployArbitrumGoerli.s.sol @@ -13,7 +13,8 @@ contract DeployArbitrumGoerli is DeployUniversalRouter { v3Factory: 0x4893376342d5D7b3e31d4184c08b265e5aB2A3f6, pairInitCodeHash: BYTES32_ZERO, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployAvalanche.s.sol b/script/deployParameters/DeployAvalanche.s.sol index 9ab12a00..fdbf195a 100644 --- a/script/deployParameters/DeployAvalanche.s.sol +++ b/script/deployParameters/DeployAvalanche.s.sol @@ -13,7 +13,8 @@ contract DeployAvalanche is DeployUniversalRouter { v3Factory: 0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0x655C406EBFa14EE2006250925e54ec43AD184f8B + v3NFTPositionManager: 0x655C406EBFa14EE2006250925e54ec43AD184f8B, + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployBSC.s.sol b/script/deployParameters/DeployBSC.s.sol index 4f5c3833..adebb646 100644 --- a/script/deployParameters/DeployBSC.s.sol +++ b/script/deployParameters/DeployBSC.s.sol @@ -13,7 +13,8 @@ contract DeployBSC is DeployUniversalRouter { v3Factory: 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613 + v3NFTPositionManager: 0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613, + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployBase.s.sol b/script/deployParameters/DeployBase.s.sol index f650533b..0be1f622 100644 --- a/script/deployParameters/DeployBase.s.sol +++ b/script/deployParameters/DeployBase.s.sol @@ -13,7 +13,8 @@ contract DeployBase is DeployUniversalRouter { v3Factory: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1 + v3NFTPositionManager: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1, + v4PositionManager: address(0) }); unsupported = 0x9E18Efb3BE848940b0C92D300504Fb08C287FE85; diff --git a/script/deployParameters/DeployBaseGoerli.s.sol b/script/deployParameters/DeployBaseGoerli.s.sol index 3d459bc8..c04c0a0e 100644 --- a/script/deployParameters/DeployBaseGoerli.s.sol +++ b/script/deployParameters/DeployBaseGoerli.s.sol @@ -13,7 +13,8 @@ contract DeployBaseGoerli is DeployUniversalRouter { v3Factory: 0x9323c1d6D800ed51Bd7C6B216cfBec678B7d0BC2, pairInitCodeHash: BYTES32_ZERO, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); unsupported = 0x7B46ee9BaB49bd5b37117494689A035b0F187B59; diff --git a/script/deployParameters/DeployBlast.s.sol b/script/deployParameters/DeployBlast.s.sol index 2e789b59..a6bf27ca 100644 --- a/script/deployParameters/DeployBlast.s.sol +++ b/script/deployParameters/DeployBlast.s.sol @@ -13,7 +13,8 @@ contract DeployBlast is DeployUniversalRouter { v3Factory: 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0xB218e4f7cF0533d4696fDfC419A0023D33345F28 + v3NFTPositionManager: 0xB218e4f7cF0533d4696fDfC419A0023D33345F28, + v4PositionManager: address(0) }); unsupported = 0x5ab1B56FB16238dB874258FB7847EFe248eb8496; diff --git a/script/deployParameters/DeployCelo.s.sol b/script/deployParameters/DeployCelo.s.sol index b5148247..84ad5911 100644 --- a/script/deployParameters/DeployCelo.s.sol +++ b/script/deployParameters/DeployCelo.s.sol @@ -13,7 +13,8 @@ contract DeployCelo is DeployUniversalRouter { v3Factory: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A + v3NFTPositionManager: 0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A, + v4PositionManager: address(0) }); unsupported = 0x5Dc88340E1c5c6366864Ee415d6034cadd1A9897; diff --git a/script/deployParameters/DeployCeloAlfajores.s.sol b/script/deployParameters/DeployCeloAlfajores.s.sol index 9447b4d8..1c850f92 100644 --- a/script/deployParameters/DeployCeloAlfajores.s.sol +++ b/script/deployParameters/DeployCeloAlfajores.s.sol @@ -13,7 +13,8 @@ contract DeployCeloAlfajores is DeployUniversalRouter { v3Factory: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc, pairInitCodeHash: BYTES32_ZERO, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployGoerli.s.sol b/script/deployParameters/DeployGoerli.s.sol index 85f83f7f..2eab4e09 100644 --- a/script/deployParameters/DeployGoerli.s.sol +++ b/script/deployParameters/DeployGoerli.s.sol @@ -13,7 +13,8 @@ contract DeployGoerli is DeployUniversalRouter { v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployMainnet.s.sol b/script/deployParameters/DeployMainnet.s.sol index 70f204c9..5ee14e27 100644 --- a/script/deployParameters/DeployMainnet.s.sol +++ b/script/deployParameters/DeployMainnet.s.sol @@ -13,7 +13,8 @@ contract DeployMainnet is DeployUniversalRouter { v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88, + v4PositionManager: address(0) }); unsupported = 0x76D631990d505E4e5b432EEDB852A60897824D68; diff --git a/script/deployParameters/DeployOptimism.s.sol b/script/deployParameters/DeployOptimism.s.sol index c3097057..ff86c238 100644 --- a/script/deployParameters/DeployOptimism.s.sol +++ b/script/deployParameters/DeployOptimism.s.sol @@ -13,7 +13,8 @@ contract DeployOptimism is DeployUniversalRouter { v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88, + v4PositionManager: address(0) }); unsupported = 0x40d51104Da22E3e77b683894E7e3E12e8FC61E65; diff --git a/script/deployParameters/DeployOptimismGoerli.s.sol b/script/deployParameters/DeployOptimismGoerli.s.sol index 35125b1c..374039d1 100644 --- a/script/deployParameters/DeployOptimismGoerli.s.sol +++ b/script/deployParameters/DeployOptimismGoerli.s.sol @@ -13,7 +13,8 @@ contract DeployOptimismGoerli is DeployUniversalRouter { v3Factory: 0xB656dA17129e7EB733A557f4EBc57B76CFbB5d10, pairInitCodeHash: BYTES32_ZERO, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeployPolygon.s.sol b/script/deployParameters/DeployPolygon.s.sol index d786990c..ee1fdd1b 100644 --- a/script/deployParameters/DeployPolygon.s.sol +++ b/script/deployParameters/DeployPolygon.s.sol @@ -13,7 +13,8 @@ contract DeployPolygon is DeployUniversalRouter { v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88, + v4PositionManager: address(0) }); unsupported = 0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B; diff --git a/script/deployParameters/DeployPolygonMumbai.s.sol b/script/deployParameters/DeployPolygonMumbai.s.sol index c26cc3b3..c9ebd672 100644 --- a/script/deployParameters/DeployPolygonMumbai.s.sol +++ b/script/deployParameters/DeployPolygonMumbai.s.sol @@ -13,7 +13,8 @@ contract DeployPolygonMumbai is DeployUniversalRouter { v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984, pairInitCodeHash: BYTES32_ZERO, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 + v3NFTPositionManager: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88, + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/script/deployParameters/DeploySepolia.s.sol b/script/deployParameters/DeploySepolia.s.sol index a1f69aa2..05734298 100644 --- a/script/deployParameters/DeploySepolia.s.sol +++ b/script/deployParameters/DeploySepolia.s.sol @@ -13,7 +13,8 @@ contract DeploySepolia is DeployUniversalRouter { v3Factory: 0x0227628f3F023bb0B980b67D528571c95c6DaC1c, pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f, poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54, - v3NFTPositionManager: 0x1238536071E1c677A632429e3655c799b22cDA52 + v3NFTPositionManager: 0x1238536071E1c677A632429e3655c799b22cDA52, + v4PositionManager: address(0) }); unsupported = 0x5302086A3a25d473aAbBd0356eFf8Dd811a4d89B; diff --git a/test/foundry-tests/UniswapV2.t.sol b/test/foundry-tests/UniswapV2.t.sol index eeaa324e..bfa6a568 100644 --- a/test/foundry-tests/UniswapV2.t.sol +++ b/test/foundry-tests/UniswapV2.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; import {IUniswapV2Factory} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol'; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; import {UniversalRouter} from '../../contracts/UniversalRouter.sol'; @@ -34,7 +34,8 @@ abstract contract UniswapV2Test is Test { v3Factory: address(0), pairInitCodeHash: bytes32(0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f), poolInitCodeHash: bytes32(0), - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); router = new UniversalRouter(params); diff --git a/test/foundry-tests/UniversalRouter.t.sol b/test/foundry-tests/UniversalRouter.t.sol index bbad7eac..d8466342 100644 --- a/test/foundry-tests/UniversalRouter.t.sol +++ b/test/foundry-tests/UniversalRouter.t.sol @@ -10,7 +10,7 @@ import {MockERC20} from './mock/MockERC20.sol'; import {Callbacks} from '../../contracts/base/Callbacks.sol'; import {ExampleModule} from '../../contracts/test/ExampleModule.sol'; import {RouterParameters} from '../../contracts/base/RouterImmutables.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; import 'permit2/src/interfaces/IAllowanceTransfer.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; @@ -31,7 +31,8 @@ contract UniversalRouterTest is Test { v3Factory: address(0), pairInitCodeHash: bytes32(0), poolInitCodeHash: bytes32(0), - v3NFTPositionManager: address(0) + v3NFTPositionManager: address(0), + v4PositionManager: address(0) }); router = new UniversalRouter(params); testModule = new ExampleModule(); diff --git a/test/foundry-tests/mock/MockERC20.sol b/test/foundry-tests/mock/MockERC20.sol index a22847e6..10f35b16 100644 --- a/test/foundry-tests/mock/MockERC20.sol +++ b/test/foundry-tests/mock/MockERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; contract MockERC20 is ERC20 { constructor() ERC20('TEST', 'test', 18) {} diff --git a/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol b/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol index fafd3adb..524231d6 100644 --- a/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol +++ b/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; import {UniswapV2Test} from '../UniswapV2.t.sol'; contract V2DaiWeth is UniswapV2Test { diff --git a/test/foundry-tests/uniswapTokens/v2WethApe.t.sol b/test/foundry-tests/uniswapTokens/v2WethApe.t.sol index e807c346..f1133282 100644 --- a/test/foundry-tests/uniswapTokens/v2WethApe.t.sol +++ b/test/foundry-tests/uniswapTokens/v2WethApe.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; -import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {ERC20} from 'solmate/tokens/ERC20.sol'; import {UniswapV2Test} from '../UniswapV2.t.sol'; contract V2WethApe is UniswapV2Test { diff --git a/test/integration-tests/CheckOwnership.test.ts b/test/integration-tests/CheckOwnership.test.ts index 2d8d3e4c..a644c347 100644 --- a/test/integration-tests/CheckOwnership.test.ts +++ b/test/integration-tests/CheckOwnership.test.ts @@ -8,7 +8,7 @@ import hre from 'hardhat' import deployUniversalRouter from './shared/deployUniversalRouter' import { findCustomErrorSelector } from './shared/parseEvents' import { BigNumber, Contract } from 'ethers' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' const { ethers } = hre describe('Check Ownership', () => { diff --git a/test/integration-tests/Uniswap.test.ts b/test/integration-tests/Uniswap.test.ts index 29026680..1c403e11 100644 --- a/test/integration-tests/Uniswap.test.ts +++ b/test/integration-tests/Uniswap.test.ts @@ -3,7 +3,7 @@ import { Pair } from '@uniswap/v2-sdk' import { expect } from './shared/expect' import { BigNumber, BigNumberish } from 'ethers' import { IPermit2, UniversalRouter } from '../../typechain' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from './shared/mainnetForkHelpers' import { ADDRESS_THIS, diff --git a/test/integration-tests/UniversalRouter.test.ts b/test/integration-tests/UniversalRouter.test.ts index f7d58a15..8d123743 100644 --- a/test/integration-tests/UniversalRouter.test.ts +++ b/test/integration-tests/UniversalRouter.test.ts @@ -2,7 +2,7 @@ import { UniversalRouter, ERC20, IWETH9, IPermit2 } from '../../typechain' 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/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { abi as WETH_ABI } from '../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json' import deployUniversalRouter from './shared/deployUniversalRouter' diff --git a/test/integration-tests/V3ToV4Migration.test.ts b/test/integration-tests/V3ToV4Migration.test.ts index b305e886..e9308bff 100644 --- a/test/integration-tests/V3ToV4Migration.test.ts +++ b/test/integration-tests/V3ToV4Migration.test.ts @@ -2,7 +2,7 @@ import type { Contract } from '@ethersproject/contracts' import { expect } from './shared/expect' import { BigNumber } from 'ethers' import { UniversalRouter, INonfungiblePositionManager } from '../../typechain' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, V3_NFT_POSITION_MANAGER } from './shared/mainnetForkHelpers' import { ZERO_ADDRESS, ALICE_ADDRESS, MAX_UINT, MAX_UINT128 } from './shared/constants' import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' diff --git a/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts b/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts index 6b156abd..b4cb6d1e 100644 --- a/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts +++ b/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts @@ -6,7 +6,7 @@ import { ALICE_ADDRESS, DEADLINE } from './../shared/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import hre from 'hardhat' import deployUniversalRouter from './../shared/deployUniversalRouter' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' const { ethers } = hre describe('Check Ownership Gas', () => { diff --git a/test/integration-tests/gas-tests/Payments.gas.test.ts b/test/integration-tests/gas-tests/Payments.gas.test.ts index 0d4a1eab..8504cbfa 100644 --- a/test/integration-tests/gas-tests/Payments.gas.test.ts +++ b/test/integration-tests/gas-tests/Payments.gas.test.ts @@ -1,6 +1,6 @@ import type { Contract } from '@ethersproject/contracts' import { UniversalRouter } from '../../../typechain' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { resetFork, DAI, WETH } from '../shared/mainnetForkHelpers' import { ALICE_ADDRESS, DEADLINE, ETH_ADDRESS, ONE_PERCENT_BIPS } from '../shared/constants' import { expandTo18DecimalsBN } from '../shared/helpers' diff --git a/test/integration-tests/gas-tests/Uniswap.gas.test.ts b/test/integration-tests/gas-tests/Uniswap.gas.test.ts index 899744e8..ae370e2a 100644 --- a/test/integration-tests/gas-tests/Uniswap.gas.test.ts +++ b/test/integration-tests/gas-tests/Uniswap.gas.test.ts @@ -19,7 +19,7 @@ import { } from '../shared/swapRouter02Helpers' import { BigNumber, BigNumberish } from 'ethers' import { IPermit2, UniversalRouter } from '../../../typechain' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { approveAndExecuteSwapRouter02, resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from '../shared/mainnetForkHelpers' import { ADDRESS_THIS, diff --git a/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts b/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts index b7dc0353..3f93f689 100644 --- a/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts +++ b/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts @@ -1,7 +1,7 @@ import { UniversalRouter, IWETH9, ERC20 } from '../../../typechain' import { expect } from '../shared/expect' import { ALICE_ADDRESS } from '../shared/constants' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { abi as WETH_ABI } from '../../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json' import { resetFork, WETH, DAI } from '../shared/mainnetForkHelpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' diff --git a/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts b/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts index e1610bee..5294f9d7 100644 --- a/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts +++ b/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts @@ -3,7 +3,7 @@ import snapshotGasCost from '@uniswap/snapshot-gas-cost' import deployUniversalRouter from '../shared/deployUniversalRouter' import { BigNumber } from 'ethers' import { UniversalRouter, INonfungiblePositionManager } from '../../../typechain' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, V3_NFT_POSITION_MANAGER } from '../shared/mainnetForkHelpers' import { ALICE_ADDRESS, DEADLINE, MAX_UINT, MAX_UINT128 } from '../shared/constants' import { expandTo18DecimalsBN, expandTo6DecimalsBN } from '../shared/helpers' diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index b1127840..ea859feb 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,31 +7,31 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104057`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104069`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138483`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3080913`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3080925`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3234520`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3234484`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102732`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102744`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4306770`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4306782`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508693`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508705`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509023`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299627`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299639`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299575`; diff --git a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap index c07cc408..e53be141 100644 --- a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap @@ -3,27 +3,27 @@ exports[`V3 to V4 Migration Gas Tests Migrator burn gas: erc721permit + decreaseLiquidity + collect + burn 1`] = ` Object { "calldataByteLength": 1092, - "gasUsed": 255764, + "gasUsed": 255774, } `; exports[`V3 to V4 Migration Gas Tests Migrator collect gas: erc721permit + decreaseLiquidity + collect 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 223454, + "gasUsed": 223466, } `; exports[`V3 to V4 Migration Gas Tests Migrator decrease liquidity gas: erc721permit + decreaseLiquidity 1`] = ` Object { "calldataByteLength": 740, - "gasUsed": 201892, + "gasUsed": 201904, } `; exports[`V3 to V4 Migration Gas Tests Migrator erc721permit gas: erc721permit 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 65902, + "gasUsed": 65914, } `; diff --git a/test/integration-tests/shared/deployUniversalRouter.ts b/test/integration-tests/shared/deployUniversalRouter.ts index 3d15949c..be68faa4 100644 --- a/test/integration-tests/shared/deployUniversalRouter.ts +++ b/test/integration-tests/shared/deployUniversalRouter.ts @@ -9,8 +9,10 @@ import { PERMIT2_ADDRESS, V3_NFT_POSITION_MANAGER_MAINNET, } from './constants' +import { deployV4PositionManager, deployV4PoolManager } from './deployV4' export async function deployRouter(mockReentrantWETH?: string): Promise { + const poolManager = await deployV4PoolManager() const routerParameters = { permit2: PERMIT2_ADDRESS, weth9: mockReentrantWETH ?? '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', @@ -19,6 +21,7 @@ export async function deployRouter(mockReentrantWETH?: string): Promise { + const positionManagerFactory = await ethers.getContractFactory('PositionManager') + const positionManager = (await positionManagerFactory.deploy(poolManager)) as unknown as PositionManager + return positionManager.address +} + +export async function deployV4PoolManager(): Promise { + const poolManagerFactory = await ethers.getContractFactory('PoolManager') + const poolManager = (await poolManagerFactory.deploy(500000)) as unknown as PoolManager + return poolManager.address +} From 6c91122491b15beed8d347ad57b9753715cf8db0 Mon Sep 17 00:00:00 2001 From: diana Date: Mon, 29 Jul 2024 16:11:45 -0400 Subject: [PATCH 05/13] update v4-periphery, change remapping, and update deploy test (#361) * update v4-periphery, change remapping, and update deploy test * make immutables public * little fixes * last one * make only migrator immutables public --- contracts/base/Dispatcher.sol | 2 +- contracts/modules/MigratorImmutables.sol | 5 ++- contracts/modules/Payments.sol | 4 +- .../modules/uniswap/UniswapImmutables.sol | 8 ++-- contracts/modules/uniswap/v2/V2SwapRouter.sol | 2 +- contracts/modules/uniswap/v3/V3SwapRouter.sol | 2 +- contracts/test/ImportsForTypechain.sol | 2 +- contracts/test/MintableERC20.sol | 2 +- contracts/test/ReenteringWETH.sol | 2 +- lib/v4-periphery | 2 +- remappings.txt | 2 +- test/foundry-tests/UniswapV2.t.sol | 2 +- test/foundry-tests/UniversalRouter.t.sol | 2 +- test/foundry-tests/mock/MockERC20.sol | 2 +- .../uniswapTokens/v2DaiWeth.t.sol | 2 +- .../uniswapTokens/v2WethApe.t.sol | 2 +- test/integration-tests/CheckOwnership.test.ts | 2 +- test/integration-tests/Uniswap.test.ts | 2 +- .../integration-tests/UniversalRouter.test.ts | 2 +- .../integration-tests/V3ToV4Migration.test.ts | 9 +++- .../gas-tests/CheckOwnership.gas.test.ts | 2 +- .../gas-tests/Payments.gas.test.ts | 4 +- .../gas-tests/Uniswap.gas.test.ts | 2 +- .../gas-tests/UniversalRouter.gas.test.ts | 2 +- .../gas-tests/V3ToV4Migration.gas.test.ts | 2 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 42 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++++----- 28 files changed, 70 insertions(+), 64 deletions(-) diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 5992167f..84869d06 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -10,7 +10,7 @@ import {V3ToV4Migrator} from '../modules/V3ToV4Migrator.sol'; import {Callbacks} from '../base/Callbacks.sol'; import {Commands} from '../libraries/Commands.sol'; import {LockAndMsgSender} from './LockAndMsgSender.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol'; import {IERC721Permit} from '@uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol'; import {Constants} from '../libraries/Constants.sol'; diff --git a/contracts/modules/MigratorImmutables.sol b/contracts/modules/MigratorImmutables.sol index d900b2f2..5632f3ac 100644 --- a/contracts/modules/MigratorImmutables.sol +++ b/contracts/modules/MigratorImmutables.sol @@ -11,8 +11,9 @@ struct MigratorParameters { contract MigratorImmutables { /// @notice v3PositionManager address - INonfungiblePositionManager internal immutable V3_POSITION_MANAGER; - IPositionManager internal immutable V4_POSITION_MANAGER; + INonfungiblePositionManager public immutable V3_POSITION_MANAGER; + /// @notice v4PositionManager address + IPositionManager public immutable V4_POSITION_MANAGER; constructor(MigratorParameters memory params) { V3_POSITION_MANAGER = INonfungiblePositionManager(params.v3PositionManager); diff --git a/contracts/modules/Payments.sol b/contracts/modules/Payments.sol index d9fc7c49..2c4fb485 100644 --- a/contracts/modules/Payments.sol +++ b/contracts/modules/Payments.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.24; import {Constants} from '../libraries/Constants.sol'; import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol'; -import {SafeTransferLib} from 'solmate/utils/SafeTransferLib.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; /// @title Payments contract /// @notice Performs various operations around the payment of ETH and tokens diff --git a/contracts/modules/uniswap/UniswapImmutables.sol b/contracts/modules/uniswap/UniswapImmutables.sol index ea5630c1..07683168 100644 --- a/contracts/modules/uniswap/UniswapImmutables.sol +++ b/contracts/modules/uniswap/UniswapImmutables.sol @@ -9,16 +9,16 @@ struct UniswapParameters { } contract UniswapImmutables { - /// @dev The address of UniswapV2Factory + /// @notice The address of UniswapV2Factory address internal immutable UNISWAP_V2_FACTORY; - /// @dev The UniswapV2Pair initcodehash + /// @notice The UniswapV2Pair initcodehash bytes32 internal immutable UNISWAP_V2_PAIR_INIT_CODE_HASH; - /// @dev The address of UniswapV3Factory + /// @notice The address of UniswapV3Factory address internal immutable UNISWAP_V3_FACTORY; - /// @dev The UniswapV3Pool initcodehash + /// @notice The UniswapV3Pool initcodehash bytes32 internal immutable UNISWAP_V3_POOL_INIT_CODE_HASH; constructor(UniswapParameters memory params) { diff --git a/contracts/modules/uniswap/v2/V2SwapRouter.sol b/contracts/modules/uniswap/v2/V2SwapRouter.sol index e6f11f82..61a1128f 100644 --- a/contracts/modules/uniswap/v2/V2SwapRouter.sol +++ b/contracts/modules/uniswap/v2/V2SwapRouter.sol @@ -7,7 +7,7 @@ import {UniswapImmutables} from '../UniswapImmutables.sol'; import {Payments} from '../../Payments.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {Constants} from '../../../libraries/Constants.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; /// @title Router for Uniswap v2 Trades abstract contract V2SwapRouter is UniswapImmutables, Permit2Payments { diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 36b3d6ae..0384a1db 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -10,7 +10,7 @@ import {Constants} from '../../../libraries/Constants.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {UniswapImmutables} from '../UniswapImmutables.sol'; import {Constants} from '../../../libraries/Constants.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; /// @title Router for Uniswap v3 Trades abstract contract V3SwapRouter is UniswapImmutables, Permit2Payments, IUniswapV3SwapCallback { diff --git a/contracts/test/ImportsForTypechain.sol b/contracts/test/ImportsForTypechain.sol index 9078cc59..08f4a346 100644 --- a/contracts/test/ImportsForTypechain.sol +++ b/contracts/test/ImportsForTypechain.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.17; import {PositionManager} from '@uniswap/v4-periphery/src/PositionManager.sol'; import {PoolManager} from '@uniswap/v4-core/src/PoolManager.sol'; -import {ERC721} from 'solmate/tokens/ERC721.sol'; +import {ERC721} from 'solmate/src/tokens/ERC721.sol'; import {ERC6909} from '@uniswap/v4-core/src/ERC6909.sol'; // this contract only exists to pull PositionManager and PoolManager into the hardhat build pipeline diff --git a/contracts/test/MintableERC20.sol b/contracts/test/MintableERC20.sol index 333470c3..e5b52b5d 100644 --- a/contracts/test/MintableERC20.sol +++ b/contracts/test/MintableERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.15; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; contract MintableERC20 is ERC20 { constructor(uint256 amountToMint) ERC20('test', 'TEST', 18) { diff --git a/contracts/test/ReenteringWETH.sol b/contracts/test/ReenteringWETH.sol index c786ad3f..699f77a4 100644 --- a/contracts/test/ReenteringWETH.sol +++ b/contracts/test/ReenteringWETH.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.15; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; contract ReenteringWETH is ERC20 { error NotAllowedReenter(); diff --git a/lib/v4-periphery b/lib/v4-periphery index 6f094c3f..b9734380 160000 --- a/lib/v4-periphery +++ b/lib/v4-periphery @@ -1 +1 @@ -Subproject commit 6f094c3f246537d9b6ce71c2711546910e64c757 +Subproject commit b97343807ab7055aff8c76f2532265347cb66e48 diff --git a/remappings.txt b/remappings.txt index 055a4d0b..5c90902b 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,4 @@ -solmate/=lib/solmate/src/ +solmate/=lib/solmate/ permit2/=lib/permit2/ forge-std/=lib/forge-std/src/ @openzeppelin/contracts=node_modules/@openzeppelin/contracts diff --git a/test/foundry-tests/UniswapV2.t.sol b/test/foundry-tests/UniswapV2.t.sol index bfa6a568..3d299c0a 100644 --- a/test/foundry-tests/UniswapV2.t.sol +++ b/test/foundry-tests/UniswapV2.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IUniswapV2Factory} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol'; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; import {UniversalRouter} from '../../contracts/UniversalRouter.sol'; diff --git a/test/foundry-tests/UniversalRouter.t.sol b/test/foundry-tests/UniversalRouter.t.sol index d8466342..74a4d107 100644 --- a/test/foundry-tests/UniversalRouter.t.sol +++ b/test/foundry-tests/UniversalRouter.t.sol @@ -10,7 +10,7 @@ import {MockERC20} from './mock/MockERC20.sol'; import {Callbacks} from '../../contracts/base/Callbacks.sol'; import {ExampleModule} from '../../contracts/test/ExampleModule.sol'; import {RouterParameters} from '../../contracts/base/RouterImmutables.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import 'permit2/src/interfaces/IAllowanceTransfer.sol'; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; diff --git a/test/foundry-tests/mock/MockERC20.sol b/test/foundry-tests/mock/MockERC20.sol index 10f35b16..a22847e6 100644 --- a/test/foundry-tests/mock/MockERC20.sol +++ b/test/foundry-tests/mock/MockERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; contract MockERC20 is ERC20 { constructor() ERC20('TEST', 'test', 18) {} diff --git a/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol b/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol index 524231d6..fafd3adb 100644 --- a/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol +++ b/test/foundry-tests/uniswapTokens/v2DaiWeth.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {UniswapV2Test} from '../UniswapV2.t.sol'; contract V2DaiWeth is UniswapV2Test { diff --git a/test/foundry-tests/uniswapTokens/v2WethApe.t.sol b/test/foundry-tests/uniswapTokens/v2WethApe.t.sol index f1133282..e807c346 100644 --- a/test/foundry-tests/uniswapTokens/v2WethApe.t.sol +++ b/test/foundry-tests/uniswapTokens/v2WethApe.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.15; import 'forge-std/Test.sol'; -import {ERC20} from 'solmate/tokens/ERC20.sol'; +import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {UniswapV2Test} from '../UniswapV2.t.sol'; contract V2WethApe is UniswapV2Test { diff --git a/test/integration-tests/CheckOwnership.test.ts b/test/integration-tests/CheckOwnership.test.ts index a644c347..2d8d3e4c 100644 --- a/test/integration-tests/CheckOwnership.test.ts +++ b/test/integration-tests/CheckOwnership.test.ts @@ -8,7 +8,7 @@ import hre from 'hardhat' import deployUniversalRouter from './shared/deployUniversalRouter' import { findCustomErrorSelector } from './shared/parseEvents' import { BigNumber, Contract } from 'ethers' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' const { ethers } = hre describe('Check Ownership', () => { diff --git a/test/integration-tests/Uniswap.test.ts b/test/integration-tests/Uniswap.test.ts index 1c403e11..29026680 100644 --- a/test/integration-tests/Uniswap.test.ts +++ b/test/integration-tests/Uniswap.test.ts @@ -3,7 +3,7 @@ import { Pair } from '@uniswap/v2-sdk' import { expect } from './shared/expect' import { BigNumber, BigNumberish } from 'ethers' import { IPermit2, UniversalRouter } from '../../typechain' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from './shared/mainnetForkHelpers' import { ADDRESS_THIS, diff --git a/test/integration-tests/UniversalRouter.test.ts b/test/integration-tests/UniversalRouter.test.ts index 8d123743..f7d58a15 100644 --- a/test/integration-tests/UniversalRouter.test.ts +++ b/test/integration-tests/UniversalRouter.test.ts @@ -2,7 +2,7 @@ import { UniversalRouter, ERC20, IWETH9, IPermit2 } from '../../typechain' 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 { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { abi as WETH_ABI } from '../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json' import deployUniversalRouter from './shared/deployUniversalRouter' diff --git a/test/integration-tests/V3ToV4Migration.test.ts b/test/integration-tests/V3ToV4Migration.test.ts index e9308bff..ba70691d 100644 --- a/test/integration-tests/V3ToV4Migration.test.ts +++ b/test/integration-tests/V3ToV4Migration.test.ts @@ -1,8 +1,8 @@ import type { Contract } from '@ethersproject/contracts' import { expect } from './shared/expect' import { BigNumber } from 'ethers' -import { UniversalRouter, INonfungiblePositionManager } from '../../typechain' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { UniversalRouter, INonfungiblePositionManager, PositionManager } from '../../typechain' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, V3_NFT_POSITION_MANAGER } from './shared/mainnetForkHelpers' import { ZERO_ADDRESS, ALICE_ADDRESS, MAX_UINT, MAX_UINT128 } from './shared/constants' import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' @@ -26,6 +26,8 @@ describe('V3 to V4 Migration Tests:', () => { let usdcContract: Contract let planner: RoutePlanner let v3NFTPositionManager: INonfungiblePositionManager + let v4PositionManagerAddress: string + let v4PositionManager: PositionManager let tokenIdv3: BigNumber @@ -43,6 +45,9 @@ describe('V3 to V4 Migration Tests:', () => { usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) v3NFTPositionManager = V3_NFT_POSITION_MANAGER.connect(bob) as INonfungiblePositionManager router = (await deployUniversalRouter()) as UniversalRouter + v4PositionManagerAddress = await router.V4_POSITION_MANAGER() + v4PositionManager = (await ethers.getContractAt('PositionManager', v4PositionManagerAddress)) as PositionManager + planner = new RoutePlanner() // alice gives bob some tokens diff --git a/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts b/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts index b4cb6d1e..6b156abd 100644 --- a/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts +++ b/test/integration-tests/gas-tests/CheckOwnership.gas.test.ts @@ -6,7 +6,7 @@ import { ALICE_ADDRESS, DEADLINE } from './../shared/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import hre from 'hardhat' import deployUniversalRouter from './../shared/deployUniversalRouter' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' const { ethers } = hre describe('Check Ownership Gas', () => { diff --git a/test/integration-tests/gas-tests/Payments.gas.test.ts b/test/integration-tests/gas-tests/Payments.gas.test.ts index 8504cbfa..2f58b215 100644 --- a/test/integration-tests/gas-tests/Payments.gas.test.ts +++ b/test/integration-tests/gas-tests/Payments.gas.test.ts @@ -1,6 +1,6 @@ import type { Contract } from '@ethersproject/contracts' -import { UniversalRouter } from '../../../typechain' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { UniversalRouter, PositionManager } from '../../../typechain' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { resetFork, DAI, WETH } from '../shared/mainnetForkHelpers' import { ALICE_ADDRESS, DEADLINE, ETH_ADDRESS, ONE_PERCENT_BIPS } from '../shared/constants' import { expandTo18DecimalsBN } from '../shared/helpers' diff --git a/test/integration-tests/gas-tests/Uniswap.gas.test.ts b/test/integration-tests/gas-tests/Uniswap.gas.test.ts index ae370e2a..899744e8 100644 --- a/test/integration-tests/gas-tests/Uniswap.gas.test.ts +++ b/test/integration-tests/gas-tests/Uniswap.gas.test.ts @@ -19,7 +19,7 @@ import { } from '../shared/swapRouter02Helpers' import { BigNumber, BigNumberish } from 'ethers' import { IPermit2, UniversalRouter } from '../../../typechain' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { approveAndExecuteSwapRouter02, resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from '../shared/mainnetForkHelpers' import { ADDRESS_THIS, diff --git a/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts b/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts index 3f93f689..b7dc0353 100644 --- a/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts +++ b/test/integration-tests/gas-tests/UniversalRouter.gas.test.ts @@ -1,7 +1,7 @@ import { UniversalRouter, IWETH9, ERC20 } from '../../../typechain' import { expect } from '../shared/expect' import { ALICE_ADDRESS } from '../shared/constants' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { abi as WETH_ABI } from '../../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json' import { resetFork, WETH, DAI } from '../shared/mainnetForkHelpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' diff --git a/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts b/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts index 5294f9d7..e1610bee 100644 --- a/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts +++ b/test/integration-tests/gas-tests/V3ToV4Migration.gas.test.ts @@ -3,7 +3,7 @@ import snapshotGasCost from '@uniswap/snapshot-gas-cost' import deployUniversalRouter from '../shared/deployUniversalRouter' import { BigNumber } from 'ethers' import { UniversalRouter, INonfungiblePositionManager } from '../../../typechain' -import { abi as TOKEN_ABI } from '../../../artifacts/solmate/tokens/ERC20.sol/ERC20.json' +import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, V3_NFT_POSITION_MANAGER } from '../shared/mainnetForkHelpers' import { ALICE_ADDRESS, DEADLINE, MAX_UINT, MAX_UINT128 } from '../shared/constants' import { expandTo18DecimalsBN, expandTo6DecimalsBN } from '../shared/helpers' diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index f733f85c..0c4c7a73 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,42 +3,42 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269333, + "gasUsed": 269421, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245060, + "gasUsed": 245148, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245060, + "gasUsed": 245148, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269333, + "gasUsed": 269421, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189426, + "gasUsed": 189470, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 177010, + "gasUsed": 177054, } `; @@ -73,35 +73,35 @@ Object { 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": 176801, + "gasUsed": 176845, } `; 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": 176576, + "gasUsed": 176620, } `; 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": 194601, + "gasUsed": 194645, } `; 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": 184672, + "gasUsed": 184716, } `; 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": 191679, + "gasUsed": 191723, } `; @@ -283,69 +283,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": 105497, + "gasUsed": 105541, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 253882, + "gasUsed": 254014, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 177076, + "gasUsed": 177164, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 115027, + "gasUsed": 115071, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 251040, + "gasUsed": 251172, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 174738, + "gasUsed": 174826, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 121771, + "gasUsed": 121815, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 131373, + "gasUsed": 131417, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 215340, + "gasUsed": 215384, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 126516, + "gasUsed": 126560, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 60b77b7c..05636a62 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14113`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14355`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index ea859feb..a719e820 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104069`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104509`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138483`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138923`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3080925`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3082245`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3234484`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3235804`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102744`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4104504`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4306782`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4308542`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508705`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508881`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509023`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509199`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299639`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299727`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299575`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299663`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; From 373f95b630c8cf0a2563508f52376907547645ea Mon Sep 17 00:00:00 2001 From: Alice Henshaw Date: Tue, 30 Jul 2024 19:13:20 +0100 Subject: [PATCH 06/13] batch permit decode in calldata --- contracts/base/Dispatcher.sol | 6 ++- .../__snapshots__/Payments.gas.test.ts.snap | 10 ++-- .../__snapshots__/Uniswap.gas.test.ts.snap | 52 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 +++---- 5 files changed, 46 insertions(+), 44 deletions(-) diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 84869d06..bf4d837d 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -86,8 +86,10 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr } permit2TransferFrom(token, _msgSender(), map(recipient), amount); } else if (command == Commands.PERMIT2_PERMIT_BATCH) { - (IAllowanceTransfer.PermitBatch memory permitBatch,) = - abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); + IAllowanceTransfer.PermitBatch calldata permitBatch; + assembly { + permitBatch := add(inputs.offset, calldataload(inputs.offset)) + } bytes calldata data = inputs.toBytes(1); PERMIT2.permit(_msgSender(), permitBatch, data); } else if (command == Commands.SWEEP) { diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 84b16c78..1c309e5a 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -3,28 +3,28 @@ exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 37013, + "gasUsed": 36960, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 65690, + "gasUsed": 65584, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 36038, + "gasUsed": 35985, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 31609, + "gasUsed": 31556, } `; @@ -38,7 +38,7 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 50905, + "gasUsed": 50799, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 0c4c7a73..ba20269c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,49 +3,49 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269421, + "gasUsed": 269286, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245148, + "gasUsed": 245013, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245148, + "gasUsed": 245013, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269421, + "gasUsed": 269286, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189470, + "gasUsed": 189429, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 177054, + "gasUsed": 177013, } `; 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": 298488, + "gasUsed": 297045, } `; @@ -59,7 +59,7 @@ Object { 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": 309785, + "gasUsed": 309749, } `; @@ -73,35 +73,35 @@ Object { 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": 176845, + "gasUsed": 176751, } `; 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": 176620, + "gasUsed": 176526, } `; 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": 194645, + "gasUsed": 194604, } `; 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": 184716, + "gasUsed": 184675, } `; 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": 191723, + "gasUsed": 191629, } `; @@ -143,7 +143,7 @@ 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": 126565, + "gasUsed": 126459, } `; @@ -213,14 +213,14 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 128329, + "gasUsed": 128276, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 136301, + "gasUsed": 136195, } `; @@ -283,69 +283,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": 105541, + "gasUsed": 105500, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 254014, + "gasUsed": 253891, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 177164, + "gasUsed": 177082, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 115071, + "gasUsed": 115030, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 251172, + "gasUsed": 251049, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 174826, + "gasUsed": 174744, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 121815, + "gasUsed": 121774, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 131417, + "gasUsed": 131376, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 215384, + "gasUsed": 215343, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 126560, + "gasUsed": 126519, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 05636a62..d060e8ad 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14355`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14018`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index a719e820..bf913bb9 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104509`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104105`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138923`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138531`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3082245`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3081021`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3235804`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3234634`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4104504`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102882`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4308542`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4306992`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508881`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508723`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509199`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509041`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299727`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299651`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299663`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299587`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; From c853d4eef1f394c922f764511db6c6c8e4c3f8f2 Mon Sep 17 00:00:00 2001 From: Alice Henshaw Date: Tue, 30 Jul 2024 19:29:01 +0100 Subject: [PATCH 07/13] array of structs --- contracts/base/Dispatcher.sol | 10 ++++++++-- contracts/modules/Permit2Payments.sol | 2 +- .../CheckOwnership.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 2 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 16 ++++++++-------- .../UniversalRouter.gas.test.ts.snap | 2 +- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index bf4d837d..b0113bf1 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -88,6 +88,8 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr } else if (command == Commands.PERMIT2_PERMIT_BATCH) { IAllowanceTransfer.PermitBatch calldata permitBatch; assembly { + // this is a variable length struct, so calldataload(inputs.offset) contains the + // offset from inputs.offset at which the struct begins permitBatch := add(inputs.offset, calldataload(inputs.offset)) } bytes calldata data = inputs.toBytes(1); @@ -190,8 +192,12 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr } Payments.unwrapWETH9(map(recipient), amountMin); } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { - (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = - abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); + IAllowanceTransfer.AllowanceTransferDetails[] calldata batchDetails; + (uint256 length, uint256 offset) = inputs.toLengthOffset(0); + assembly { + batchDetails.length := length + batchDetails.offset := offset + } permit2TransferFrom(batchDetails, _msgSender()); } else if (command == Commands.BALANCE_CHECK_ERC20) { // equivalent: abi.decode(inputs, (address, address, uint256)) diff --git a/contracts/modules/Permit2Payments.sol b/contracts/modules/Permit2Payments.sol index 49f70b01..ea01cdfb 100644 --- a/contracts/modules/Permit2Payments.sol +++ b/contracts/modules/Permit2Payments.sol @@ -23,7 +23,7 @@ abstract contract Permit2Payments is Payments { /// @notice Performs a batch transferFrom on Permit2 /// @param batchDetails An array detailing each of the transfers that should occur - function permit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails, address owner) + function permit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] calldata batchDetails, address owner) internal { uint256 batchLength = batchDetails.length; diff --git a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap index 361b3d6d..d5544ff7 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Check Ownership Gas gas: balance check ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 37644, + "gasUsed": 37692, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 1c309e5a..88ffee07 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -24,7 +24,7 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 31556, + "gasUsed": 31553, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index ba20269c..34ae5b44 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -59,7 +59,7 @@ Object { 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": 309749, + "gasUsed": 308988, } `; @@ -87,7 +87,7 @@ Object { 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": 194604, + "gasUsed": 194568, } `; @@ -185,21 +185,21 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 106926, + "gasUsed": 106890, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 246827, + "gasUsed": 246791, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 176950, + "gasUsed": 176914, } `; @@ -213,14 +213,14 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 128276, + "gasUsed": 128240, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 136195, + "gasUsed": 136159, } `; @@ -234,7 +234,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 125534, + "gasUsed": 125498, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index d060e8ad..f74fed7b 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14018`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `13879`; From 43f1443a66d74762f23d4a36325efbdd34acf5ee Mon Sep 17 00:00:00 2001 From: Alice <34962750+hensha256@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:04:42 +0100 Subject: [PATCH 08/13] Make msgSender public (#365) --- contracts/base/Dispatcher.sol | 20 ++++----- contracts/base/LockAndMsgSender.sol | 4 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 42 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++++----- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/contracts/base/Dispatcher.sol b/contracts/base/Dispatcher.sol index 84869d06..2c54188c 100644 --- a/contracts/base/Dispatcher.sol +++ b/contracts/base/Dispatcher.sol @@ -56,7 +56,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? _msgSender() : address(this); + address payer = payerIsUser ? msgSender() : address(this); v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); } else if (command == Commands.V3_SWAP_EXACT_OUT) { // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) @@ -72,7 +72,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } bytes calldata path = inputs.toBytes(3); - address payer = payerIsUser ? _msgSender() : address(this); + address payer = payerIsUser ? msgSender() : address(this); v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); } else if (command == Commands.PERMIT2_TRANSFER_FROM) { // equivalent: abi.decode(inputs, (address, address, uint160)) @@ -84,12 +84,12 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr recipient := calldataload(add(inputs.offset, 0x20)) amount := calldataload(add(inputs.offset, 0x40)) } - permit2TransferFrom(token, _msgSender(), map(recipient), amount); + permit2TransferFrom(token, msgSender(), map(recipient), amount); } else if (command == Commands.PERMIT2_PERMIT_BATCH) { (IAllowanceTransfer.PermitBatch memory permitBatch,) = abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes)); bytes calldata data = inputs.toBytes(1); - PERMIT2.permit(_msgSender(), permitBatch, data); + PERMIT2.permit(msgSender(), permitBatch, data); } else if (command == Commands.SWEEP) { // equivalent: abi.decode(inputs, (address, address, uint256)) address token; @@ -143,7 +143,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? _msgSender() : address(this); + address payer = payerIsUser ? msgSender() : address(this); v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer); } else if (command == Commands.V2_SWAP_EXACT_OUT) { // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) @@ -159,7 +159,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr payerIsUser := calldataload(add(inputs.offset, 0x80)) } address[] calldata path = inputs.toAddressArray(3); - address payer = payerIsUser ? _msgSender() : address(this); + address payer = payerIsUser ? msgSender() : address(this); v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer); } else if (command == Commands.PERMIT2_PERMIT) { // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes)) @@ -168,7 +168,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr permitSingle := inputs.offset } bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) - PERMIT2.permit(_msgSender(), permitSingle, data); + PERMIT2.permit(msgSender(), permitSingle, data); } else if (command == Commands.WRAP_ETH) { // equivalent: abi.decode(inputs, (address, uint256)) address recipient; @@ -190,7 +190,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) { (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) = abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[])); - permit2TransferFrom(batchDetails, _msgSender()); + permit2TransferFrom(batchDetails, msgSender()); } else if (command == Commands.BALANCE_CHECK_ERC20) { // equivalent: abi.decode(inputs, (address, address, uint256)) address owner; @@ -237,7 +237,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr // This can be done in 2 ways: // 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens // 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token - if (!isAuthorizedForToken(_msgSender(), tokenId)) { + if (!isAuthorizedForToken(msgSender(), tokenId)) { revert NotAuthorizedForToken(tokenId); } @@ -265,7 +265,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V3ToV4Migr /// @return output The resultant recipient for the command function map(address recipient) internal view returns (address) { if (recipient == Constants.MSG_SENDER) { - return _msgSender(); + return msgSender(); } else if (recipient == Constants.ADDRESS_THIS) { return address(this); } else { diff --git a/contracts/base/LockAndMsgSender.sol b/contracts/base/LockAndMsgSender.sol index c9adbf86..7887583a 100644 --- a/contracts/base/LockAndMsgSender.sol +++ b/contracts/base/LockAndMsgSender.sol @@ -21,8 +21,8 @@ contract LockAndMsgSender { } /// @notice Function to be used instead of msg.sender, as the contract performs self-reentrancy and at - /// times msg.sender == address(this). Instead _msgSender() returns the initiator of the lock - function _msgSender() internal view returns (address) { + /// times msg.sender == address(this). Instead msgSender() returns the initiator of the lock + function msgSender() public view returns (address) { return Locker.get(); } } diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 0c4c7a73..89c4bca5 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,42 +3,42 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269421, + "gasUsed": 269465, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245148, + "gasUsed": 245192, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245148, + "gasUsed": 245192, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269421, + "gasUsed": 269465, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189470, + "gasUsed": 189492, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 177054, + "gasUsed": 177076, } `; @@ -73,35 +73,35 @@ Object { 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": 176845, + "gasUsed": 176867, } `; 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": 176620, + "gasUsed": 176642, } `; 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": 194645, + "gasUsed": 194667, } `; 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": 184716, + "gasUsed": 184738, } `; 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": 191723, + "gasUsed": 191745, } `; @@ -283,69 +283,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": 105541, + "gasUsed": 105563, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 254014, + "gasUsed": 254080, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 177164, + "gasUsed": 177208, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 115071, + "gasUsed": 115093, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 251172, + "gasUsed": 251238, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 174826, + "gasUsed": 174870, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 121815, + "gasUsed": 121837, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 131417, + "gasUsed": 131439, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 215384, + "gasUsed": 215406, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 126560, + "gasUsed": 126582, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 05636a62..865b2b1f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14355`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14478`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index a719e820..561a69cb 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104509`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104729`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138923`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1139143`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3082245`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3082905`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3235804`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3236464`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4104504`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4105384`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4308542`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4309422`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508881`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508969`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509199`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509287`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299727`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299771`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299663`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299707`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; From 12132d54532a13f701e91feb3bff87a3f7ffc25c Mon Sep 17 00:00:00 2001 From: diana Date: Thu, 1 Aug 2024 10:50:55 -0400 Subject: [PATCH 09/13] make maximum input transient (#366) --- contracts/libraries/MaxInputAmount.sol | 20 +++++++++++++ contracts/modules/uniswap/v3/V3SwapRouter.sol | 14 +++------- test/foundry-tests/MaxInputAmount.t.sol | 28 +++++++++++++++++++ .../__snapshots__/Uniswap.gas.test.ts.snap | 12 ++++---- .../UniversalRouter.gas.test.ts.snap | 2 +- 5 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 contracts/libraries/MaxInputAmount.sol create mode 100644 test/foundry-tests/MaxInputAmount.t.sol diff --git a/contracts/libraries/MaxInputAmount.sol b/contracts/libraries/MaxInputAmount.sol new file mode 100644 index 00000000..ea99e4df --- /dev/null +++ b/contracts/libraries/MaxInputAmount.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +/// @notice A library used to store the maximum desired amount of input tokens for exact output swaps; used for checking slippage +library MaxInputAmount { + // The slot holding the the maximum desired amount of input tokens, transiently. bytes32(uint256(keccak256("MaxAmountIn")) - 1) + bytes32 constant MAX_AMOUNT_IN_SLOT = 0xaf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f692; + + function set(uint256 maxAmountIn) internal { + assembly ("memory-safe") { + tstore(MAX_AMOUNT_IN_SLOT, maxAmountIn) + } + } + + function get() internal view returns (uint256 maxAmountIn) { + assembly ("memory-safe") { + maxAmountIn := tload(MAX_AMOUNT_IN_SLOT) + } + } +} diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 0384a1db..cf6e0d55 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -10,6 +10,7 @@ import {Constants} from '../../../libraries/Constants.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {UniswapImmutables} from '../UniswapImmutables.sol'; import {Constants} from '../../../libraries/Constants.sol'; +import {MaxInputAmount} from '../../../libraries/MaxInputAmount.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; /// @title Router for Uniswap v3 Trades @@ -24,13 +25,6 @@ abstract contract V3SwapRouter is UniswapImmutables, Permit2Payments, IUniswapV3 error V3InvalidAmountOut(); error V3InvalidCaller(); - /// @dev Used as the placeholder value for maxAmountIn, because the computed amount in for an exact output swap - /// can never actually be this value - uint256 private constant DEFAULT_MAX_AMOUNT_IN = type(uint256).max; - - /// @dev Transient storage variable used for checking slippage - uint256 private maxAmountInCached = DEFAULT_MAX_AMOUNT_IN; - /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) uint160 internal constant MIN_SQRT_RATIO = 4295128739; @@ -60,7 +54,7 @@ abstract contract V3SwapRouter is UniswapImmutables, Permit2Payments, IUniswapV3 path = path.skipToken(); _swap(-amountToPay.toInt256(), msg.sender, path, payer, false); } else { - if (amountToPay > maxAmountInCached) revert V3TooMuchRequested(); + if (amountToPay > MaxInputAmount.get()) revert V3TooMuchRequested(); // note that because exact output swaps are executed in reverse order, tokenOut is actually tokenIn payOrPermit2Transfer(tokenOut, payer, msg.sender, amountToPay); } @@ -127,7 +121,7 @@ abstract contract V3SwapRouter is UniswapImmutables, Permit2Payments, IUniswapV3 bytes calldata path, address payer ) internal { - maxAmountInCached = amountInMaximum; + MaxInputAmount.set(amountInMaximum); (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap(-amountOut.toInt256(), recipient, path, payer, false); @@ -135,7 +129,7 @@ abstract contract V3SwapRouter is UniswapImmutables, Permit2Payments, IUniswapV3 if (amountOutReceived != amountOut) revert V3InvalidAmountOut(); - maxAmountInCached = DEFAULT_MAX_AMOUNT_IN; + MaxInputAmount.set(0); } /// @dev Performs a single swap for both exactIn and exactOut diff --git a/test/foundry-tests/MaxInputAmount.t.sol b/test/foundry-tests/MaxInputAmount.t.sol new file mode 100644 index 00000000..8a04b137 --- /dev/null +++ b/test/foundry-tests/MaxInputAmount.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import 'forge-std/Test.sol'; +import {MaxInputAmount} from '../../contracts/libraries/MaxInputAmount.sol'; + +contract MaxInputAmountTest is Test { + function test_fuzz_maxAmtIn_set_get(uint256 value1, uint256 value2, uint256 value3) public { + assertEq(MaxInputAmount.get(), 0); + + MaxInputAmount.set(value1); + assertEq(MaxInputAmount.get(), value1); + + MaxInputAmount.set(value2); + assertEq(MaxInputAmount.get(), value2); + + MaxInputAmount.set(value3); + assertEq(MaxInputAmount.get(), value3); + + MaxInputAmount.set(0); + assertEq(MaxInputAmount.get(), 0); + } + + function test_maxAmtInSlot() public { + bytes32 expectedSlot = bytes32(uint256(keccak256('MaxAmountIn')) - 1); + assertEq(expectedSlot, MaxInputAmount.MAX_AMOUNT_IN_SLOT); + } +} diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 89c4bca5..c1e6424c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -87,7 +87,7 @@ Object { 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": 194667, + "gasUsed": 192569, } `; @@ -304,21 +304,21 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 115093, + "gasUsed": 112995, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 251238, + "gasUsed": 249140, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 174870, + "gasUsed": 172772, } `; @@ -332,7 +332,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 131439, + "gasUsed": 129341, } `; @@ -346,6 +346,6 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 126582, + "gasUsed": 124484, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 865b2b1f..b7a73481 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14478`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `14542`; From 49848f9e35fd5543dd2ff7c6065816e5f4d44030 Mon Sep 17 00:00:00 2001 From: diana Date: Thu, 1 Aug 2024 11:44:55 -0400 Subject: [PATCH 10/13] update v4 periphery to main and update deployRouter (#367) * update v4 periphery to main and update deployRouter * point to updated v4 posm --- lib/v4-periphery | 2 +- test/integration-tests/shared/deployUniversalRouter.ts | 2 +- test/integration-tests/shared/deployV4.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/v4-periphery b/lib/v4-periphery index b9734380..c77742c6 160000 --- a/lib/v4-periphery +++ b/lib/v4-periphery @@ -1 +1 @@ -Subproject commit b97343807ab7055aff8c76f2532265347cb66e48 +Subproject commit c77742c6ed0354f8ef1468f7d10f5c62827e7e9c diff --git a/test/integration-tests/shared/deployUniversalRouter.ts b/test/integration-tests/shared/deployUniversalRouter.ts index be68faa4..3c032322 100644 --- a/test/integration-tests/shared/deployUniversalRouter.ts +++ b/test/integration-tests/shared/deployUniversalRouter.ts @@ -21,7 +21,7 @@ export async function deployRouter(mockReentrantWETH?: string): Promise { +export async function deployV4PositionManager(poolManager: string, permit2: string): Promise { const positionManagerFactory = await ethers.getContractFactory('PositionManager') - const positionManager = (await positionManagerFactory.deploy(poolManager)) as unknown as PositionManager + const positionManager = (await positionManagerFactory.deploy(poolManager, permit2)) as unknown as PositionManager return positionManager.address } From bc3d8dddb6cbafc25b7b1ae8ae6000d165e8178e Mon Sep 17 00:00:00 2001 From: Alice <34962750+hensha256@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:56:24 +0100 Subject: [PATCH 11/13] v4 planner (#368) * v4 planner * some renaming --- test/integration-tests/shared/v4Planner.ts | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 test/integration-tests/shared/v4Planner.ts diff --git a/test/integration-tests/shared/v4Planner.ts b/test/integration-tests/shared/v4Planner.ts new file mode 100644 index 00000000..660d58a8 --- /dev/null +++ b/test/integration-tests/shared/v4Planner.ts @@ -0,0 +1,110 @@ +import { defaultAbiCoder } from 'ethers/lib/utils' + +/** + * Actions + * @description Constants that define what action to perform + * @enum {number} + */ +export enum Actions { + // pool actions + // liquidity actions + INCREASE_LIQUIDITY = 0x00, + DECREASE_LIQUIDITY = 0x01, + MINT_POSITION = 0x02, + BURN_POSITION = 0x03, + // swapping + SWAP_EXACT_IN_SINGLE = 0x04, + SWAP_EXACT_IN = 0x05, + SWAP_EXACT_OUT_SINGLE = 0x06, + SWAP_EXACT_OUT = 0x07, + // donate + // DONATE = 0x08, + + // closing deltas on the pool manager + // settling + // SETTLE = 0x10, + SETTLE_ALL = 0x11, + SETTLE_WITH_BALANCE = 0x12, + // taking + // TAKE = 0x13, + TAKE_ALL = 0x14, + // TAKE_PORTION = 0x15, + + CLOSE_CURRENCY = 0x16, + // CLOSE_PAIR = 0x17, + // CLEAR = 0x18, + SWEEP = 0x19, + + // minting/burning 6909s to close deltas + // MINT_6909 = 0x20, + // BURN_6909 = 0x21, +} + +const POOL_KEY_STRUCT = '(address currency0,address currency1,uint24 fee,int24 tickSpacing,address hooks)' + +const PATH_KEY_STRUCT = '(address intermediateCurrency,uint256 fee,int24 tickSpacing,address hooks,bytes hookData)' + +const POSITION_CONFIG_STRUCT = '(' + POOL_KEY_STRUCT + ' poolKey,int24 tickLower,int24 tickUpper)' + +const SWAP_EXACT_IN_SINGLE_STRUCT = + '(' + + POOL_KEY_STRUCT + + ' poolKey,bool zeroForOne,uint128 amountIn,uint128 amountOutMinimum,uint160 sqrtPriceLimitX96,bytes hookData)' + +const SWAP_EXACT_IN_STRUCT = + '(address currencyIn,' + PATH_KEY_STRUCT + '[] path,uint128 amountIn,uint128 amountOutMinimum)' + +const SWAP_EXACT_OUT_SINGLE_STRUCT = + '(' + + POOL_KEY_STRUCT + + ' poolKey,bool zeroForOne,uint128 amountOut,uint128 amountInMaximum,uint160 sqrtPriceLimitX96,bytes hookData)' + +const SWAP_EXACT_OUT_STRUCT = + '(address currencyOut,' + PATH_KEY_STRUCT + '[] path,uint128 amountOut,uint128 amountInMaximum)' + +const ABI_DEFINITION: { [key in Actions]: string[] } = { + // Liquidity commands + [Actions.INCREASE_LIQUIDITY]: ['uint256', POSITION_CONFIG_STRUCT, 'uint256', 'bytes'], + [Actions.DECREASE_LIQUIDITY]: ['uint256', POSITION_CONFIG_STRUCT, 'uint256', 'bytes'], + [Actions.MINT_POSITION]: [POSITION_CONFIG_STRUCT, 'uint256', 'address', 'bytes'], + [Actions.BURN_POSITION]: ['uint256', POSITION_CONFIG_STRUCT, 'bytes'], + + // Swapping commands + [Actions.SWAP_EXACT_IN_SINGLE]: [SWAP_EXACT_IN_SINGLE_STRUCT], + [Actions.SWAP_EXACT_IN]: [SWAP_EXACT_IN_STRUCT], + [Actions.SWAP_EXACT_OUT_SINGLE]: [SWAP_EXACT_OUT_SINGLE_STRUCT], + [Actions.SWAP_EXACT_OUT]: [SWAP_EXACT_OUT_STRUCT], + + // Payments commands + [Actions.SETTLE_ALL]: ['address'], + [Actions.SETTLE_WITH_BALANCE]: ['address'], + [Actions.TAKE_ALL]: ['address', 'address'], + [Actions.CLOSE_CURRENCY]: ['address'], + [Actions.SWEEP]: ['address', 'address'], +} + +export class V4Planner { + actions: string + params: string[] + + constructor() { + this.actions = '0x' + this.params = [] + } + + addAction(type: Actions, parameters: any[]): void { + let command = createAction(type, parameters) + this.params.push(command.encodedInput) + this.actions = this.actions.concat(command.action.toString(16).padStart(2, '0')) + } +} + +export type RouterAction = { + action: Actions + encodedInput: string +} + +export function createAction(action: Actions, parameters: any[]): RouterAction { + const encodedInput = defaultAbiCoder.encode(ABI_DEFINITION[action], parameters) + return { action, encodedInput } +} From 297efe5667762257a8cd8649b72ecfd219a78d6a Mon Sep 17 00:00:00 2001 From: Alice <34962750+hensha256@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:38:57 +0100 Subject: [PATCH 12/13] separate uniswap tests (#369) * separate uniswap tests * Move permit2 tests --- test/integration-tests/Uniswap.test.ts | 1395 ------------------- test/integration-tests/UniswapMixed.test.ts | 763 ++++++++++ test/integration-tests/UniswapV2.test.ts | 409 ++++++ test/integration-tests/UniswapV3.test.ts | 344 +++++ 4 files changed, 1516 insertions(+), 1395 deletions(-) delete mode 100644 test/integration-tests/Uniswap.test.ts create mode 100644 test/integration-tests/UniswapMixed.test.ts create mode 100644 test/integration-tests/UniswapV2.test.ts create mode 100644 test/integration-tests/UniswapV3.test.ts diff --git a/test/integration-tests/Uniswap.test.ts b/test/integration-tests/Uniswap.test.ts deleted file mode 100644 index 29026680..00000000 --- a/test/integration-tests/Uniswap.test.ts +++ /dev/null @@ -1,1395 +0,0 @@ -import type { Contract } from '@ethersproject/contracts' -import { Pair } from '@uniswap/v2-sdk' -import { expect } from './shared/expect' -import { BigNumber, BigNumberish } from 'ethers' -import { IPermit2, UniversalRouter } from '../../typechain' -import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' -import { resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from './shared/mainnetForkHelpers' -import { - ADDRESS_THIS, - ALICE_ADDRESS, - CONTRACT_BALANCE, - DEADLINE, - ETH_ADDRESS, - MAX_UINT, - MAX_UINT160, - MSG_SENDER, - ONE_PERCENT_BIPS, - SOURCE_MSG_SENDER, - SOURCE_ROUTER, -} from './shared/constants' -import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import deployUniversalRouter from './shared/deployUniversalRouter' -import { RoutePlanner, CommandType } from './shared/planner' -import hre from 'hardhat' -import { getPermitSignature, getPermitBatchSignature, PermitSingle } from './shared/protocolHelpers/permit2' -import { encodePathExactInput, encodePathExactOutput } from './shared/swapRouter02Helpers' -import { executeRouter } from './shared/executeRouter' -const { ethers } = hre - -describe('Uniswap V2 and V3 Tests:', () => { - let alice: SignerWithAddress - let bob: SignerWithAddress - let router: UniversalRouter - let permit2: IPermit2 - let daiContract: Contract - let wethContract: Contract - let usdcContract: Contract - let planner: RoutePlanner - - beforeEach(async () => { - await resetFork() - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [ALICE_ADDRESS], - }) - alice = await ethers.getSigner(ALICE_ADDRESS) - bob = (await ethers.getSigners())[1] - daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) - wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) - usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) - permit2 = PERMIT2.connect(bob) as IPermit2 - router = (await deployUniversalRouter()) as UniversalRouter - planner = new RoutePlanner() - - // alice gives bob some tokens - await daiContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100000)) - await wethContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100)) - await usdcContract.connect(alice).transfer(bob.address, expandTo6DecimalsBN(100000)) - - // Bob max-approves the permit2 contract to access his DAI and WETH - await daiContract.connect(bob).approve(permit2.address, MAX_UINT) - await wethContract.connect(bob).approve(permit2.address, MAX_UINT) - await usdcContract.connect(bob).approve(permit2.address, MAX_UINT) - }) - - describe('Trade on Uniswap with Permit2, giving approval every time', () => { - describe('ERC20 --> ERC20', () => { - let permit: PermitSingle - - it('V2 exactIn, permiting the exact amount', async () => { - const amountInDAI = expandTo18DecimalsBN(100) - const minAmountOutWETH = expandTo18DecimalsBN(0.02) - - // second bob signs a permit to allow the router to access his DAI - permit = { - details: { - token: DAI.address, - amount: amountInDAI, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - // 1) permit the router to access funds, 2) withdraw the funds into the pair, 3) trade - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - amountInDAI, - minAmountOutWETH, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOutWETH) - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) - }) - - it('V2 exactOut, permiting the maxAmountIn', async () => { - const maxAmountInDAI = expandTo18DecimalsBN(4000) - const amountOutWETH = expandTo18DecimalsBN(1) - - // second bob signs a permit to allow the router to access his DAI - permit = { - details: { - token: DAI.address, - amount: maxAmountInDAI, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - // 1) permit the router to access funds, 2) trade - the transfer happens within the trade for exactOut - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - MSG_SENDER, - amountOutWETH, - maxAmountInDAI, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.eq(amountOutWETH) - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.lte(maxAmountInDAI) - }) - - it('V2 exactIn, swapping more than max_uint160 should revert', async () => { - const max_uint = BigNumber.from(MAX_UINT160) - const minAmountOutWETH = expandTo18DecimalsBN(0.03) - - // second bob signs a permit to allow the router to access his DAI - permit = { - details: { - token: DAI.address, - amount: max_uint, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - // 1) permit the router to access funds, 2) withdraw the funds into the pair, 3) trade - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - BigNumber.from(MAX_UINT160).add(1), - minAmountOutWETH, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - - const testCustomErrors = await (await ethers.getContractFactory('TestCustomErrors')).deploy() - await expect( - executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) - ).to.be.revertedWithCustomError(testCustomErrors, 'UnsafeCast') - }) - - it('V3 exactIn, permiting the exact amount', async () => { - const amountInDAI = expandTo18DecimalsBN(100) - const minAmountOutWETH = expandTo18DecimalsBN(0.02) - - // first bob approves permit2 to access his DAI - await daiContract.connect(bob).approve(permit2.address, MAX_UINT) - - // second bob signs a permit to allow the router to access his DAI - permit = { - details: { - token: DAI.address, - amount: amountInDAI, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - const path = encodePathExactInput([DAI.address, WETH.address]) - - // 1) permit the router to access funds, 2) trade, which takes the funds directly from permit2 - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - amountInDAI, - minAmountOutWETH, - path, - SOURCE_MSG_SENDER, - ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOutWETH) - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) - }) - - it('V3 exactOut, permiting the exact amount', async () => { - const maxAmountInDAI = expandTo18DecimalsBN(4000) - const amountOutWETH = expandTo18DecimalsBN(1) - - // first bob approves permit2 to access his DAI - await daiContract.connect(bob).approve(permit2.address, MAX_UINT) - - // second bob signs a permit to allow the router to access his DAI - permit = { - details: { - token: DAI.address, - amount: maxAmountInDAI, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - spender: router.address, - sigDeadline: DEADLINE, - } - const sig = await getPermitSignature(permit, bob, permit2) - - const path = encodePathExactOutput([DAI.address, WETH.address]) - - // 1) permit the router to access funds, 2) trade, which takes the funds directly from permit2 - planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) - planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [ - MSG_SENDER, - amountOutWETH, - maxAmountInDAI, - path, - SOURCE_MSG_SENDER, - ]) - const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.eq(amountOutWETH) - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.lte(maxAmountInDAI) - }) - }) - }) - - describe('Trade on UniswapV2', () => { - const amountIn: BigNumber = expandTo18DecimalsBN(5) - beforeEach(async () => { - // for these tests Bob gives the router max approval on permit2 - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) - }) - - describe('ERC20 --> ERC20', () => { - it('completes a V2 exactIn swap', async () => { - const minAmountOut = expandTo18DecimalsBN(0.0001) - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - amountIn, - minAmountOut, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gt(minAmountOut) - }) - - it('completes a V2 exactOut swap', async () => { - const amountOut = expandTo18DecimalsBN(1) - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - MSG_SENDER, - amountOut, - expandTo18DecimalsBN(10000), - [WETH.address, DAI.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, 0]) - const { daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gt(amountOut) - }) - - it('exactIn trade, where an output fee is taken', async () => { - // back to the router so someone can take a fee - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - amountIn, - 1, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.PAY_PORTION, [WETH.address, alice.address, ONE_PERCENT_BIPS]) - planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, 1]) - - const { commands, inputs } = planner - const wethBalanceBeforeAlice = await wethContract.balanceOf(alice.address) - const wethBalanceBeforeBob = await wethContract.balanceOf(bob.address) - - await router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) - - const wethBalanceAfterAlice = await wethContract.balanceOf(alice.address) - const wethBalanceAfterBob = await wethContract.balanceOf(bob.address) - - const aliceFee = wethBalanceAfterAlice.sub(wethBalanceBeforeAlice) - const bobEarnings = wethBalanceAfterBob.sub(wethBalanceBeforeBob) - - expect(bobEarnings).to.be.gt(0) - expect(aliceFee).to.be.gt(0) - - // total fee is 1% of bob's output - expect(aliceFee.add(bobEarnings).mul(ONE_PERCENT_BIPS).div(10_000)).to.eq(aliceFee) - }) - - it('completes a V2 exactIn swap with longer path', async () => { - const minAmountOut = expandTo18DecimalsBN(0.0001) - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - amountIn, - minAmountOut, - [DAI.address, USDC.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gt(minAmountOut) - }) - }) - - describe('ERC20 --> ETH', () => { - it('completes a V2 exactIn swap', async () => { - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - amountIn, - 1, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) - - const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1Out: wethTraded } = v2SwapEventArgs! - - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethTraded.sub(gasSpent)) - }) - - it('completes a V2 exactOut swap', async () => { - const amountOut = expandTo18DecimalsBN(1) - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - ADDRESS_THIS, - amountOut, - expandTo18DecimalsBN(10000), - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, amountOut]) - planner.addCommand(CommandType.SWEEP, [DAI.address, MSG_SENDER, 0]) - - const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1Out: wethTraded } = v2SwapEventArgs! - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(amountOut.sub(gasSpent)) - expect(wethTraded).to.eq(amountOut) - }) - - it('completes a V2 exactOut swap, with ETH fee', async () => { - const amountOut = expandTo18DecimalsBN(1) - const totalPortion = amountOut.mul(ONE_PERCENT_BIPS).div(10000) - const actualAmountOut = amountOut.sub(totalPortion) - - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - ADDRESS_THIS, - amountOut, - expandTo18DecimalsBN(10000), - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, amountOut]) - planner.addCommand(CommandType.PAY_PORTION, [ETH_ADDRESS, alice.address, ONE_PERCENT_BIPS]) - planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, MSG_SENDER, 0]) - - const { commands, inputs } = planner - - await expect( - router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) - ).to.changeEtherBalances([alice, bob], [totalPortion, actualAmountOut]) - }) - }) - - describe('ETH --> ERC20', () => { - it('completes a V2 exactIn swap', async () => { - const minAmountOut = expandTo18DecimalsBN(0.001) - const pairAddress = Pair.getAddress(DAI, WETH) - planner.addCommand(CommandType.WRAP_ETH, [pairAddress, amountIn]) - // amountIn of 0 because the weth is already in the pair - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - 0, - minAmountOut, - [WETH.address, DAI.address], - SOURCE_MSG_SENDER, - ]) - - const { daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract, - amountIn - ) - const { amount0Out: daiTraded } = v2SwapEventArgs! - - expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gt(minAmountOut) - expect(daiBalanceAfter.sub(daiBalanceBefore)).to.equal(daiTraded) - }) - - it('completes a V2 exactOut swap', async () => { - const amountOut = expandTo18DecimalsBN(100) - const value = expandTo18DecimalsBN(1) - - planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, value]) - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - MSG_SENDER, - amountOut, - expandTo18DecimalsBN(1), - [WETH.address, DAI.address], - SOURCE_ROUTER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) - - const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs, gasSpent } = - await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, value) - const { amount0Out: daiTraded, amount1In: wethTraded } = v2SwapEventArgs! - expect(daiBalanceAfter.sub(daiBalanceBefore)).gt(amountOut) // rounding - expect(daiBalanceAfter.sub(daiBalanceBefore)).eq(daiTraded) - expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(wethTraded.add(gasSpent)) - }) - }) - }) - - describe('Trade on UniswapV3', () => { - const amountIn: BigNumber = expandTo18DecimalsBN(500) - const amountInMax: BigNumber = expandTo18DecimalsBN(5000) - const amountOut: BigNumber = expandTo18DecimalsBN(1) - - beforeEach(async () => { - // for these tests Bob gives the router max approval on permit2 - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) - }) - - const addV3ExactInTrades = ( - planner: RoutePlanner, - numTrades: BigNumberish, - amountOutMin: BigNumberish, - recipient?: string, - tokens: string[] = [DAI.address, WETH.address], - tokenSource: boolean = SOURCE_MSG_SENDER - ) => { - const path = encodePathExactInput(tokens) - for (let i = 0; i < numTrades; i++) { - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - recipient ?? MSG_SENDER, - amountIn, - amountOutMin, - path, - tokenSource, - ]) - } - } - - describe('ERC20 --> ERC20', () => { - it('completes a V3 exactIn swap', async () => { - const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) - addV3ExactInTrades(planner, 1, amountOutMin) - - const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1: wethTraded } = v3SwapEventArgs! - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(amountOutMin) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1)) - }) - - it('completes a V3 exactIn swap with longer path', async () => { - const amountOutMin: number = 3 * 10 ** 6 - addV3ExactInTrades( - planner, - 1, - amountOutMin, - MSG_SENDER, - [DAI.address, WETH.address, USDC.address], - SOURCE_MSG_SENDER - ) - - const { - daiBalanceBefore, - daiBalanceAfter, - wethBalanceBefore, - wethBalanceAfter, - usdcBalanceBefore, - usdcBalanceAfter, - } = await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) - - expect(daiBalanceBefore.sub(amountIn)).to.eq(daiBalanceAfter) - expect(wethBalanceAfter).to.eq(wethBalanceBefore) - expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(amountOutMin) - }) - - it('completes a V3 exactOut swap', async () => { - // trade DAI in for WETH out - const tokens = [DAI.address, WETH.address] - const path = encodePathExactOutput(tokens) - - planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) - - const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount0: daiTraded } = v3SwapEventArgs! - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(amountOut) - expect(daiTraded).to.be.lt(amountInMax) - }) - - it('completes a V3 exactOut swap with longer path', async () => { - // trade DAI in for WETH out - const tokens = [DAI.address, USDC.address, WETH.address] - const path = encodePathExactOutput(tokens) - - planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) - const { commands, inputs } = planner - - const balanceWethBefore = await wethContract.balanceOf(bob.address) - await router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) - const balanceWethAfter = await wethContract.balanceOf(bob.address) - expect(balanceWethAfter.sub(balanceWethBefore)).to.eq(amountOut) - }) - }) - - describe('ERC20 --> ETH', () => { - it('completes a V3 exactIn swap', async () => { - const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) - addV3ExactInTrades(planner, 1, amountOutMin, ADDRESS_THIS) - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) - - const { ethBalanceBefore, ethBalanceAfter, v3SwapEventArgs, gasSpent } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1: wethTraded } = v3SwapEventArgs! - - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.be.gte(amountOutMin.sub(gasSpent)) - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethTraded.mul(-1).sub(gasSpent)) - }) - - it('completes a V3 exactOut swap', async () => { - // trade DAI in for WETH out - const tokens = [DAI.address, WETH.address] - const path = encodePathExactOutput(tokens) - - planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [ - ADDRESS_THIS, - amountOut, - amountInMax, - path, - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, amountOut]) - - const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(amountOut.sub(gasSpent)) - }) - }) - - describe('ETH --> ERC20', () => { - it('completes a V3 exactIn swap', async () => { - const tokens = [WETH.address, DAI.address] - const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) - - planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, amountIn]) - addV3ExactInTrades(planner, 1, amountOutMin, MSG_SENDER, tokens, SOURCE_ROUTER) - - const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, gasSpent } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract, - amountIn - ) - - expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(amountIn.add(gasSpent)) - expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gte(amountOutMin) - }) - - it('completes a V3 exactOut swap', async () => { - const tokens = [WETH.address, DAI.address] - const path = encodePathExactOutput(tokens) - - planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, amountInMax]) - planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_ROUTER]) - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) - - const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, gasSpent, v3SwapEventArgs } = - await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, amountInMax) - const { amount0: daiTraded, amount1: wethTraded } = v3SwapEventArgs! - - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(daiTraded) - expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(wethTraded.add(gasSpent)) - }) - }) - }) - - describe('Mixing V2 and V3', () => { - beforeEach(async () => { - // for these tests Bob gives the router max approval on permit2 - await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) - await permit2.approve(USDC.address, router.address, MAX_UINT160, DEADLINE) - }) - - describe('Interleaving routes', () => { - it('V3, then V2', async () => { - const v3Tokens = [DAI.address, USDC.address] - const v2Tokens = [USDC.address, WETH.address] - const v3AmountIn: BigNumber = expandTo18DecimalsBN(5) - const v3AmountOutMin = 0 - const v2AmountOutMin = expandTo18DecimalsBN(0.0005) - - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - Pair.getAddress(USDC, WETH), - v3AmountIn, - v3AmountOutMin, - encodePathExactInput(v3Tokens), - SOURCE_MSG_SENDER, - ]) - // amountIn of 0 because the USDC is already in the pair - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, v2AmountOutMin, v2Tokens, SOURCE_MSG_SENDER]) - - const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1Out: wethTraded } = v2SwapEventArgs! - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded) - }) - - it('V2, then V3', async () => { - const v2Tokens = [DAI.address, USDC.address] - const v3Tokens = [USDC.address, WETH.address] - const v2AmountIn: BigNumber = expandTo18DecimalsBN(5) - const v2AmountOutMin = 0 // doesnt matter how much USDC it is, what matters is the end of the trade - const v3AmountOutMin = expandTo18DecimalsBN(0.0005) - - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - v2AmountIn, - v2AmountOutMin, - v2Tokens, - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - CONTRACT_BALANCE, - v3AmountOutMin, - encodePathExactInput(v3Tokens), - SOURCE_ROUTER, - ]) - - const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1: wethTraded } = v3SwapEventArgs! - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1)) - }) - }) - - describe('Split routes', () => { - it('ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from', async () => { - const route1 = [DAI.address, USDC.address, WETH.address] - const route2 = [DAI.address, USDT.address, WETH.address] - const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) - const v2AmountIn2: BigNumber = expandTo18DecimalsBN(30) - const minAmountOut1 = expandTo18DecimalsBN(0.005) - const minAmountOut2 = expandTo18DecimalsBN(0.0075) - - // 1) transfer funds into DAI-USDC and DAI-USDT pairs to trade - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [DAI.address, Pair.getAddress(DAI, USDC), v2AmountIn1]) - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [DAI.address, Pair.getAddress(DAI, USDT), v2AmountIn2]) - - // 2) trade route1 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut1, route1, SOURCE_MSG_SENDER]) - // 3) trade route2 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut2, route2, SOURCE_MSG_SENDER]) - - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) - }) - - it('ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch', async () => { - const route1 = [DAI.address, USDC.address, WETH.address] - const route2 = [DAI.address, USDT.address, WETH.address] - const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) - const v2AmountIn2: BigNumber = expandTo18DecimalsBN(30) - const minAmountOut1 = expandTo18DecimalsBN(0.005) - const minAmountOut2 = expandTo18DecimalsBN(0.0075) - - const BATCH_TRANSFER = [ - { - from: bob.address, - to: Pair.getAddress(DAI, USDC), - amount: v2AmountIn1, - token: DAI.address, - }, - { - from: bob.address, - to: Pair.getAddress(DAI, USDT), - amount: v2AmountIn2, - token: DAI.address, - }, - ] - - // 1) transfer funds into DAI-USDC and DAI-USDT pairs to trade - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM_BATCH, [BATCH_TRANSFER]) - - // 2) trade route1 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut1, route1, SOURCE_MSG_SENDER]) - // 3) trade route2 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut2, route2, SOURCE_MSG_SENDER]) - - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) - }) - - it('ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit', async () => { - const route1 = [DAI.address, USDC.address, WETH.address] - const route2 = [DAI.address, USDT.address, WETH.address] - const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) - const v2AmountIn2: BigNumber = expandTo18DecimalsBN(30) - const minAmountOut1 = expandTo18DecimalsBN(0.005) - const minAmountOut2 = expandTo18DecimalsBN(0.0075) - - // 1) trade route1 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - v2AmountIn1, - minAmountOut1, - route1, - SOURCE_MSG_SENDER, - ]) - // 2) trade route2 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - v2AmountIn2, - minAmountOut2, - route2, - SOURCE_MSG_SENDER, - ]) - - const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) - }) - - it('ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit', async () => { - const route1 = [DAI.address, WETH.address, USDC.address] - const route2 = [WETH.address, DAI.address, USDC.address] - const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) - const v2AmountIn2: BigNumber = expandTo18DecimalsBN(5) - const minAmountOut1 = BigNumber.from(0.005 * 10 ** 6) - const minAmountOut2 = BigNumber.from(0.0075 * 10 ** 6) - - const BATCH_PERMIT = { - details: [ - { - token: DAI.address, - amount: v2AmountIn1, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - { - token: WETH.address, - amount: v2AmountIn2, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - ], - spender: router.address, - sigDeadline: DEADLINE, - } - - const sig = await getPermitBatchSignature(BATCH_PERMIT, bob, permit2) - - // 1) transfer funds into DAI-USDC and DAI-USDT pairs to trade - planner.addCommand(CommandType.PERMIT2_PERMIT_BATCH, [BATCH_PERMIT, sig]) - - // 2) trade route1 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - v2AmountIn1, - minAmountOut1, - route1, - SOURCE_MSG_SENDER, - ]) - // 3) trade route2 and return tokens to bob - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - MSG_SENDER, - v2AmountIn2, - minAmountOut2, - route2, - SOURCE_MSG_SENDER, - ]) - - const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) - }) - - it('ERC20 --> ERC20 V3 trades with different input tokens with batch permit and batch transfer', async () => { - const route1 = [DAI.address, WETH.address] - const route2 = [WETH.address, USDC.address] - const v3AmountIn1: BigNumber = expandTo18DecimalsBN(20) - const v3AmountIn2: BigNumber = expandTo18DecimalsBN(5) - const minAmountOut1WETH = BigNumber.from(0) - const minAmountOut1USDC = BigNumber.from(0.005 * 10 ** 6) - const minAmountOut2USDC = BigNumber.from(0.0075 * 10 ** 6) - - const BATCH_PERMIT = { - details: [ - { - token: DAI.address, - amount: v3AmountIn1, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - { - token: WETH.address, - amount: v3AmountIn2, - expiration: 0, // expiration of 0 is block.timestamp - nonce: 0, // this is his first trade - }, - ], - spender: router.address, - sigDeadline: DEADLINE, - } - - const BATCH_TRANSFER = [ - { - from: bob.address, - to: router.address, - amount: v3AmountIn1, - token: DAI.address, - }, - { - from: bob.address, - to: router.address, - amount: v3AmountIn2, - token: WETH.address, - }, - ] - - const sig = await getPermitBatchSignature(BATCH_PERMIT, bob, permit2) - - // 1) permit dai and weth to be spent by router - planner.addCommand(CommandType.PERMIT2_PERMIT_BATCH, [BATCH_PERMIT, sig]) - - // 2) transfer dai and weth into router to use contract balance - planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM_BATCH, [BATCH_TRANSFER]) - - // v3SwapExactInput(recipient, amountIn, amountOutMin, path, payer); - - // 2) trade route1 and return tokens to router for the second trade - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - CONTRACT_BALANCE, - minAmountOut1WETH, - encodePathExactInput(route1), - SOURCE_ROUTER, - ]) - // 3) trade route2 and return tokens to bob - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - CONTRACT_BALANCE, - minAmountOut1USDC.add(minAmountOut2USDC), - encodePathExactInput(route2), - SOURCE_ROUTER, - ]) - - const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(minAmountOut1USDC.add(minAmountOut2USDC)) - }) - - it('ERC20 --> ERC20 split V2 and V3, one hop', async () => { - const tokens = [DAI.address, WETH.address] - const v2AmountIn: BigNumber = expandTo18DecimalsBN(2) - const v3AmountIn: BigNumber = expandTo18DecimalsBN(3) - const minAmountOut = expandTo18DecimalsBN(0.0005) - - // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ADDRESS_THIS, v2AmountIn, 0, tokens, SOURCE_MSG_SENDER]) - // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - v3AmountIn, - 0, - encodePathExactInput(tokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, minAmountOut]) - - const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1Out: wethOutV2 } = v2SwapEventArgs! - let { amount1: wethOutV3 } = v3SwapEventArgs! - - // expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(v2AmountIn.add(v3AmountIn)) // TODO: with permit2 can check from alice's balance - expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethOutV2.sub(wethOutV3)) - }) - - it('ETH --> ERC20 split V2 and V3, one hop', async () => { - const tokens = [WETH.address, USDC.address] - const v2AmountIn: BigNumber = expandTo18DecimalsBN(2) - const v3AmountIn: BigNumber = expandTo18DecimalsBN(3) - const value = v2AmountIn.add(v3AmountIn) - - planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, value]) - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ADDRESS_THIS, v2AmountIn, 0, tokens, SOURCE_ROUTER]) - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - v3AmountIn, - 0, - encodePathExactInput(tokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - planner.addCommand(CommandType.SWEEP, [USDC.address, MSG_SENDER, 0.0005 * 10 ** 6]) - - const { usdcBalanceBefore, usdcBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract, - value - ) - const { amount0Out: usdcOutV2 } = v2SwapEventArgs! - let { amount0: usdcOutV3 } = v3SwapEventArgs! - usdcOutV3 = usdcOutV3.mul(-1) - expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.eq(usdcOutV2.add(usdcOutV3)) - }) - - it('ERC20 --> ETH split V2 and V3, one hop', async () => { - const tokens = [DAI.address, WETH.address] - const v2AmountIn: BigNumber = expandTo18DecimalsBN(20) - const v3AmountIn: BigNumber = expandTo18DecimalsBN(30) - - planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ADDRESS_THIS, v2AmountIn, 0, tokens, SOURCE_MSG_SENDER]) - planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - v3AmountIn, - 0, - encodePathExactInput(tokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, expandTo18DecimalsBN(0.0005)]) - - const { ethBalanceBefore, ethBalanceAfter, gasSpent, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - const { amount1Out: wethOutV2 } = v2SwapEventArgs! - let { amount1: wethOutV3 } = v3SwapEventArgs! - wethOutV3 = wethOutV3.mul(-1) - - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethOutV2.add(wethOutV3).sub(gasSpent)) - }) - - it('ERC20 --> ETH split V2 and V3, exactOut, one hop', async () => { - const tokens = [DAI.address, WETH.address] - const v2AmountOut: BigNumber = expandTo18DecimalsBN(0.5) - const v3AmountOut: BigNumber = expandTo18DecimalsBN(1) - const path = encodePathExactOutput(tokens) - const maxAmountIn = expandTo18DecimalsBN(4000) - const fullAmountOut = v2AmountOut.add(v3AmountOut) - - planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ - ADDRESS_THIS, - v2AmountOut, - maxAmountIn, - [DAI.address, WETH.address], - SOURCE_MSG_SENDER, - ]) - planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [ - ADDRESS_THIS, - v3AmountOut, - maxAmountIn, - path, - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, fullAmountOut]) - - const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - - // TODO: permit2 test alice doesn't send more than maxAmountIn DAI - expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(fullAmountOut.sub(gasSpent)) - }) - - describe('Batch reverts', () => { - let subplan: RoutePlanner - const planOneTokens = [DAI.address, WETH.address] - const planTwoTokens = [USDC.address, WETH.address] - const planOneV2AmountIn: BigNumber = expandTo18DecimalsBN(2) - const planOneV3AmountIn: BigNumber = expandTo18DecimalsBN(3) - const planTwoV3AmountIn = expandTo6DecimalsBN(5) - - beforeEach(async () => { - subplan = new RoutePlanner() - }) - - it('2 sub-plans, neither fails', async () => { - // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. - const planOneWethMinOut = expandTo18DecimalsBN(0.0005) - - // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade - subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV2AmountIn, - 0, - planOneTokens, - SOURCE_MSG_SENDER, - ]) - // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV3AmountIn, - 0, - encodePathExactInput(planOneTokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) - - // add the subplan to the main planner - planner.addSubPlan(subplan) - subplan = new RoutePlanner() - - // second split route sub-plan. USDC->WETH, 1 route on V3 - const wethMinAmountOut2 = expandTo18DecimalsBN(0.0005) - - // Add the trade to the sub-plan - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - planTwoV3AmountIn, - wethMinAmountOut2, - encodePathExactInput(planTwoTokens), - SOURCE_MSG_SENDER, - ]) - - // add the second subplan to the main planner - planner.addSubPlan(subplan) - - const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(planOneV2AmountIn.add(planOneV3AmountIn)) - expect(usdcBalanceBefore.sub(usdcBalanceAfter)).to.eq(planTwoV3AmountIn) - }) - - it('2 sub-plans, the first fails', async () => { - // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. - // FAIL: large weth amount out to cause a failure - const planOneWethMinOut = expandTo18DecimalsBN(1) - - // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade - subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV2AmountIn, - 0, - planOneTokens, - SOURCE_MSG_SENDER, - ]) - // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV3AmountIn, - 0, - encodePathExactInput(planOneTokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) - - // add the subplan to the main planner - planner.addSubPlan(subplan) - subplan = new RoutePlanner() - - // second split route sub-plan. USDC->WETH, 1 route on V3 - const wethMinAmountOut2 = expandTo18DecimalsBN(0.0005) - - // Add the trade to the sub-plan - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - planTwoV3AmountIn, - wethMinAmountOut2, - encodePathExactInput(planTwoTokens), - SOURCE_MSG_SENDER, - ]) - - // add the second subplan to the main planner - planner.addSubPlan(subplan) - - const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - - // dai balance should be unchanged as the weth sweep failed - expect(daiBalanceBefore).to.eq(daiBalanceAfter) - - // usdc is the second trade so the balance has changed - expect(usdcBalanceBefore.sub(usdcBalanceAfter)).to.eq(planTwoV3AmountIn) - }) - - it('2 sub-plans, both fail but the transaction succeeds', async () => { - // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. - // FAIL: large amount out to cause the swap to revert - const planOneWethMinOut = expandTo18DecimalsBN(1) - - // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade - subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV2AmountIn, - 0, - planOneTokens, - SOURCE_MSG_SENDER, - ]) - // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV3AmountIn, - 0, - encodePathExactInput(planOneTokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) - - // add the subplan to the main planner - planner.addSubPlan(subplan) - subplan = new RoutePlanner() - - // second split route sub-plan. USDC->WETH, 1 route on V3 - // FAIL: large amount out to cause the swap to revert - const wethMinAmountOut2 = expandTo18DecimalsBN(1) - - // Add the trade to the sub-plan - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - planTwoV3AmountIn, - wethMinAmountOut2, - encodePathExactInput(planTwoTokens), - SOURCE_MSG_SENDER, - ]) - - // add the second subplan to the main planner - planner.addSubPlan(subplan) - - const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - - // dai and usdc balances both unchanged because both trades failed - expect(daiBalanceBefore).to.eq(daiBalanceAfter) - expect(usdcBalanceBefore).to.eq(usdcBalanceAfter) - }) - - it('2 sub-plans, second sub plan fails', async () => { - // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. - const planOneWethMinOut = expandTo18DecimalsBN(0.0005) - - // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade - subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV2AmountIn, - 0, - planOneTokens, - SOURCE_MSG_SENDER, - ]) - // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - ADDRESS_THIS, - planOneV3AmountIn, - 0, - encodePathExactInput(planOneTokens), - SOURCE_MSG_SENDER, - ]) - // aggregate slippage check - subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) - - // add the subplan to the main planner - planner.addSubPlan(subplan) - subplan = new RoutePlanner() - - // second split route sub-plan. USDC->WETH, 1 route on V3 - // FAIL: large amount out to cause the swap to revert - const wethMinAmountOut2 = expandTo18DecimalsBN(1) - - // Add the trade to the sub-plan - subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ - MSG_SENDER, - planTwoV3AmountIn, - wethMinAmountOut2, - encodePathExactInput(planTwoTokens), - SOURCE_MSG_SENDER, - ]) - - // add the second subplan to the main planner - planner.addSubPlan(subplan) - - const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( - planner, - bob, - router, - wethContract, - daiContract, - usdcContract - ) - - // dai balance has changed as this trade should succeed - expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(planOneV2AmountIn.add(planOneV3AmountIn)) - - // usdc is unchanged as the second trade should have failed - expect(usdcBalanceBefore).to.eq(usdcBalanceAfter) - }) - }) - }) - }) -}) diff --git a/test/integration-tests/UniswapMixed.test.ts b/test/integration-tests/UniswapMixed.test.ts new file mode 100644 index 00000000..e6e899f8 --- /dev/null +++ b/test/integration-tests/UniswapMixed.test.ts @@ -0,0 +1,763 @@ +import type { Contract } from '@ethersproject/contracts' +import { Pair } from '@uniswap/v2-sdk' +import { expect } from './shared/expect' +import { BigNumber } from 'ethers' +import { IPermit2, UniversalRouter } from '../../typechain' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { resetFork, WETH, DAI, USDC, USDT, PERMIT2 } from './shared/mainnetForkHelpers' +import { + ADDRESS_THIS, + ALICE_ADDRESS, + CONTRACT_BALANCE, + DEADLINE, + MAX_UINT, + MAX_UINT160, + MSG_SENDER, + SOURCE_MSG_SENDER, + SOURCE_ROUTER, +} from './shared/constants' +import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import deployUniversalRouter from './shared/deployUniversalRouter' +import { RoutePlanner, CommandType } from './shared/planner' +import hre from 'hardhat' +import { getPermitBatchSignature } from './shared/protocolHelpers/permit2' +import { encodePathExactInput, encodePathExactOutput } from './shared/swapRouter02Helpers' +import { executeRouter } from './shared/executeRouter' +const { ethers } = hre + +describe('Uniswap V2 and V3 Tests:', () => { + let alice: SignerWithAddress + let bob: SignerWithAddress + let router: UniversalRouter + let permit2: IPermit2 + let daiContract: Contract + let wethContract: Contract + let usdcContract: Contract + let planner: RoutePlanner + + beforeEach(async () => { + await resetFork() + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ALICE_ADDRESS], + }) + alice = await ethers.getSigner(ALICE_ADDRESS) + bob = (await ethers.getSigners())[1] + daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) + wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) + usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) + permit2 = PERMIT2.connect(bob) as IPermit2 + router = (await deployUniversalRouter()) as UniversalRouter + planner = new RoutePlanner() + + // alice gives bob some tokens + await daiContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100000)) + await wethContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100)) + await usdcContract.connect(alice).transfer(bob.address, expandTo6DecimalsBN(100000)) + + // Bob max-approves the permit2 contract to access his DAI and WETH + await daiContract.connect(bob).approve(permit2.address, MAX_UINT) + await wethContract.connect(bob).approve(permit2.address, MAX_UINT) + await usdcContract.connect(bob).approve(permit2.address, MAX_UINT) + + // for these tests Bob gives the router max approval on permit2 + await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) + await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) + await permit2.approve(USDC.address, router.address, MAX_UINT160, DEADLINE) + }) + + describe('Interleaving routes', () => { + it('V3, then V2', async () => { + const v3Tokens = [DAI.address, USDC.address] + const v2Tokens = [USDC.address, WETH.address] + const v3AmountIn: BigNumber = expandTo18DecimalsBN(5) + const v3AmountOutMin = 0 + const v2AmountOutMin = expandTo18DecimalsBN(0.0005) + + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + Pair.getAddress(USDC, WETH), + v3AmountIn, + v3AmountOutMin, + encodePathExactInput(v3Tokens), + SOURCE_MSG_SENDER, + ]) + // amountIn of 0 because the USDC is already in the pair + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, v2AmountOutMin, v2Tokens, SOURCE_MSG_SENDER]) + + const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1Out: wethTraded } = v2SwapEventArgs! + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded) + }) + + it('V2, then V3', async () => { + const v2Tokens = [DAI.address, USDC.address] + const v3Tokens = [USDC.address, WETH.address] + const v2AmountIn: BigNumber = expandTo18DecimalsBN(5) + const v2AmountOutMin = 0 // doesnt matter how much USDC it is, what matters is the end of the trade + const v3AmountOutMin = expandTo18DecimalsBN(0.0005) + + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + v2AmountIn, + v2AmountOutMin, + v2Tokens, + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + CONTRACT_BALANCE, + v3AmountOutMin, + encodePathExactInput(v3Tokens), + SOURCE_ROUTER, + ]) + + const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1: wethTraded } = v3SwapEventArgs! + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1)) + }) + }) + + describe('Split routes', () => { + it('ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from', async () => { + const route1 = [DAI.address, USDC.address, WETH.address] + const route2 = [DAI.address, USDT.address, WETH.address] + const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) + const v2AmountIn2: BigNumber = expandTo18DecimalsBN(30) + const minAmountOut1 = expandTo18DecimalsBN(0.005) + const minAmountOut2 = expandTo18DecimalsBN(0.0075) + + // 1) transfer funds into DAI-USDC and DAI-USDT pairs to trade + planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [DAI.address, Pair.getAddress(DAI, USDC), v2AmountIn1]) + planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [DAI.address, Pair.getAddress(DAI, USDT), v2AmountIn2]) + + // 2) trade route1 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut1, route1, SOURCE_MSG_SENDER]) + // 3) trade route2 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut2, route2, SOURCE_MSG_SENDER]) + + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) + }) + + it('ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch', async () => { + const route1 = [DAI.address, USDC.address, WETH.address] + const route2 = [DAI.address, USDT.address, WETH.address] + const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) + const v2AmountIn2: BigNumber = expandTo18DecimalsBN(30) + const minAmountOut1 = expandTo18DecimalsBN(0.005) + const minAmountOut2 = expandTo18DecimalsBN(0.0075) + + const BATCH_TRANSFER = [ + { + from: bob.address, + to: Pair.getAddress(DAI, USDC), + amount: v2AmountIn1, + token: DAI.address, + }, + { + from: bob.address, + to: Pair.getAddress(DAI, USDT), + amount: v2AmountIn2, + token: DAI.address, + }, + ] + + // 1) transfer funds into DAI-USDC and DAI-USDT pairs to trade + planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM_BATCH, [BATCH_TRANSFER]) + + // 2) trade route1 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut1, route1, SOURCE_MSG_SENDER]) + // 3) trade route2 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, minAmountOut2, route2, SOURCE_MSG_SENDER]) + + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) + }) + + it('ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit', async () => { + const route1 = [DAI.address, USDC.address, WETH.address] + const route2 = [DAI.address, USDT.address, WETH.address] + const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) + const v2AmountIn2: BigNumber = expandTo18DecimalsBN(30) + const minAmountOut1 = expandTo18DecimalsBN(0.005) + const minAmountOut2 = expandTo18DecimalsBN(0.0075) + + // 1) trade route1 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + v2AmountIn1, + minAmountOut1, + route1, + SOURCE_MSG_SENDER, + ]) + // 2) trade route2 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + v2AmountIn2, + minAmountOut2, + route2, + SOURCE_MSG_SENDER, + ]) + + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) + }) + + it('ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit', async () => { + const route1 = [DAI.address, WETH.address, USDC.address] + const route2 = [WETH.address, DAI.address, USDC.address] + const v2AmountIn1: BigNumber = expandTo18DecimalsBN(20) + const v2AmountIn2: BigNumber = expandTo18DecimalsBN(5) + const minAmountOut1 = BigNumber.from(0.005 * 10 ** 6) + const minAmountOut2 = BigNumber.from(0.0075 * 10 ** 6) + + const BATCH_PERMIT = { + details: [ + { + token: DAI.address, + amount: v2AmountIn1, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + { + token: WETH.address, + amount: v2AmountIn2, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + ], + spender: router.address, + sigDeadline: DEADLINE, + } + + const sig = await getPermitBatchSignature(BATCH_PERMIT, bob, permit2) + + // 1) transfer funds into DAI-USDC and DAI-USDT pairs to trade + planner.addCommand(CommandType.PERMIT2_PERMIT_BATCH, [BATCH_PERMIT, sig]) + + // 2) trade route1 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + v2AmountIn1, + minAmountOut1, + route1, + SOURCE_MSG_SENDER, + ]) + // 3) trade route2 and return tokens to bob + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + v2AmountIn2, + minAmountOut2, + route2, + SOURCE_MSG_SENDER, + ]) + + const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(minAmountOut1.add(minAmountOut2)) + }) + + it('ERC20 --> ERC20 V3 trades with different input tokens with batch permit and batch transfer', async () => { + const route1 = [DAI.address, WETH.address] + const route2 = [WETH.address, USDC.address] + const v3AmountIn1: BigNumber = expandTo18DecimalsBN(20) + const v3AmountIn2: BigNumber = expandTo18DecimalsBN(5) + const minAmountOut1WETH = BigNumber.from(0) + const minAmountOut1USDC = BigNumber.from(0.005 * 10 ** 6) + const minAmountOut2USDC = BigNumber.from(0.0075 * 10 ** 6) + + const BATCH_PERMIT = { + details: [ + { + token: DAI.address, + amount: v3AmountIn1, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + { + token: WETH.address, + amount: v3AmountIn2, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + ], + spender: router.address, + sigDeadline: DEADLINE, + } + + const BATCH_TRANSFER = [ + { + from: bob.address, + to: router.address, + amount: v3AmountIn1, + token: DAI.address, + }, + { + from: bob.address, + to: router.address, + amount: v3AmountIn2, + token: WETH.address, + }, + ] + + const sig = await getPermitBatchSignature(BATCH_PERMIT, bob, permit2) + + // 1) permit dai and weth to be spent by router + planner.addCommand(CommandType.PERMIT2_PERMIT_BATCH, [BATCH_PERMIT, sig]) + + // 2) transfer dai and weth into router to use contract balance + planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM_BATCH, [BATCH_TRANSFER]) + + // v3SwapExactInput(recipient, amountIn, amountOutMin, path, payer); + + // 2) trade route1 and return tokens to router for the second trade + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + CONTRACT_BALANCE, + minAmountOut1WETH, + encodePathExactInput(route1), + SOURCE_ROUTER, + ]) + // 3) trade route2 and return tokens to bob + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + CONTRACT_BALANCE, + minAmountOut1USDC.add(minAmountOut2USDC), + encodePathExactInput(route2), + SOURCE_ROUTER, + ]) + + const { usdcBalanceBefore, usdcBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(minAmountOut1USDC.add(minAmountOut2USDC)) + }) + + it('ERC20 --> ERC20 split V2 and V3, one hop', async () => { + const tokens = [DAI.address, WETH.address] + const v2AmountIn: BigNumber = expandTo18DecimalsBN(2) + const v3AmountIn: BigNumber = expandTo18DecimalsBN(3) + const minAmountOut = expandTo18DecimalsBN(0.0005) + + // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ADDRESS_THIS, v2AmountIn, 0, tokens, SOURCE_MSG_SENDER]) + // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + v3AmountIn, + 0, + encodePathExactInput(tokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, minAmountOut]) + + const { wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1Out: wethOutV2 } = v2SwapEventArgs! + let { amount1: wethOutV3 } = v3SwapEventArgs! + + // expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(v2AmountIn.add(v3AmountIn)) // TODO: with permit2 can check from alice's balance + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethOutV2.sub(wethOutV3)) + }) + + it('ETH --> ERC20 split V2 and V3, one hop', async () => { + const tokens = [WETH.address, USDC.address] + const v2AmountIn: BigNumber = expandTo18DecimalsBN(2) + const v3AmountIn: BigNumber = expandTo18DecimalsBN(3) + const value = v2AmountIn.add(v3AmountIn) + + planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, value]) + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ADDRESS_THIS, v2AmountIn, 0, tokens, SOURCE_ROUTER]) + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + v3AmountIn, + 0, + encodePathExactInput(tokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + planner.addCommand(CommandType.SWEEP, [USDC.address, MSG_SENDER, 0.0005 * 10 ** 6]) + + const { usdcBalanceBefore, usdcBalanceAfter, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract, + value + ) + const { amount0Out: usdcOutV2 } = v2SwapEventArgs! + let { amount0: usdcOutV3 } = v3SwapEventArgs! + usdcOutV3 = usdcOutV3.mul(-1) + expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.eq(usdcOutV2.add(usdcOutV3)) + }) + + it('ERC20 --> ETH split V2 and V3, one hop', async () => { + const tokens = [DAI.address, WETH.address] + const v2AmountIn: BigNumber = expandTo18DecimalsBN(20) + const v3AmountIn: BigNumber = expandTo18DecimalsBN(30) + + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ADDRESS_THIS, v2AmountIn, 0, tokens, SOURCE_MSG_SENDER]) + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + v3AmountIn, + 0, + encodePathExactInput(tokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, expandTo18DecimalsBN(0.0005)]) + + const { ethBalanceBefore, ethBalanceAfter, gasSpent, v2SwapEventArgs, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1Out: wethOutV2 } = v2SwapEventArgs! + let { amount1: wethOutV3 } = v3SwapEventArgs! + wethOutV3 = wethOutV3.mul(-1) + + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethOutV2.add(wethOutV3).sub(gasSpent)) + }) + + it('ERC20 --> ETH split V2 and V3, exactOut, one hop', async () => { + const tokens = [DAI.address, WETH.address] + const v2AmountOut: BigNumber = expandTo18DecimalsBN(0.5) + const v3AmountOut: BigNumber = expandTo18DecimalsBN(1) + const path = encodePathExactOutput(tokens) + const maxAmountIn = expandTo18DecimalsBN(4000) + const fullAmountOut = v2AmountOut.add(v3AmountOut) + + planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ + ADDRESS_THIS, + v2AmountOut, + maxAmountIn, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [ + ADDRESS_THIS, + v3AmountOut, + maxAmountIn, + path, + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, fullAmountOut]) + + const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + // TODO: permit2 test alice doesn't send more than maxAmountIn DAI + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(fullAmountOut.sub(gasSpent)) + }) + + describe('Batch reverts', () => { + let subplan: RoutePlanner + const planOneTokens = [DAI.address, WETH.address] + const planTwoTokens = [USDC.address, WETH.address] + const planOneV2AmountIn: BigNumber = expandTo18DecimalsBN(2) + const planOneV3AmountIn: BigNumber = expandTo18DecimalsBN(3) + const planTwoV3AmountIn = expandTo6DecimalsBN(5) + + beforeEach(async () => { + subplan = new RoutePlanner() + }) + + it('2 sub-plans, neither fails', async () => { + // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. + const planOneWethMinOut = expandTo18DecimalsBN(0.0005) + + // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade + subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV2AmountIn, + 0, + planOneTokens, + SOURCE_MSG_SENDER, + ]) + // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV3AmountIn, + 0, + encodePathExactInput(planOneTokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) + + // add the subplan to the main planner + planner.addSubPlan(subplan) + subplan = new RoutePlanner() + + // second split route sub-plan. USDC->WETH, 1 route on V3 + const wethMinAmountOut2 = expandTo18DecimalsBN(0.0005) + + // Add the trade to the sub-plan + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + planTwoV3AmountIn, + wethMinAmountOut2, + encodePathExactInput(planTwoTokens), + SOURCE_MSG_SENDER, + ]) + + // add the second subplan to the main planner + planner.addSubPlan(subplan) + + const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(planOneV2AmountIn.add(planOneV3AmountIn)) + expect(usdcBalanceBefore.sub(usdcBalanceAfter)).to.eq(planTwoV3AmountIn) + }) + + it('2 sub-plans, the first fails', async () => { + // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. + // FAIL: large weth amount out to cause a failure + const planOneWethMinOut = expandTo18DecimalsBN(1) + + // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade + subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV2AmountIn, + 0, + planOneTokens, + SOURCE_MSG_SENDER, + ]) + // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV3AmountIn, + 0, + encodePathExactInput(planOneTokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) + + // add the subplan to the main planner + planner.addSubPlan(subplan) + subplan = new RoutePlanner() + + // second split route sub-plan. USDC->WETH, 1 route on V3 + const wethMinAmountOut2 = expandTo18DecimalsBN(0.0005) + + // Add the trade to the sub-plan + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + planTwoV3AmountIn, + wethMinAmountOut2, + encodePathExactInput(planTwoTokens), + SOURCE_MSG_SENDER, + ]) + + // add the second subplan to the main planner + planner.addSubPlan(subplan) + + const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + // dai balance should be unchanged as the weth sweep failed + expect(daiBalanceBefore).to.eq(daiBalanceAfter) + + // usdc is the second trade so the balance has changed + expect(usdcBalanceBefore.sub(usdcBalanceAfter)).to.eq(planTwoV3AmountIn) + }) + + it('2 sub-plans, both fail but the transaction succeeds', async () => { + // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. + // FAIL: large amount out to cause the swap to revert + const planOneWethMinOut = expandTo18DecimalsBN(1) + + // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade + subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV2AmountIn, + 0, + planOneTokens, + SOURCE_MSG_SENDER, + ]) + // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV3AmountIn, + 0, + encodePathExactInput(planOneTokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) + + // add the subplan to the main planner + planner.addSubPlan(subplan) + subplan = new RoutePlanner() + + // second split route sub-plan. USDC->WETH, 1 route on V3 + // FAIL: large amount out to cause the swap to revert + const wethMinAmountOut2 = expandTo18DecimalsBN(1) + + // Add the trade to the sub-plan + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + planTwoV3AmountIn, + wethMinAmountOut2, + encodePathExactInput(planTwoTokens), + SOURCE_MSG_SENDER, + ]) + + // add the second subplan to the main planner + planner.addSubPlan(subplan) + + const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + // dai and usdc balances both unchanged because both trades failed + expect(daiBalanceBefore).to.eq(daiBalanceAfter) + expect(usdcBalanceBefore).to.eq(usdcBalanceAfter) + }) + + it('2 sub-plans, second sub plan fails', async () => { + // first split route sub-plan. DAI->WETH, 2 routes on V2 and V3. + const planOneWethMinOut = expandTo18DecimalsBN(0.0005) + + // V2 trades DAI for USDC, sending the tokens back to the router for v3 trade + subplan.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV2AmountIn, + 0, + planOneTokens, + SOURCE_MSG_SENDER, + ]) + // V3 trades USDC for WETH, trading the whole balance, with a recipient of Alice + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + ADDRESS_THIS, + planOneV3AmountIn, + 0, + encodePathExactInput(planOneTokens), + SOURCE_MSG_SENDER, + ]) + // aggregate slippage check + subplan.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, planOneWethMinOut]) + + // add the subplan to the main planner + planner.addSubPlan(subplan) + subplan = new RoutePlanner() + + // second split route sub-plan. USDC->WETH, 1 route on V3 + // FAIL: large amount out to cause the swap to revert + const wethMinAmountOut2 = expandTo18DecimalsBN(1) + + // Add the trade to the sub-plan + subplan.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + planTwoV3AmountIn, + wethMinAmountOut2, + encodePathExactInput(planTwoTokens), + SOURCE_MSG_SENDER, + ]) + + // add the second subplan to the main planner + planner.addSubPlan(subplan) + + const { usdcBalanceBefore, usdcBalanceAfter, daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + // dai balance has changed as this trade should succeed + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(planOneV2AmountIn.add(planOneV3AmountIn)) + + // usdc is unchanged as the second trade should have failed + expect(usdcBalanceBefore).to.eq(usdcBalanceAfter) + }) + }) + }) +}) diff --git a/test/integration-tests/UniswapV2.test.ts b/test/integration-tests/UniswapV2.test.ts new file mode 100644 index 00000000..7ecd34c3 --- /dev/null +++ b/test/integration-tests/UniswapV2.test.ts @@ -0,0 +1,409 @@ +import type { Contract } from '@ethersproject/contracts' +import { Pair } from '@uniswap/v2-sdk' +import { expect } from './shared/expect' +import { BigNumber } from 'ethers' +import { IPermit2, UniversalRouter } from '../../typechain' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { resetFork, WETH, DAI, USDC, PERMIT2 } from './shared/mainnetForkHelpers' +import { + ADDRESS_THIS, + ALICE_ADDRESS, + DEADLINE, + ETH_ADDRESS, + MAX_UINT, + MAX_UINT160, + MSG_SENDER, + ONE_PERCENT_BIPS, + SOURCE_MSG_SENDER, + SOURCE_ROUTER, +} from './shared/constants' +import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import deployUniversalRouter from './shared/deployUniversalRouter' +import { RoutePlanner, CommandType } from './shared/planner' +import hre from 'hardhat' +import { executeRouter } from './shared/executeRouter' +import { getPermitSignature, PermitSingle } from './shared/protocolHelpers/permit2' +import { ADDRESS_ZERO } from '@uniswap/v3-sdk' +const { ethers } = hre + +describe('Uniswap V2 Tests:', () => { + let alice: SignerWithAddress + let bob: SignerWithAddress + let router: UniversalRouter + let permit2: IPermit2 + let daiContract: Contract + let wethContract: Contract + let usdcContract: Contract + let planner: RoutePlanner + + const amountIn: BigNumber = expandTo18DecimalsBN(5) + + beforeEach(async () => { + await resetFork() + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ALICE_ADDRESS], + }) + alice = await ethers.getSigner(ALICE_ADDRESS) + bob = (await ethers.getSigners())[1] + daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) + wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) + usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) + permit2 = PERMIT2.connect(bob) as IPermit2 + router = (await deployUniversalRouter()) as UniversalRouter + planner = new RoutePlanner() + + // alice gives bob some tokens + await daiContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100000)) + await wethContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100)) + await usdcContract.connect(alice).transfer(bob.address, expandTo6DecimalsBN(100000)) + + // Bob max-approves the permit2 contract to access his DAI and WETH + await daiContract.connect(bob).approve(permit2.address, MAX_UINT) + await wethContract.connect(bob).approve(permit2.address, MAX_UINT) + await usdcContract.connect(bob).approve(permit2.address, MAX_UINT) + + // for these tests Bob gives the router max approval on permit2 + await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) + await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) + }) + + describe('Trade on Uniswap with Permit2, giving approval every time', () => { + let permit: PermitSingle + + beforeEach(async () => { + // cancel the permit on DAI + await permit2.approve(DAI.address, ADDRESS_ZERO, 0, 0) + }) + + it('V2 exactIn, permiting the exact amount', async () => { + const amountInDAI = expandTo18DecimalsBN(100) + const minAmountOutWETH = expandTo18DecimalsBN(0.02) + + // second bob signs a permit to allow the router to access his DAI + permit = { + details: { + token: DAI.address, + amount: amountInDAI, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + spender: router.address, + sigDeadline: DEADLINE, + } + const sig = await getPermitSignature(permit, bob, permit2) + + // 1) permit the router to access funds, 2) withdraw the funds into the pair, 3) trade + planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + amountInDAI, + minAmountOutWETH, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOutWETH) + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) + }) + + it('V2 exactOut, permiting the maxAmountIn', async () => { + const maxAmountInDAI = expandTo18DecimalsBN(4000) + const amountOutWETH = expandTo18DecimalsBN(1) + + // second bob signs a permit to allow the router to access his DAI + permit = { + details: { + token: DAI.address, + amount: maxAmountInDAI, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + spender: router.address, + sigDeadline: DEADLINE, + } + const sig = await getPermitSignature(permit, bob, permit2) + + // 1) permit the router to access funds, 2) trade - the transfer happens within the trade for exactOut + planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) + planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ + MSG_SENDER, + amountOutWETH, + maxAmountInDAI, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.eq(amountOutWETH) + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.lte(maxAmountInDAI) + }) + + it('V2 exactIn, swapping more than max_uint160 should revert', async () => { + const max_uint = BigNumber.from(MAX_UINT160) + const minAmountOutWETH = expandTo18DecimalsBN(0.03) + + // second bob signs a permit to allow the router to access his DAI + permit = { + details: { + token: DAI.address, + amount: max_uint, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + spender: router.address, + sigDeadline: DEADLINE, + } + const sig = await getPermitSignature(permit, bob, permit2) + + // 1) permit the router to access funds, 2) withdraw the funds into the pair, 3) trade + planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + BigNumber.from(MAX_UINT160).add(1), + minAmountOutWETH, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + + const testCustomErrors = await (await ethers.getContractFactory('TestCustomErrors')).deploy() + await expect( + executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + ).to.be.revertedWithCustomError(testCustomErrors, 'UnsafeCast') + }) + }) + + describe('ERC20 --> ERC20', () => { + it('completes a V2 exactIn swap', async () => { + const minAmountOut = expandTo18DecimalsBN(0.0001) + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + amountIn, + minAmountOut, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gt(minAmountOut) + }) + + it('completes a V2 exactOut swap', async () => { + const amountOut = expandTo18DecimalsBN(1) + planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ + MSG_SENDER, + amountOut, + expandTo18DecimalsBN(10000), + [WETH.address, DAI.address], + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, 0]) + const { daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gt(amountOut) + }) + + it('exactIn trade, where an output fee is taken', async () => { + // back to the router so someone can take a fee + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + amountIn, + 1, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.PAY_PORTION, [WETH.address, alice.address, ONE_PERCENT_BIPS]) + planner.addCommand(CommandType.SWEEP, [WETH.address, MSG_SENDER, 1]) + + const { commands, inputs } = planner + const wethBalanceBeforeAlice = await wethContract.balanceOf(alice.address) + const wethBalanceBeforeBob = await wethContract.balanceOf(bob.address) + + await router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) + + const wethBalanceAfterAlice = await wethContract.balanceOf(alice.address) + const wethBalanceAfterBob = await wethContract.balanceOf(bob.address) + + const aliceFee = wethBalanceAfterAlice.sub(wethBalanceBeforeAlice) + const bobEarnings = wethBalanceAfterBob.sub(wethBalanceBeforeBob) + + expect(bobEarnings).to.be.gt(0) + expect(aliceFee).to.be.gt(0) + + // total fee is 1% of bob's output + expect(aliceFee.add(bobEarnings).mul(ONE_PERCENT_BIPS).div(10_000)).to.eq(aliceFee) + }) + + it('completes a V2 exactIn swap with longer path', async () => { + const minAmountOut = expandTo18DecimalsBN(0.0001) + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + amountIn, + minAmountOut, + [DAI.address, USDC.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gt(minAmountOut) + }) + }) + + describe('ERC20 --> ETH', () => { + it('completes a V2 exactIn swap', async () => { + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + ADDRESS_THIS, + amountIn, + 1, + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) + + const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1Out: wethTraded } = v2SwapEventArgs! + + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethTraded.sub(gasSpent)) + }) + + it('completes a V2 exactOut swap', async () => { + const amountOut = expandTo18DecimalsBN(1) + planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ + ADDRESS_THIS, + amountOut, + expandTo18DecimalsBN(10000), + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, amountOut]) + planner.addCommand(CommandType.SWEEP, [DAI.address, MSG_SENDER, 0]) + + const { gasSpent, ethBalanceBefore, ethBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1Out: wethTraded } = v2SwapEventArgs! + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(amountOut.sub(gasSpent)) + expect(wethTraded).to.eq(amountOut) + }) + + it('completes a V2 exactOut swap, with ETH fee', async () => { + const amountOut = expandTo18DecimalsBN(1) + const totalPortion = amountOut.mul(ONE_PERCENT_BIPS).div(10000) + const actualAmountOut = amountOut.sub(totalPortion) + + planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ + ADDRESS_THIS, + amountOut, + expandTo18DecimalsBN(10000), + [DAI.address, WETH.address], + SOURCE_MSG_SENDER, + ]) + planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, amountOut]) + planner.addCommand(CommandType.PAY_PORTION, [ETH_ADDRESS, alice.address, ONE_PERCENT_BIPS]) + planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, MSG_SENDER, 0]) + + const { commands, inputs } = planner + + await expect( + router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) + ).to.changeEtherBalances([alice, bob], [totalPortion, actualAmountOut]) + }) + }) + + describe('ETH --> ERC20', () => { + it('completes a V2 exactIn swap', async () => { + const minAmountOut = expandTo18DecimalsBN(0.001) + const pairAddress = Pair.getAddress(DAI, WETH) + planner.addCommand(CommandType.WRAP_ETH, [pairAddress, amountIn]) + // amountIn of 0 because the weth is already in the pair + planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [ + MSG_SENDER, + 0, + minAmountOut, + [WETH.address, DAI.address], + SOURCE_MSG_SENDER, + ]) + + const { daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract, + amountIn + ) + const { amount0Out: daiTraded } = v2SwapEventArgs! + + expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gt(minAmountOut) + expect(daiBalanceAfter.sub(daiBalanceBefore)).to.equal(daiTraded) + }) + + it('completes a V2 exactOut swap', async () => { + const amountOut = expandTo18DecimalsBN(100) + const value = expandTo18DecimalsBN(1) + + planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, value]) + planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [ + MSG_SENDER, + amountOut, + expandTo18DecimalsBN(1), + [WETH.address, DAI.address], + SOURCE_ROUTER, + ]) + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) + + const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, v2SwapEventArgs, gasSpent } = + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, value) + const { amount0Out: daiTraded, amount1In: wethTraded } = v2SwapEventArgs! + expect(daiBalanceAfter.sub(daiBalanceBefore)).gt(amountOut) // rounding + expect(daiBalanceAfter.sub(daiBalanceBefore)).eq(daiTraded) + expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(wethTraded.add(gasSpent)) + }) + }) +}) diff --git a/test/integration-tests/UniswapV3.test.ts b/test/integration-tests/UniswapV3.test.ts new file mode 100644 index 00000000..bf9939be --- /dev/null +++ b/test/integration-tests/UniswapV3.test.ts @@ -0,0 +1,344 @@ +import type { Contract } from '@ethersproject/contracts' +import { expect } from './shared/expect' +import { BigNumber, BigNumberish } from 'ethers' +import { IPermit2, UniversalRouter } from '../../typechain' +import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' +import { resetFork, WETH, DAI, USDC, PERMIT2 } from './shared/mainnetForkHelpers' +import { + ADDRESS_THIS, + ALICE_ADDRESS, + DEADLINE, + MAX_UINT, + MAX_UINT160, + MSG_SENDER, + SOURCE_MSG_SENDER, + SOURCE_ROUTER, +} from './shared/constants' +import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import deployUniversalRouter from './shared/deployUniversalRouter' +import { RoutePlanner, CommandType } from './shared/planner' +import hre from 'hardhat' +import { encodePathExactInput, encodePathExactOutput } from './shared/swapRouter02Helpers' +import { executeRouter } from './shared/executeRouter' +import { getPermitSignature, PermitSingle } from './shared/protocolHelpers/permit2' +import { ADDRESS_ZERO } from '@uniswap/v3-sdk' +const { ethers } = hre + +describe('Uniswap V3 Tests:', () => { + let alice: SignerWithAddress + let bob: SignerWithAddress + let router: UniversalRouter + let permit2: IPermit2 + let daiContract: Contract + let wethContract: Contract + let usdcContract: Contract + let planner: RoutePlanner + + const amountIn: BigNumber = expandTo18DecimalsBN(500) + const amountInMax: BigNumber = expandTo18DecimalsBN(5000) + const amountOut: BigNumber = expandTo18DecimalsBN(1) + + beforeEach(async () => { + await resetFork() + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ALICE_ADDRESS], + }) + alice = await ethers.getSigner(ALICE_ADDRESS) + bob = (await ethers.getSigners())[1] + daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob) + wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob) + usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob) + permit2 = PERMIT2.connect(bob) as IPermit2 + router = (await deployUniversalRouter()) as UniversalRouter + planner = new RoutePlanner() + + // alice gives bob some tokens + await daiContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100000)) + await wethContract.connect(alice).transfer(bob.address, expandTo18DecimalsBN(100)) + await usdcContract.connect(alice).transfer(bob.address, expandTo6DecimalsBN(100000)) + + // Bob max-approves the permit2 contract to access his DAI and WETH + await daiContract.connect(bob).approve(permit2.address, MAX_UINT) + await wethContract.connect(bob).approve(permit2.address, MAX_UINT) + await usdcContract.connect(bob).approve(permit2.address, MAX_UINT) + + // for these tests Bob gives the router max approval on permit2 + await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE) + await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE) + }) + + const addV3ExactInTrades = ( + planner: RoutePlanner, + numTrades: BigNumberish, + amountOutMin: BigNumberish, + recipient?: string, + tokens: string[] = [DAI.address, WETH.address], + tokenSource: boolean = SOURCE_MSG_SENDER + ) => { + const path = encodePathExactInput(tokens) + for (let i = 0; i < numTrades; i++) { + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + recipient ?? MSG_SENDER, + amountIn, + amountOutMin, + path, + tokenSource, + ]) + } + } + + describe('Trade on Uniswap with Permit2, giving approval every time', () => { + let permit: PermitSingle + + beforeEach(async () => { + // cancel the permit on DAI + await permit2.approve(DAI.address, ADDRESS_ZERO, 0, 0) + }) + + it('V3 exactIn, permiting the exact amount', async () => { + const amountInDAI = expandTo18DecimalsBN(100) + const minAmountOutWETH = expandTo18DecimalsBN(0.02) + + // first bob approves permit2 to access his DAI + await daiContract.connect(bob).approve(permit2.address, MAX_UINT) + + // second bob signs a permit to allow the router to access his DAI + permit = { + details: { + token: DAI.address, + amount: amountInDAI, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + spender: router.address, + sigDeadline: DEADLINE, + } + const sig = await getPermitSignature(permit, bob, permit2) + + const path = encodePathExactInput([DAI.address, WETH.address]) + + // 1) permit the router to access funds, 2) trade, which takes the funds directly from permit2 + planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) + planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [ + MSG_SENDER, + amountInDAI, + minAmountOutWETH, + path, + SOURCE_MSG_SENDER, + ]) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(minAmountOutWETH) + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) + }) + + it('V3 exactOut, permiting the exact amount', async () => { + const maxAmountInDAI = expandTo18DecimalsBN(4000) + const amountOutWETH = expandTo18DecimalsBN(1) + + // first bob approves permit2 to access his DAI + await daiContract.connect(bob).approve(permit2.address, MAX_UINT) + + // second bob signs a permit to allow the router to access his DAI + permit = { + details: { + token: DAI.address, + amount: maxAmountInDAI, + expiration: 0, // expiration of 0 is block.timestamp + nonce: 0, // this is his first trade + }, + spender: router.address, + sigDeadline: DEADLINE, + } + const sig = await getPermitSignature(permit, bob, permit2) + + const path = encodePathExactOutput([DAI.address, WETH.address]) + + // 1) permit the router to access funds, 2) trade, which takes the funds directly from permit2 + planner.addCommand(CommandType.PERMIT2_PERMIT, [permit, sig]) + planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [ + MSG_SENDER, + amountOutWETH, + maxAmountInDAI, + path, + SOURCE_MSG_SENDER, + ]) + const { wethBalanceBefore, wethBalanceAfter, daiBalanceAfter, daiBalanceBefore } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.eq(amountOutWETH) + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.lte(maxAmountInDAI) + }) + }) + + describe('ERC20 --> ERC20', () => { + it('completes a V3 exactIn swap', async () => { + const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) + addV3ExactInTrades(planner, 1, amountOutMin) + + const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1: wethTraded } = v3SwapEventArgs! + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(amountOutMin) + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1)) + }) + + it('completes a V3 exactIn swap with longer path', async () => { + const amountOutMin: number = 3 * 10 ** 6 + addV3ExactInTrades( + planner, + 1, + amountOutMin, + MSG_SENDER, + [DAI.address, WETH.address, USDC.address], + SOURCE_MSG_SENDER + ) + + const { + daiBalanceBefore, + daiBalanceAfter, + wethBalanceBefore, + wethBalanceAfter, + usdcBalanceBefore, + usdcBalanceAfter, + } = await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract) + + expect(daiBalanceBefore.sub(amountIn)).to.eq(daiBalanceAfter) + expect(wethBalanceAfter).to.eq(wethBalanceBefore) + expect(usdcBalanceAfter.sub(usdcBalanceBefore)).to.be.gte(amountOutMin) + }) + + it('completes a V3 exactOut swap', async () => { + // trade DAI in for WETH out + const tokens = [DAI.address, WETH.address] + const path = encodePathExactOutput(tokens) + + planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) + + const { wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount0: daiTraded } = v3SwapEventArgs! + expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(amountOut) + expect(daiTraded).to.be.lt(amountInMax) + }) + + it('completes a V3 exactOut swap with longer path', async () => { + // trade DAI in for WETH out + const tokens = [DAI.address, USDC.address, WETH.address] + const path = encodePathExactOutput(tokens) + + planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) + const { commands, inputs } = planner + + const balanceWethBefore = await wethContract.balanceOf(bob.address) + await router.connect(bob)['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE) + const balanceWethAfter = await wethContract.balanceOf(bob.address) + expect(balanceWethAfter.sub(balanceWethBefore)).to.eq(amountOut) + }) + }) + + describe('ERC20 --> ETH', () => { + it('completes a V3 exactIn swap', async () => { + const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) + addV3ExactInTrades(planner, 1, amountOutMin, ADDRESS_THIS) + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) + + const { ethBalanceBefore, ethBalanceAfter, v3SwapEventArgs, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + const { amount1: wethTraded } = v3SwapEventArgs! + + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.be.gte(amountOutMin.sub(gasSpent)) + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(wethTraded.mul(-1).sub(gasSpent)) + }) + + it('completes a V3 exactOut swap', async () => { + // trade DAI in for WETH out + const tokens = [DAI.address, WETH.address] + const path = encodePathExactOutput(tokens) + + planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [ADDRESS_THIS, amountOut, amountInMax, path, SOURCE_MSG_SENDER]) + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, amountOut]) + + const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + expect(ethBalanceAfter.sub(ethBalanceBefore)).to.eq(amountOut.sub(gasSpent)) + }) + }) + + describe('ETH --> ERC20', () => { + it('completes a V3 exactIn swap', async () => { + const tokens = [WETH.address, DAI.address] + const amountOutMin: BigNumber = expandTo18DecimalsBN(0.0005) + + planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, amountIn]) + addV3ExactInTrades(planner, 1, amountOutMin, MSG_SENDER, tokens, SOURCE_ROUTER) + + const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract, + amountIn + ) + + expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(amountIn.add(gasSpent)) + expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.gte(amountOutMin) + }) + + it('completes a V3 exactOut swap', async () => { + const tokens = [WETH.address, DAI.address] + const path = encodePathExactOutput(tokens) + + planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, amountInMax]) + planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [MSG_SENDER, amountOut, amountInMax, path, SOURCE_ROUTER]) + planner.addCommand(CommandType.UNWRAP_WETH, [MSG_SENDER, 0]) + + const { ethBalanceBefore, ethBalanceAfter, daiBalanceBefore, daiBalanceAfter, gasSpent, v3SwapEventArgs } = + await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, amountInMax) + const { amount0: daiTraded, amount1: wethTraded } = v3SwapEventArgs! + + expect(daiBalanceBefore.sub(daiBalanceAfter)).to.eq(daiTraded) + expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(wethTraded.add(gasSpent)) + }) + }) +}) From 0d81bcaa816248b2143ab1704f8a6e2dbdbe8dce Mon Sep 17 00:00:00 2001 From: Alice Henshaw Date: Fri, 2 Aug 2024 23:27:05 +0100 Subject: [PATCH 13/13] PR comment --- .../modules/uniswap/v2/UniswapV2Library.sol | 2 +- .../CheckOwnership.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 14 ++-- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- .../V3ToV4Migration.gas.test.ts.snap | 8 +- 7 files changed, 63 insertions(+), 63 deletions(-) diff --git a/contracts/modules/uniswap/v2/UniswapV2Library.sol b/contracts/modules/uniswap/v2/UniswapV2Library.sol index 936d7cd1..3b89e482 100644 --- a/contracts/modules/uniswap/v2/UniswapV2Library.sol +++ b/contracts/modules/uniswap/v2/UniswapV2Library.sol @@ -122,7 +122,7 @@ library UniswapV2Library { /// @param path The path of the multi-hop trade /// @return amount The input amount of the input token /// @return pair The first pair in the trade - function getAmountInMultihop(address factory, bytes32 initCodeHash, uint256 amountOut, address[] memory path) + function getAmountInMultihop(address factory, bytes32 initCodeHash, uint256 amountOut, address[] calldata path) internal view returns (uint256 amount, address pair) diff --git a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap index d5544ff7..591d189f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Check Ownership Gas gas: balance check ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 37692, + "gasUsed": 37639, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 88ffee07..1d211df7 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -3,48 +3,48 @@ exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 36960, + "gasUsed": 36947, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 65584, + "gasUsed": 65568, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 35985, + "gasUsed": 35972, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 31553, + "gasUsed": 31543, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 44577, + "gasUsed": 44505, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 50799, + "gasUsed": 50721, } `; exports[`Payments Gas Tests Individual Command Tests gas: WRAP_ETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 53402, + "gasUsed": 53344, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 34ae5b44..b673ae91 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269286, + "gasUsed": 269232, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245013, + "gasUsed": 244951, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 245013, + "gasUsed": 244951, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 269286, + "gasUsed": 269232, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189429, + "gasUsed": 189398, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 177013, + "gasUsed": 176982, } `; 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": 297045, + "gasUsed": 297014, } `; 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": 307720, + "gasUsed": 307686, } `; 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": 308988, + "gasUsed": 308921, } `; 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": 304268, + "gasUsed": 304240, } `; 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": 176751, + "gasUsed": 176717, } `; 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": 176526, + "gasUsed": 176492, } `; 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": 194568, + "gasUsed": 194045, } `; 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": 184675, + "gasUsed": 184582, } `; 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": 191629, + "gasUsed": 191533, } `; @@ -143,98 +143,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": 126459, + "gasUsed": 126434, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 106820, + "gasUsed": 106801, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 241426, + "gasUsed": 241407, } `; 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": 241168, + "gasUsed": 241149, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 174169, + "gasUsed": 174149, } `; 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": 174169, + "gasUsed": 174149, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 106890, + "gasUsed": 106435, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 246791, + "gasUsed": 246271, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 176914, + "gasUsed": 176427, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 123046, + "gasUsed": 122965, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 128240, + "gasUsed": 127720, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 136159, + "gasUsed": 135636, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 106657, + "gasUsed": 106576, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 125498, + "gasUsed": 124919, } `; @@ -283,69 +283,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": 105500, + "gasUsed": 105478, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 253891, + "gasUsed": 253863, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 177082, + "gasUsed": 177056, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 115030, + "gasUsed": 115014, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 251049, + "gasUsed": 251027, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 174744, + "gasUsed": 174725, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 121774, + "gasUsed": 121690, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 131376, + "gasUsed": 131298, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 215343, + "gasUsed": 215259, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 126519, + "gasUsed": 126379, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index f74fed7b..e140cb8a 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `13879`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `13736`; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index bf913bb9..23508d23 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1104105`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1103919`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138531`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1138221`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1124979`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3081021`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3080584`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3234634`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3233639`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3195011`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102882`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4102191`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4306992`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4305557`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4282374`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508723`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `508612`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `509041`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `508930`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `500008`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299651`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `299563`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299587`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `299499`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `270033`; diff --git a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap index e53be141..f23a584c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/V3ToV4Migration.gas.test.ts.snap @@ -3,27 +3,27 @@ exports[`V3 to V4 Migration Gas Tests Migrator burn gas: erc721permit + decreaseLiquidity + collect + burn 1`] = ` Object { "calldataByteLength": 1092, - "gasUsed": 255774, + "gasUsed": 255766, } `; exports[`V3 to V4 Migration Gas Tests Migrator collect gas: erc721permit + decreaseLiquidity + collect 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 223466, + "gasUsed": 223456, } `; exports[`V3 to V4 Migration Gas Tests Migrator decrease liquidity gas: erc721permit + decreaseLiquidity 1`] = ` Object { "calldataByteLength": 740, - "gasUsed": 201904, + "gasUsed": 201894, } `; exports[`V3 to V4 Migration Gas Tests Migrator erc721permit gas: erc721permit 1`] = ` Object { "calldataByteLength": 484, - "gasUsed": 65914, + "gasUsed": 65904, } `;