diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..44358150a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.eslintrc* +artifacts +cache +constants +coverage +lib/murky +lib/openzeppelin-contracts \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..4a461452c --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,57 @@ +module.exports = { + env: { + browser: false, + es2021: true, + mocha: true, + node: true, + }, + plugins: ["@typescript-eslint", "import"], + extends: [ + "standard", + "plugin:prettier/recommended", + "eslint:recommended", + "plugin:import/recommended", + "plugin:import/typescript", + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 12, + project: "./tsconfig.json", + }, + rules: { + "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/prefer-nullish-coalescing": "error", + camelcase: [ + "error", + { allow: ["Conduit__factory", "EIP1271Wallet__factory"] }, + ], + "import/order": [ + "error", + { + alphabetize: { + order: "asc", + }, + groups: [ + "object", + ["builtin", "external"], + "parent", + "sibling", + "index", + "type", + ], + "newlines-between": "always", + }, + ], + "object-shorthand": "error", + "prefer-const": "error", + "sort-imports": ["error", { ignoreDeclarationSort: true }], + }, + overrides: [ + { + files: ["test/**/*.spec.ts"], + rules: { + "no-unused-expressions": "off", + }, + }, + ], +}; diff --git a/.gas_reports/374278b75e1acc08d85bfacef494b7cfe42fa2d9.json b/.gas_reports/374278b75e1acc08d85bfacef494b7cfe42fa2d9.json new file mode 100644 index 000000000..d09e9b45c --- /dev/null +++ b/.gas_reports/374278b75e1acc08d85bfacef494b7cfe42fa2d9.json @@ -0,0 +1,502 @@ +{ + "commitHash": "374278b75e1acc08d85bfacef494b7cfe42fa2d9", + "contractReports": { + "Conduit": { + "name": "Conduit", + "methods": [ + { + "method": "execute", + "min": 77435, + "max": 2373103, + "avg": 483652, + "calls": 6 + }, + { + "method": "executeBatch1155", + "min": null, + "max": null, + "avg": 97245, + "calls": 1 + }, + { + "method": "executeWithBatch1155", + "min": 97717, + "max": 361418, + "avg": 228764, + "calls": 4 + }, + { + "method": "updateChannel", + "min": null, + "max": null, + "avg": 45802, + "calls": 1 + } + ], + "bytecodeSize": 3071, + "deployedBytecodeSize": 3030 + }, + "ConduitController": { + "name": "ConduitController", + "methods": [ + { + "method": "acceptOwnership", + "min": null, + "max": null, + "avg": 32944, + "calls": 1 + }, + { + "method": "cancelOwnershipTransfer", + "min": null, + "max": null, + "avg": 27966, + "calls": 1 + }, + { + "method": "createConduit", + "min": 712802, + "max": 712970, + "avg": 712930, + "calls": 51 + }, + { + "method": "transferOwnership", + "min": null, + "max": null, + "avg": 50329, + "calls": 2 + }, + { + "method": "updateChannel", + "min": 34454, + "max": 121098, + "avg": 117184, + "calls": 69 + } + ], + "bytecodeSize": 12007, + "deployedBytecodeSize": 8660 + }, + "ConduitControllerMock": { + "name": "ConduitControllerMock", + "methods": [ + { + "method": "createConduit", + "min": 226092, + "max": 231533, + "avg": 229598, + "calls": 6 + } + ], + "bytecodeSize": 10541, + "deployedBytecodeSize": 7340 + }, + "EIP1271Wallet": { + "name": "EIP1271Wallet", + "methods": [ + { + "method": "approveNFT", + "min": null, + "max": null, + "avg": 49674, + "calls": 14 + }, + { + "method": "registerDigest", + "min": 22239, + "max": 44151, + "avg": 36847, + "calls": 3 + }, + { + "method": "revertWithMessage", + "min": null, + "max": null, + "avg": 21677, + "calls": 1 + }, + { + "method": "setValid", + "min": 21699, + "max": 43611, + "avg": 32655, + "calls": 2 + } + ], + "bytecodeSize": 2834, + "deployedBytecodeSize": 2656 + }, + "ExcessReturnDataRecipient": { + "name": "ExcessReturnDataRecipient", + "methods": [ + { + "method": "setRevertDataSize", + "min": null, + "max": null, + "avg": 43441, + "calls": 2 + } + ], + "bytecodeSize": 1907, + "deployedBytecodeSize": 1879 + }, + "PausableZone": { + "name": "PausableZone", + "methods": [ + { + "method": "cancelOrders", + "min": null, + "max": null, + "avg": 65315, + "calls": 1 + } + ], + "bytecodeSize": 5556, + "deployedBytecodeSize": 5450 + }, + "PausableZoneController": { + "name": "PausableZoneController", + "methods": [ + { + "method": "acceptOwnership", + "min": null, + "max": null, + "avg": 28942, + "calls": 1 + }, + { + "method": "assignOperator", + "min": null, + "max": null, + "avg": 50892, + "calls": 1 + }, + { + "method": "assignPauser", + "min": null, + "max": null, + "avg": 47183, + "calls": 1 + }, + { + "method": "cancelOrders", + "min": null, + "max": null, + "avg": 73894, + "calls": 1 + }, + { + "method": "cancelOwnershipTransfer", + "min": null, + "max": null, + "avg": 24578, + "calls": 1 + }, + { + "method": "createZone", + "min": null, + "max": null, + "avg": 1154314, + "calls": 31 + }, + { + "method": "executeMatchAdvancedOrders", + "min": null, + "max": null, + "avg": 288298, + "calls": 2 + }, + { + "method": "executeMatchOrders", + "min": null, + "max": null, + "avg": 281862, + "calls": 2 + }, + { + "method": "pause", + "min": 32851, + "max": 35006, + "avg": 33569, + "calls": 3 + }, + { + "method": "transferOwnership", + "min": null, + "max": null, + "avg": 47199, + "calls": 2 + } + ], + "bytecodeSize": 17744, + "deployedBytecodeSize": 11975 + }, + "Reenterer": { + "name": "Reenterer", + "methods": [ + { + "method": "prepare", + "min": 69404, + "max": 2350940, + "avg": 1171827, + "calls": 20 + } + ], + "bytecodeSize": 2459, + "deployedBytecodeSize": 2431 + }, + "Seaport": { + "name": "Seaport", + "methods": [ + { + "method": "cancel", + "min": 41250, + "max": 58422, + "avg": 54035, + "calls": 16 + }, + { + "method": "fulfillAdvancedOrder", + "min": 96312, + "max": 225169, + "avg": 159958, + "calls": 188 + }, + { + "method": "fulfillAvailableAdvancedOrders", + "min": 149979, + "max": 339949, + "avg": 207233, + "calls": 29 + }, + { + "method": "fulfillAvailableOrders", + "min": 165345, + "max": 216505, + "avg": 201935, + "calls": 21 + }, + { + "method": "fulfillBasicOrder", + "min": 90639, + "max": 1621627, + "avg": 598708, + "calls": 187 + }, + { + "method": "fulfillBasicOrder_efficient_6GL6yc", + "min": 90261, + "max": 111444, + "avg": 100853, + "calls": 6 + }, + { + "method": "fulfillOrder", + "min": 119409, + "max": 225080, + "avg": 177756, + "calls": 105 + }, + { + "method": "incrementCounter", + "min": null, + "max": null, + "avg": 47054, + "calls": 6 + }, + { + "method": "matchAdvancedOrders", + "min": 180326, + "max": 299883, + "avg": 249683, + "calls": 77 + }, + { + "method": "matchOrders", + "min": 158265, + "max": 349010, + "avg": 265283, + "calls": 151 + }, + { + "method": "validate", + "min": 53201, + "max": 83886, + "avg": 73544, + "calls": 29 + } + ], + "bytecodeSize": 26712, + "deployedBytecodeSize": 23553 + }, + "TestContractOfferer": { + "name": "TestContractOfferer", + "methods": [ + { + "method": "activate", + "min": 201531, + "max": 246674, + "avg": 205514, + "calls": 33 + }, + { + "method": "activateWithCriteria", + "min": null, + "max": null, + "avg": 201834, + "calls": 1 + }, + { + "method": "extendAvailable", + "min": null, + "max": null, + "avg": 50704, + "calls": 1 + }, + { + "method": "extendRequired", + "min": null, + "max": null, + "avg": 45780, + "calls": 1 + } + ], + "bytecodeSize": 8462, + "deployedBytecodeSize": 8265 + }, + "TestContractOffererNativeToken": { + "name": "TestContractOffererNativeToken", + "methods": [ + { + "method": "activate", + "min": null, + "max": null, + "avg": 139181, + "calls": 1 + } + ], + "bytecodeSize": 6940, + "deployedBytecodeSize": 6764 + }, + "TestERC1155": { + "name": "TestERC1155", + "methods": [ + { + "method": "mint", + "min": 47235, + "max": 49915, + "avg": 49443, + "calls": 245 + }, + { + "method": "setApprovalForAll", + "min": 26102, + "max": 46002, + "avg": 45654, + "calls": 458 + } + ], + "bytecodeSize": 4173, + "deployedBytecodeSize": 4145 + }, + "TestERC20": { + "name": "TestERC20", + "methods": [ + { + "method": "approve", + "min": 28881, + "max": 46245, + "avg": 45749, + "calls": 292 + }, + { + "method": "blockTransfer", + "min": 21978, + "max": 43890, + "avg": 32934, + "calls": 4 + }, + { + "method": "mint", + "min": 33994, + "max": 68458, + "avg": 67348, + "calls": 141 + }, + { + "method": "setNoReturnData", + "min": 21926, + "max": 43838, + "avg": 32882, + "calls": 2 + } + ], + "bytecodeSize": 5807, + "deployedBytecodeSize": 4636 + }, + "TestERC721": { + "name": "TestERC721", + "methods": [ + { + "method": "mint", + "min": 51492, + "max": 68784, + "avg": 66201, + "calls": 307 + }, + { + "method": "setApprovalForAll", + "min": 26195, + "max": 46095, + "avg": 45560, + "calls": 522 + } + ], + "bytecodeSize": 5238, + "deployedBytecodeSize": 4451 + }, + "TestInvalidContractOfferer": { + "name": "TestInvalidContractOfferer", + "methods": [ + { + "method": "activate", + "min": null, + "max": null, + "avg": 201567, + "calls": 2 + } + ], + "bytecodeSize": 7954, + "deployedBytecodeSize": 7764 + }, + "TestInvalidContractOffererRatifyOrder": { + "name": "TestInvalidContractOffererRatifyOrder", + "methods": [ + { + "method": "activate", + "min": null, + "max": null, + "avg": 201558, + "calls": 1 + } + ], + "bytecodeSize": 7957, + "deployedBytecodeSize": 7760 + }, + "TransferHelper": { + "name": "TransferHelper", + "methods": [ + { + "method": "bulkTransfer", + "min": 77935, + "max": 1546854, + "avg": 673107, + "calls": 3 + } + ], + "bytecodeSize": 4140, + "deployedBytecodeSize": 3865 + } + } +} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2665adfb8..535cce3eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,11 @@ -name: Consideration Test CI +name: Seaport Test CI -on: [push, pull_request] +on: + push: + branches: [main, 1.*, 2.*] + tags: ["*"] + pull_request: + types: [opened, reopened, synchronize] concurrency: group: ${{github.workflow}}-${{github.ref}} @@ -13,7 +18,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [16.15.1] steps: - uses: actions/checkout@v3 @@ -21,6 +26,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "yarn" - run: yarn install - run: yarn build @@ -30,7 +36,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [16.15.1] steps: - uses: actions/checkout@v3 @@ -38,6 +44,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "yarn" - run: yarn install - run: yarn lint:check @@ -47,7 +54,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [16.15.1] steps: - uses: actions/checkout@v3 @@ -55,6 +62,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "yarn" - run: yarn install - run: yarn build - run: yarn test @@ -65,7 +73,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [16.15.1] env: REFERENCE: true @@ -76,6 +84,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "yarn" - run: yarn install - run: yarn build - run: yarn build:ref @@ -97,7 +106,7 @@ jobs: - name: Install forge dependencies run: forge install - - name: Precompile reference using 0.8.7 and via-ir=false + - name: Precompile reference using 0.8.13 and via-ir=false run: FOUNDRY_PROFILE=reference forge build - name: Run tests @@ -119,22 +128,65 @@ jobs: - name: Install forge dependencies run: forge install - - name: Precompile reference using 0.8.7 and via-ir=false + - name: Precompile reference using 0.8.13 and via-ir=false run: FOUNDRY_PROFILE=reference forge build - - name: Precompile optimized using 0.8.14 and via-ir=true + - name: Precompile optimized using 0.8.17 and via-ir=true run: FOUNDRY_PROFILE=optimized forge build - name: Run tests run: FOUNDRY_PROFILE=test forge test -vvv + forge-offerers: + name: Run Contract Offerer Forge Tests (via_ir = false; fuzz_runs = 1000) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Install forge dependencies + run: forge install + + - name: Run tests + run: FOUNDRY_PROFILE=offerers forge test -vvv + + forge-coverage: + name: Run Forge Coverage report on tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Install forge dependencies + run: forge install + + - name: Run coverage with lcov output + run: SEAPORT_COVERAGE=true forge coverage --report lcov + + - uses: codecov/codecov-action@v3 + with: + files: ./lcov.info + flags: foundry + coverage: name: Run Coverage Tests runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x] + node-version: [16.15.1] steps: - uses: actions/checkout@v3 @@ -142,13 +194,14 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "yarn" - run: yarn install - run: yarn build - run: yarn coverage - - uses: VeryGoodOpenSource/very_good_coverage@v1 + - uses: codecov/codecov-action@v3 with: - path: "./coverage/lcov.info" - min_coverage: 100 + files: ./coverage/lcov.info + flags: production reference-coverage: name: Run Reference Coverage Tests @@ -156,7 +209,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [16.15.1] env: REFERENCE: true @@ -167,11 +220,12 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "yarn" - run: yarn install - run: yarn build - run: yarn build:ref - run: yarn coverage:ref - - uses: VeryGoodOpenSource/very_good_coverage@v1 + - uses: codecov/codecov-action@v3 with: - path: "./coverage/lcov.info" - min_coverage: 100 + files: ./coverage/lcov.info + flags: reference diff --git a/.gitignore b/.gitignore index 32960bcc7..0db299cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,20 @@ out reference-out optimized-out reference-working +offerers-out + .DS_Store # VScode .vscode -__pycache__ \ No newline at end of file +__pycache__ + +Seaport.yul + +# coverage +html +lcov.info + +test/utils/eip712/gen.sol +.gas_reports/*.md \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 9dfb90988..4fcf68b13 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,15 @@ [submodule "lib/forge-std"] path = lib/forge-std - url = https://github.com/brockelmore/forge-std + url = https://github.com/foundry-rs/forge-std [submodule "lib/ds-test"] path = lib/ds-test url = https://github.com/dapphub/ds-test -[submodule "lib/solmate"] - path = lib/solmate - url = https://github.com/rari-capital/solmate [submodule "lib/murky"] path = lib/murky url = https://github.com/dmfxyz/murky +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "lib/solmate"] + path = lib/solmate + url = https://github.com/transmissions11/solmate diff --git a/.nvmrc b/.nvmrc index 9c846f9a2..b8c9fdcbe 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.11.0 \ No newline at end of file +16.15.1 \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index d35df378a..ab450da55 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,13 @@ node_modules artifacts cache coverage* -gasReporterOutput.json \ No newline at end of file +gasReporterOutput.json + +typechain-types/ + +lib/ds-test +lib/forge-std +lib/murky +lib/openzeppelin-contracts +lib/solmate + diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..bf473f15f --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,13 @@ +module.exports = { + overrides: [ + { + files: "*.sol", + options: { + tabWidth: 4, + printWidth: 80, + bracketSpacing: true, + compiler: "0.8.17", + }, + }, + ], +}; diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e80ee82e0..7b9394abd 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -5,13 +5,14 @@ Contributor | ENS 0age | `0age.eth` d1ll0n | `d1ll0n.eth` transmissions11 | `t11s.eth` +James Wenzel | `emo.eth` Kartik | `slokh.eth` LeFevre | `lefevre.eth` Joseph Schiarizzi | `CupOJoseph.eth` Aspyn Palatnick | `stuckinaboot.eth` -James Wenzel | `emo.eth` Stephan Min | `stephanm.eth` Ryan Ghods | `ralxz.eth` +Daniel Viau | `snotrocket.eth` 0xPatissier | pcaversaccio | David Eiber | @@ -33,7 +34,7 @@ Rajiv Patel-O'Connor | `rajivpoc.eth` tserg | `tserg.eth` cygaar | `cygaar.eth` Meta0xNull | `meta0xnull.eth` -sach1r0 | +sach1r0 | `sach1r0.eth` gpersoon | `gpersoon.eth` Matt Solomon | `msolomon.eth` twojoy0 | @@ -69,3 +70,5 @@ rfart(rfa) | shuklaayush | `shuklaayush.eth` Riley Holterhus | big-tech-sux | +naps62 | `naps62.eth` +Dravee | `dravee.eth` diff --git a/LICENSE b/LICENSE index 8d4b8ca3a..d29f6cf51 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2022 Ozone Networks, Inc. +Copyright 2023 Ozone Networks, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index 611eca339..039476b01 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,32 @@ ![Seaport](img/Seaport-banner.png) +[![Version][version-badge]][version-link] +[![Test CI][ci-badge]][ci-link] +[![Code Coverage][coverage-badge]][coverage-link] +[![License][license-badge]][license-link] +[![Docs][docs-badge]][docs-link] +[![Discussions][discussions-badge]][discussions-link] +[![JS Library][js-library-badge]][js-library-link] +[![Discord][discord-badge]][discord-link] + # Seaport Seaport is a new marketplace protocol for safely and efficiently buying and selling NFTs. ## Table of Contents -- [Background](#background) -- [Deployments](#deployments) -- [Diagram](#diagram) -- [Install](#install) -- [Usage](#usage) -- [Audits](#audits) -- [Contributing](#contributing) -- [License](#license) +- [Seaport](#seaport) + - [Table of Contents](#table-of-contents) + - [Background](#background) + - [Deployments](#deployments) + - [Diagram](#diagram) + - [Install](#install) + - [Usage](#usage) + - [Foundry Tests](#foundry-tests) + - [Linting](#linting) + - [Audits](#audits) + - [Contributing](#contributing) + - [License](#license) ## Background @@ -23,19 +36,295 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts ## Deployments -Seaport 1.1 deployment addresses: +### Canonical Cross-chain Deployment Addresses + + + + + + + + + + + + + + + + + + +
ContractCanonical Cross-chain Deployment Address
Seaport 1.10x00000000006c3852cbEf3e08E8dF289169EdE581
Seaport 1.20x00000000000006c7676171937C444f6BDe3D6282
ConduitController0x00000000F9490004C11Cef243f5400493c00Ad63
+ +### Deployments By EVM Chain + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NetworkSeaport 1.1Seaport 1.2ConduitController
Ethereum + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +[0x00000000000006c7676171937C444f6BDe3D6282](https://etherscan.io/address/0x00000000000006c7676171937C444f6BDe3D6282#code) + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Rinkeby + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://rinkeby.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://rinkeby.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Goerli + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://goerli.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Kovan + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://kovan.etherscan/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://kovan.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Sepolia + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://sepolia.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://sepolia.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Polygon + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://polygonscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://polygonscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Mumbai + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://mumbai.polygonscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://mumbai.polygonscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Klaytn + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://scope.klaytn.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://scope.klaytn.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Baobab + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://baobab.scope.klaytn.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://baobab.scope.klaytn.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Optimism + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://optimistic.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://optimistic.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Optimistic Goerli + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli-optimism.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + -| Network | Address | -| ---------------- | ------------------------------------------ | -| Ethereum Mainnet | [0x00000000006c3852cbEf3e08E8dF289169EdE581](https://etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) | -| Rinkeby | [0x00000000006c3852cbEf3e08E8dF289169EdE581](https://rinkeby.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) | +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://goerli-optimism.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) -Conduit Controller deployment addresses: +
Arbitrum -| Network | Address | -| ---------------- | ------------------------------------------ | -| Ethereum Mainnet | [0x00000000F9490004C11Cef243f5400493c00Ad63](https://etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) | -| Rinkeby | [0x00000000F9490004C11Cef243f5400493c00Ad63](https://rinkeby.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) | +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://arbiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Arbitrum Goerli + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli.arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://goerli.arbiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Arbitrum Nova + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://nova.arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://nova.arbiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Avalanche C-Chain + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://snowtrace.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://snowtrace.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Avalanche Fuji + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://testnet.snowtrace.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://testnet.snowtrace.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
Gnosis Chain + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://gnosisscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://gnossiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
BSC + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://bscscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://bscscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
BSC Testnet + +[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://testnet.bscscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code) + + + +- + + + +[0x00000000F9490004C11Cef243f5400493c00Ad63](https://testnet.bscscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code) + +
+ +To be deployed on other EVM chains, such as: + +- Skale +- Celo +- Fantom +- RSK + +To deploy to a new EVM chain, follow the [steps outlined here](docs/Deployment.md). ## Diagram @@ -101,6 +390,12 @@ yarn test:ref yarn coverage:ref ``` +To open the generated Hardhat coverage report locally after running `yarn coverage` or `yarn coverage:ref`: + +```bash +open coverage/index.html +``` + To profile gas usage: ```bash @@ -111,60 +406,36 @@ yarn profile Seaport also includes a suite of fuzzing tests written in solidity with Foundry. -To install Foundry (assuming a Linux or macOS system): +To run tests with full traces and debugging with source, create an `.env` file with the following line: ```bash -curl -L https://foundry.paradigm.xyz | bash +FOUNDRY_PROFILE=debug ``` -This will download foundryup. To start Foundry, run: - -```bash -foundryup -``` - -To install dependencies: - -``` -forge install -``` +You may then run tests with `forge test`, optionally specifying a level of verbosity (anywhere from one to five `v`'s, eg, `-vvv`) -To precompile contracts: +This will compile tests and contracts without `via-ir` enabled, which is must faster, but will not exactly match the deployed bytecode. -The optimized contracts are compiled using the IR pipeline, which can take a long time to compile. By default, the differential test suite deploys precompiled versions of both the optimized and reference contracts. Precompilation can be done by specifying specific Foundry profiles. +To run tests against the actual bytecode intended to be deployed on networks, you will need to pre-compile the contracts, and remove the `FOUNDRY_PROFILE` variable from your `.env` file. **Note** that informative error traces may not be available, and the Forge debugger will not show the accompanying source code. ```bash FOUNDRY_PROFILE=optimized forge build FOUNDRY_PROFILE=reference forge build ``` -There are three Foundry profiles for running the test suites, which bypass the IR pipeline to speed up compilation. To run tests, run any of the following: +To run Forge coverage tests and open the generated coverage report locally: ```bash -FOUNDRY_PROFILE=test forge test # with 5000 fuzz runs -FOUNDRY_PROFILE=lite forge test # with 1000 fuzz runs -FOUNDRY_PROFILE=local-ffi forge test # compiles and deploys ReferenceConsideration normally, with 1000 fuzz runs +brew install lcov +SEAPORT_COVERAGE=true forge coverage --report summary --report lcov && genhtml lcov.info -o html --branch +open html/index.htmlg ``` -You may wish to include a `.env` file that `export`s a specific profile when developing locally. +**Note** that Forge does not yet ignore specific filepaths when running coverage tests. -**Note** that stack+debug traces will not be available for precompiled contracts. To facilitate local development, specifying `FOUNDRY_PROFILE=local-ffi` will compile and deploy the reference implementation normally, allowing for stack+debug traces. +For information on Foundry, including installation and testing, see the [Foundry Book](https://book.getfoundry.sh/). -**Note** the `local-ffi` profile uses Forge's `ffi` flag. `ffi` can potentially be unsafe, as it allows Forge to execute arbitrary code. Use with caution, and always ensure you trust the code in this repository, especially when working on third-party forks. - - -The following modifiers are also available: - -- Level 2 (-vv): Logs emitted during tests are also displayed. -- Level 3 (-vvv): Stack traces for failing tests are also displayed. -- Level 4 (-vvvv): Stack traces for all tests are displayed, and setup traces for failing tests are displayed. -- Level 5 (-vvvvv): Stack traces and setup traces are always displayed. - -```bash -FOUNDRY_PROFILE=test forge test -vv -``` - -For more information on foundry testing and use, see [Foundry Book installation instructions](https://book.getfoundry.sh/getting-started/installation.html). +### Linting To run lint checks: @@ -192,16 +463,32 @@ When making a pull request, ensure that: - All tests pass. - Code coverage remains at 100% (coverage tests must currently be written in hardhat). - All new code adheres to the style guide: - - All lint checks pass. - - Code is thoroughly commented with natspec where relevant. + - All lint checks pass. + - Code is thoroughly commented with natspec where relevant. - If making a change to the contracts: - - Gas snapshots are provided and demonstrate an improvement (or an acceptable deficit given other improvements). - - Reference contracts are modified correspondingly if relevant. - - New tests (ideally via foundry) are included for all new features or code paths. + - Gas snapshots are provided and demonstrate an improvement (or an acceptable deficit given other improvements). + - Reference contracts are modified correspondingly if relevant. + - New tests (ideally via foundry) are included for all new features or code paths. - If making a modification to third-party dependencies, `yarn audit` passes. - A descriptive summary of the PR has been provided. ## License -[MIT](LICENSE) Copyright 2022 Ozone Networks, Inc. - +[MIT](LICENSE) Copyright 2023 Ozone Networks, Inc. + +[version-badge]: https://img.shields.io/github/package-json/v/ProjectOpenSea/seaport +[version-link]: https://github.com/ProjectOpenSea/seaport/releases +[ci-badge]: https://github.com/ProjectOpenSea/seaport/actions/workflows/test.yml/badge.svg +[ci-link]: https://github.com/ProjectOpenSea/seaport/actions/workflows/test.yml +[coverage-badge]: https://codecov.io/gh/ProjectOpenSea/seaport/branch/main/graph/badge.svg +[coverage-link]: https://codecov.io/gh/ProjectOpenSea/seaport +[license-badge]: https://img.shields.io/github/license/ProjectOpenSea/seaport +[license-link]: https://github.com/ProjectOpenSea/seaport/blob/main/LICENSE +[docs-badge]: https://img.shields.io/badge/Seaport-documentation-informational +[docs-link]: https://github.com/ProjectOpenSea/seaport/tree/main/docs +[discussions-badge]: https://img.shields.io/badge/Seaport-discussions-blueviolet +[discussions-link]: https://github.com/ProjectOpenSea/seaport/discussions +[js-library-badge]: https://img.shields.io/badge/Seaport.js-library-red +[js-library-link]: https://github.com/ProjectOpenSea/seaport-js +[discord-badge]: https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue +[discord-link]: https://discord.gg/ADXcTXpqry diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..4d89c9b08 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,40 @@ +coverage: + range: 100..100 + round: down + precision: 2 + status: + project: + default: + target: auto + threshold: 0% + base: auto + if_ci_failed: error + +flag_management: + default_rules: # the rules that will be followed for any flag added, generally + statuses: + - type: project + target: 100% + threshold: 0% + - type: patch + target: 100% + threshold: 0% + individual_flags: # exceptions to the default rules above, stated flag by flag + - name: foundry + statuses: + - type: project + target: 100% + threshold: 0% + - type: patch + target: 100% + threshold: 0% + +# Ignore folders that forge coverage is reporting on +ignore: + - offerers + - script + - test + - contracts/test + - contracts/helpers + - contracts/zones + - contracts/lib/ConsiderationStructs.sol diff --git a/config/.solcover-reference.js b/config/.solcover-reference.js index 71ef3285d..a8e0bab93 100644 --- a/config/.solcover-reference.js +++ b/config/.solcover-reference.js @@ -5,12 +5,14 @@ module.exports = { "conduit/lib/ConduitEnums.sol", "conduit/lib/ConduitStructs.sol", "Consideration.sol", + "helpers/PointerLibraries.sol", "interfaces/AbridgedProxyInterfaces.sol", "interfaces/AbridgedTokenInterfaces.sol", "interfaces/ConduitControllerInterface.sol", "interfaces/ConduitInterface.sol", "interfaces/ConsiderationEventsAndErrors.sol", "interfaces/ConsiderationInterface.sol", + "interfaces/ContractOffererInterface.sol", "interfaces/EIP1271Interface.sol", "interfaces/ZoneInterface.sol", "lib/ConsiderationBase.sol", @@ -25,8 +27,28 @@ module.exports = { "test/ExcessReturnDataRecipient.sol", "test/Reenterer.sol", "test/TestERC1155.sol", + "test/TestERC1155Revert.sol", "test/TestERC20.sol", + "test/TestERC20NotOk.sol", "test/TestERC721.sol", + "test/TestERC721Revert.sol", + "test/TestContractOfferer.sol", + "test/TestContractOffererNativeToken.sol", + "test/TestInvalidContractOfferer.sol", + "test/TestInvalidContractOffererRatifyOrder.sol", + "test/TestBadContractOfferer.sol", "test/TestZone.sol", + "test/TestPostExecution.sol", + "test/TestERC20Panic.sol", + "test/TestERC20Revert.sol", + "test/InvalidERC721Recipient.sol", + "test/ERC721ReceiverMock.sol", + "test/ConduitControllerMock.sol", + "test/ConduitMock.sol", + "test/ConduitMockErrors.sol", + "test/ConduitMockInvalidMagic.sol", + "test/ConduitMockRevertBytes.sol", + "test/ConduitMockRevertNoReason.sol", + "../reference/shim/Shim.sol", ], }; diff --git a/config/.solcover.js b/config/.solcover.js index 9cba82828..2a8e2f2fd 100644 --- a/config/.solcover.js +++ b/config/.solcover.js @@ -2,34 +2,52 @@ module.exports = { skipFiles: [ "conduit/lib/ConduitEnums.sol", "conduit/lib/ConduitStructs.sol", + "helpers/PointerLibraries.sol", "interfaces/AbridgedProxyInterfaces.sol", "interfaces/AbridgedTokenInterfaces.sol", "interfaces/ConduitControllerInterface.sol", "interfaces/ConduitInterface.sol", "interfaces/ConsiderationEventsAndErrors.sol", "interfaces/ConsiderationInterface.sol", + "interfaces/ContractOffererInterface.sol", "interfaces/EIP1271Interface.sol", "interfaces/SeaportInterface.sol", "interfaces/ZoneInterface.sol", "lib/ConsiderationConstants.sol", "lib/ConsiderationEnums.sol", "lib/ConsiderationStructs.sol", - "reference/ReferenceConsideration.sol", - "reference/conduit/ReferenceConduit.sol", - "reference/conduit/ReferenceConduitController.sol", - "reference/lib/ReferenceConsiderationBase.sol", - "reference/lib/ReferenceConsiderationInternal.sol", - "reference/lib/ReferenceConsiderationInternalView.sol", - "reference/lib/ReferenceConsiderationPure.sol", - "reference/lib/ReferenceTokenTransferrer.sol", "test/EIP1271Wallet.sol", "test/ExcessReturnDataRecipient.sol", "test/ERC1155BatchRecipient.sol", "test/Reenterer.sol", "test/TestERC1155.sol", + "test/TestERC1155Revert.sol", "test/TestERC20.sol", + "test/TestERC20NotOk.sol", "test/TestERC721.sol", + "test/TestERC721Revert.sol", + "test/TestContractOfferer.sol", + "test/TestContractOffererNativeToken.sol", + "test/TestInvalidContractOfferer.sol", + "test/TestInvalidContractOffererRatifyOrder.sol", + "test/TestBadContractOfferer.sol", + "test/TestPostExecution.sol", "test/TestZone.sol", + "test/TestERC20Panic.sol", + "test/TestERC20Revert.sol", + "test/InvalidERC721Recipient.sol", + "test/ERC721ReceiverMock.sol", + "test/ConduitControllerMock.sol", + "test/ConduitMock.sol", + "test/ConduitMockErrors.sol", + "test/ConduitMockInvalidMagic.sol", + "test/ConduitMockRevertBytes.sol", + "test/ConduitMockRevertNoReason.sol", + "zones/PausableZone.sol", + "zones/PausableZoneController.sol", + "zones/interfaces/PausableZoneControllerInterface.sol", + "zones/interfaces/PausableZoneEventsAndErrors.sol", + "zones/interfaces/PausableZoneInterface.sol", ], configureYulOptimizer: true, solcOptimizerDetails: { diff --git a/contracts/Seaport.sol b/contracts/Seaport.sol index ad036f657..c682a49c1 100644 --- a/contracts/Seaport.sol +++ b/contracts/Seaport.sol @@ -1,21 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { Consideration } from "./lib/Consideration.sol"; /** * @title Seaport - * @custom:version 1.1 + * @custom:version 1.2 * @author 0age (0age.eth) * @custom:coauthor d1ll0n (d1ll0n.eth) * @custom:coauthor transmissions11 (t11s.eth) + * @custom:coauthor James Wenzel (emo.eth) * @custom:contributor Kartik (slokh.eth) * @custom:contributor LeFevre (lefevre.eth) * @custom:contributor Joseph Schiarizzi (CupOJoseph.eth) * @custom:contributor Aspyn Palatnick (stuckinaboot.eth) - * @custom:contributor James Wenzel (emo.eth) * @custom:contributor Stephan Min (stephanm.eth) * @custom:contributor Ryan Ghods (ralxz.eth) + * @custom:contributor Daniel Viau (snotrocket.eth) * @custom:contributor hack3r-0m (hack3r-0m.eth) * @custom:contributor Diego Estevez (antidiego.eth) * @custom:contributor Chomtana (chomtana.eth) @@ -56,6 +57,7 @@ import { Consideration } from "./lib/Consideration.sol"; * @custom:contributor blockdev (blockd3v.eth) * @custom:contributor fiveoutofnine (fiveoutofnine.eth) * @custom:contributor shuklaayush (shuklaayush.eth) + * @custom:contributor dravee (dravee.eth) * @custom:contributor 0xPatissier * @custom:contributor pcaversaccio * @custom:contributor David Eiber @@ -73,13 +75,12 @@ import { Consideration } from "./lib/Consideration.sol"; * @custom:contributor rfart(rfa) * @custom:contributor Riley Holterhus * @custom:contributor big-tech-sux - * @notice Seaport is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. It - * minimizes external calls to the greatest extent possible and provides - * lightweight methods for common routes as well as more flexible - * methods for composing advanced orders or groups of orders. Each order - * contains an arbitrary number of items that may be spent (the "offer") - * along with an arbitrary number of items that must be received back by - * the indicated recipients (the "consideration"). + * @notice Seaport is a generalized native token/ERC20/ERC721/ERC1155 + * marketplace with lightweight methods for common routes as well as + * more flexible methods for composing advanced orders or groups of + * orders. Each order contains an arbitrary number of items that may be + * spent (the "offer") along with an arbitrary number of items that must + * be received back by the indicated recipients (the "consideration"). */ contract Seaport is Consideration { /** diff --git a/contracts/conduit/Conduit.sol b/contracts/conduit/Conduit.sol index 411f77bc4..aa2ecae99 100644 --- a/contracts/conduit/Conduit.sol +++ b/contracts/conduit/Conduit.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; @@ -7,13 +7,20 @@ import { ConduitItemType } from "./lib/ConduitEnums.sol"; import { TokenTransferrer } from "../lib/TokenTransferrer.sol"; -// prettier-ignore import { - ConduitTransfer, - ConduitBatch1155Transfer + ConduitBatch1155Transfer, + ConduitTransfer } from "./lib/ConduitStructs.sol"; -import "./lib/ConduitConstants.sol"; +import { + ChannelClosed_channel_ptr, + ChannelClosed_error_length, + ChannelClosed_error_ptr, + ChannelClosed_error_signature, + ChannelKey_channel_ptr, + ChannelKey_length, + ChannelKey_slot_ptr +} from "./lib/ConduitConstants.sol"; /** * @title Conduit @@ -59,7 +66,11 @@ contract Conduit is ConduitInterface, TokenTransferrer { // Next, set the caller as the argument. mstore(ChannelClosed_channel_ptr, caller()) - // Finally, revert, returning full custom error with argument. + // Finally, revert, returning full custom error with argument + // data in memory. + // revert(abi.encodeWithSignature( + // "ChannelClosed(address)", caller() + // )) revert(ChannelClosed_error_ptr, ChannelClosed_error_length) } } @@ -90,12 +101,9 @@ contract Conduit is ConduitInterface, TokenTransferrer { * @return magicValue A magic value indicating that the transfers were * performed successfully. */ - function execute(ConduitTransfer[] calldata transfers) - external - override - onlyOpenChannel - returns (bytes4 magicValue) - { + function execute( + ConduitTransfer[] calldata transfers + ) external override onlyOpenChannel returns (bytes4 magicValue) { // Retrieve the total number of transfers and place on the stack. uint256 totalStandardTransfers = transfers.length; @@ -224,7 +232,7 @@ contract Conduit is ConduitInterface, TokenTransferrer { } else if (item.itemType == ConduitItemType.ERC721) { // Ensure that exactly one 721 item is being transferred. if (item.amount != 1) { - revert InvalidERC721TransferAmount(); + revert InvalidERC721TransferAmount(item.amount); } // Transfer ERC721 token. diff --git a/contracts/conduit/ConduitController.sol b/contracts/conduit/ConduitController.sol index d3e0b711a..1b15fe5c0 100644 --- a/contracts/conduit/ConduitController.sol +++ b/contracts/conduit/ConduitController.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { - ConduitControllerInterface + ConduitControllerInterface } from "../interfaces/ConduitControllerInterface.sol"; import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; @@ -54,11 +53,10 @@ contract ConduitController is ConduitControllerInterface { * * @return conduit The address of the newly deployed conduit. */ - function createConduit(bytes32 conduitKey, address initialOwner) - external - override - returns (address conduit) - { + function createConduit( + bytes32 conduitKey, + address initialOwner + ) external override returns (address conduit) { // Ensure that an initial owner has been supplied. if (initialOwner == address(0)) { revert InvalidInitialOwner(); @@ -201,10 +199,10 @@ contract ConduitController is ConduitControllerInterface { * @param conduit The conduit for which to initiate ownership transfer. * @param newPotentialOwner The new potential owner of the conduit. */ - function transferOwnership(address conduit, address newPotentialOwner) - external - override - { + function transferOwnership( + address conduit, + address newPotentialOwner + ) external override { // Ensure the caller is the current owner of the conduit in question. _assertCallerIsConduitOwner(conduit); @@ -288,12 +286,9 @@ contract ConduitController is ConduitControllerInterface { * * @return owner The owner of the supplied conduit. */ - function ownerOf(address conduit) - external - view - override - returns (address owner) - { + function ownerOf( + address conduit + ) external view override returns (address owner) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -310,12 +305,9 @@ contract ConduitController is ConduitControllerInterface { * * @return conduitKey The conduit key used to deploy the supplied conduit. */ - function getKey(address conduit) - external - view - override - returns (bytes32 conduitKey) - { + function getKey( + address conduit + ) external view override returns (bytes32 conduitKey) { // Attempt to retrieve a conduit key for the conduit in question. conduitKey = _conduits[conduit].key; @@ -336,12 +328,9 @@ contract ConduitController is ConduitControllerInterface { * @return exists A boolean indicating whether the derived conduit has been * deployed or not. */ - function getConduit(bytes32 conduitKey) - external - view - override - returns (address conduit, bool exists) - { + function getConduit( + bytes32 conduitKey + ) external view override returns (address conduit, bool exists) { // Derive address from deployer, conduit key and creation code hash. conduit = address( uint160( @@ -372,12 +361,9 @@ contract ConduitController is ConduitControllerInterface { * * @return potentialOwner The potential owner, if any, for the conduit. */ - function getPotentialOwner(address conduit) - external - view - override - returns (address potentialOwner) - { + function getPotentialOwner( + address conduit + ) external view override returns (address potentialOwner) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -394,12 +380,10 @@ contract ConduitController is ConduitControllerInterface { * * @return isOpen The status of the channel on the given conduit. */ - function getChannelStatus(address conduit, address channel) - external - view - override - returns (bool isOpen) - { + function getChannelStatus( + address conduit, + address channel + ) external view override returns (bool isOpen) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -414,12 +398,9 @@ contract ConduitController is ConduitControllerInterface { * * @return totalChannels The total number of open channels for the conduit. */ - function getTotalChannels(address conduit) - external - view - override - returns (uint256 totalChannels) - { + function getTotalChannels( + address conduit + ) external view override returns (uint256 totalChannels) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -437,12 +418,10 @@ contract ConduitController is ConduitControllerInterface { * * @return channel The open channel, if any, at the specified channel index. */ - function getChannel(address conduit, uint256 channelIndex) - external - view - override - returns (address channel) - { + function getChannel( + address conduit, + uint256 channelIndex + ) external view override returns (address channel) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -467,12 +446,9 @@ contract ConduitController is ConduitControllerInterface { * * @return channels An array of open channels on the given conduit. */ - function getChannels(address conduit) - external - view - override - returns (address[] memory channels) - { + function getChannels( + address conduit + ) external view override returns (address[] memory channels) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); diff --git a/contracts/conduit/lib/ConduitConstants.sol b/contracts/conduit/lib/ConduitConstants.sol index 2979289c4..42197c8e2 100644 --- a/contracts/conduit/lib/ConduitConstants.sol +++ b/contracts/conduit/lib/ConduitConstants.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; // error ChannelClosed(address channel) uint256 constant ChannelClosed_error_signature = ( diff --git a/contracts/conduit/lib/ConduitEnums.sol b/contracts/conduit/lib/ConduitEnums.sol index 3d0243cd4..05a6587f5 100644 --- a/contracts/conduit/lib/ConduitEnums.sol +++ b/contracts/conduit/lib/ConduitEnums.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; enum ConduitItemType { NATIVE, // unused diff --git a/contracts/conduit/lib/ConduitStructs.sol b/contracts/conduit/lib/ConduitStructs.sol index d1b19f0c3..b7fc8322d 100644 --- a/contracts/conduit/lib/ConduitStructs.sol +++ b/contracts/conduit/lib/ConduitStructs.sol @@ -1,8 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; import { ConduitItemType } from "./ConduitEnums.sol"; +/** + * @dev A ConduitTransfer is a struct that contains the information needed for a + * conduit to transfer an item from one address to another. + */ struct ConduitTransfer { ConduitItemType itemType; address token; @@ -12,6 +16,11 @@ struct ConduitTransfer { uint256 amount; } +/** + * @dev A ConduitBatch1155Transfer is a struct that contains the information + * needed for a conduit to transfer a batch of ERC-1155 tokens from one + * address to another. + */ struct ConduitBatch1155Transfer { address token; address from; diff --git a/contracts/helpers/PointerLibraries.sol b/contracts/helpers/PointerLibraries.sol new file mode 100644 index 000000000..7f927b564 --- /dev/null +++ b/contracts/helpers/PointerLibraries.sol @@ -0,0 +1,3095 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +type CalldataPointer is uint256; + +type ReturndataPointer is uint256; + +type MemoryPointer is uint256; + +using CalldataPointerLib for CalldataPointer global; +using MemoryPointerLib for MemoryPointer global; +using ReturndataPointerLib for ReturndataPointer global; + +using CalldataReaders for CalldataPointer global; +using ReturndataReaders for ReturndataPointer global; +using MemoryReaders for MemoryPointer global; +using MemoryWriters for MemoryPointer global; + +CalldataPointer constant CalldataStart = CalldataPointer.wrap(0x04); +MemoryPointer constant FreeMemoryPPtr = MemoryPointer.wrap(0x40); +uint256 constant IdentityPrecompileAddress = 0x4; +uint256 constant OffsetOrLengthMask = 0xffffffff; +uint256 constant _OneWord = 0x20; +uint256 constant _FreeMemoryPointerSlot = 0x40; + +/// @dev Allocates `size` bytes in memory by increasing the free memory pointer +/// and returns the memory pointer to the first byte of the allocated region. +// (Free functions cannot have visibility.) +// solhint-disable-next-line func-visibility +function malloc(uint256 size) pure returns (MemoryPointer mPtr) { + assembly { + mPtr := mload(_FreeMemoryPointerSlot) + mstore(_FreeMemoryPointerSlot, add(mPtr, size)) + } +} + +// (Free functions cannot have visibility.) +// solhint-disable-next-line func-visibility +function getFreeMemoryPointer() pure returns (MemoryPointer mPtr) { + mPtr = FreeMemoryPPtr.readMemoryPointer(); +} + +// (Free functions cannot have visibility.) +// solhint-disable-next-line func-visibility +function setFreeMemoryPointer(MemoryPointer mPtr) pure { + FreeMemoryPPtr.write(mPtr); +} + +library CalldataPointerLib { + function lt( + CalldataPointer a, + CalldataPointer b + ) internal pure returns (bool c) { + assembly { + c := lt(a, b) + } + } + + function gt( + CalldataPointer a, + CalldataPointer b + ) internal pure returns (bool c) { + assembly { + c := gt(a, b) + } + } + + function eq( + CalldataPointer a, + CalldataPointer b + ) internal pure returns (bool c) { + assembly { + c := eq(a, b) + } + } + + /// @dev Resolves an offset stored at `cdPtr + headOffset` to a calldata. + /// pointer `cdPtr` must point to some parent object with a dynamic + /// type's head stored at `cdPtr + headOffset`. + function pptr( + CalldataPointer cdPtr, + uint256 headOffset + ) internal pure returns (CalldataPointer cdPtrChild) { + cdPtrChild = cdPtr.offset( + cdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask + ); + } + + /// @dev Resolves an offset stored at `cdPtr` to a calldata pointer. + /// `cdPtr` must point to some parent object with a dynamic type as its + /// first member, e.g. `struct { bytes data; }` + function pptr( + CalldataPointer cdPtr + ) internal pure returns (CalldataPointer cdPtrChild) { + cdPtrChild = cdPtr.offset(cdPtr.readUint256() & OffsetOrLengthMask); + } + + /// @dev Returns the calldata pointer one word after `cdPtr`. + function next( + CalldataPointer cdPtr + ) internal pure returns (CalldataPointer cdPtrNext) { + assembly { + cdPtrNext := add(cdPtr, _OneWord) + } + } + + /// @dev Returns the calldata pointer `_offset` bytes after `cdPtr`. + function offset( + CalldataPointer cdPtr, + uint256 _offset + ) internal pure returns (CalldataPointer cdPtrNext) { + assembly { + cdPtrNext := add(cdPtr, _offset) + } + } + + /// @dev Copies `size` bytes from calldata starting at `src` to memory at + /// `dst`. + function copy( + CalldataPointer src, + MemoryPointer dst, + uint256 size + ) internal pure { + assembly { + calldatacopy(dst, src, size) + } + } +} + +library ReturndataPointerLib { + function lt( + ReturndataPointer a, + ReturndataPointer b + ) internal pure returns (bool c) { + assembly { + c := lt(a, b) + } + } + + function gt( + ReturndataPointer a, + ReturndataPointer b + ) internal pure returns (bool c) { + assembly { + c := gt(a, b) + } + } + + function eq( + ReturndataPointer a, + ReturndataPointer b + ) internal pure returns (bool c) { + assembly { + c := eq(a, b) + } + } + + /// @dev Resolves an offset stored at `rdPtr + headOffset` to a returndata + /// pointer. `rdPtr` must point to some parent object with a dynamic + /// type's head stored at `rdPtr + headOffset`. + function pptr( + ReturndataPointer rdPtr, + uint256 headOffset + ) internal pure returns (ReturndataPointer rdPtrChild) { + rdPtrChild = rdPtr.offset( + rdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask + ); + } + + /// @dev Resolves an offset stored at `rdPtr` to a returndata pointer. + /// `rdPtr` must point to some parent object with a dynamic type as its + /// first member, e.g. `struct { bytes data; }` + function pptr( + ReturndataPointer rdPtr + ) internal pure returns (ReturndataPointer rdPtrChild) { + rdPtrChild = rdPtr.offset(rdPtr.readUint256() & OffsetOrLengthMask); + } + + /// @dev Returns the returndata pointer one word after `cdPtr`. + function next( + ReturndataPointer rdPtr + ) internal pure returns (ReturndataPointer rdPtrNext) { + assembly { + rdPtrNext := add(rdPtr, _OneWord) + } + } + + /// @dev Returns the returndata pointer `_offset` bytes after `cdPtr`. + function offset( + ReturndataPointer rdPtr, + uint256 _offset + ) internal pure returns (ReturndataPointer rdPtrNext) { + assembly { + rdPtrNext := add(rdPtr, _offset) + } + } + + /// @dev Copies `size` bytes from returndata starting at `src` to memory at + /// `dst`. + function copy( + ReturndataPointer src, + MemoryPointer dst, + uint256 size + ) internal pure { + assembly { + returndatacopy(dst, src, size) + } + } +} + +library MemoryPointerLib { + function copy( + MemoryPointer src, + MemoryPointer dst, + uint256 size + ) internal view { + assembly { + let success := staticcall( + gas(), + IdentityPrecompileAddress, + src, + size, + dst, + size + ) + if or(iszero(returndatasize()), iszero(success)) { + revert(0, 0) + } + } + } + + function lt( + MemoryPointer a, + MemoryPointer b + ) internal pure returns (bool c) { + assembly { + c := lt(a, b) + } + } + + function gt( + MemoryPointer a, + MemoryPointer b + ) internal pure returns (bool c) { + assembly { + c := gt(a, b) + } + } + + function eq( + MemoryPointer a, + MemoryPointer b + ) internal pure returns (bool c) { + assembly { + c := eq(a, b) + } + } + + /// @dev Returns the memory pointer one word after `mPtr`. + function next( + MemoryPointer mPtr + ) internal pure returns (MemoryPointer mPtrNext) { + assembly { + mPtrNext := add(mPtr, _OneWord) + } + } + + /// @dev Returns the memory pointer `_offset` bytes after `mPtr`. + function offset( + MemoryPointer mPtr, + uint256 _offset + ) internal pure returns (MemoryPointer mPtrNext) { + assembly { + mPtrNext := add(mPtr, _offset) + } + } + + /// @dev Resolves a pointer pointer at `mPtr + headOffset` to a memory + /// pointer. `mPtr` must point to some parent object with a dynamic + /// type's pointer stored at `mPtr + headOffset`. + function pptr( + MemoryPointer mPtr, + uint256 headOffset + ) internal pure returns (MemoryPointer mPtrChild) { + mPtrChild = mPtr.offset(headOffset).readMemoryPointer(); + } + + /// @dev Resolves a pointer pointer stored at `mPtr` to a memory pointer. + /// `mPtr` must point to some parent object with a dynamic type as its + /// first member, e.g. `struct { bytes data; }` + function pptr( + MemoryPointer mPtr + ) internal pure returns (MemoryPointer mPtrChild) { + mPtrChild = mPtr.readMemoryPointer(); + } +} + +library CalldataReaders { + /// @dev Reads the value at `cdPtr` and applies a mask to return only the + /// last 4 bytes. + function readMaskedUint256( + CalldataPointer cdPtr + ) internal pure returns (uint256 value) { + value = cdPtr.readUint256() & OffsetOrLengthMask; + } + + /// @dev Reads the bool at `cdPtr` in calldata. + function readBool( + CalldataPointer cdPtr + ) internal pure returns (bool value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the address at `cdPtr` in calldata. + function readAddress( + CalldataPointer cdPtr + ) internal pure returns (address value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes1 at `cdPtr` in calldata. + function readBytes1( + CalldataPointer cdPtr + ) internal pure returns (bytes1 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes2 at `cdPtr` in calldata. + function readBytes2( + CalldataPointer cdPtr + ) internal pure returns (bytes2 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes3 at `cdPtr` in calldata. + function readBytes3( + CalldataPointer cdPtr + ) internal pure returns (bytes3 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes4 at `cdPtr` in calldata. + function readBytes4( + CalldataPointer cdPtr + ) internal pure returns (bytes4 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes5 at `cdPtr` in calldata. + function readBytes5( + CalldataPointer cdPtr + ) internal pure returns (bytes5 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes6 at `cdPtr` in calldata. + function readBytes6( + CalldataPointer cdPtr + ) internal pure returns (bytes6 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes7 at `cdPtr` in calldata. + function readBytes7( + CalldataPointer cdPtr + ) internal pure returns (bytes7 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes8 at `cdPtr` in calldata. + function readBytes8( + CalldataPointer cdPtr + ) internal pure returns (bytes8 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes9 at `cdPtr` in calldata. + function readBytes9( + CalldataPointer cdPtr + ) internal pure returns (bytes9 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes10 at `cdPtr` in calldata. + function readBytes10( + CalldataPointer cdPtr + ) internal pure returns (bytes10 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes11 at `cdPtr` in calldata. + function readBytes11( + CalldataPointer cdPtr + ) internal pure returns (bytes11 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes12 at `cdPtr` in calldata. + function readBytes12( + CalldataPointer cdPtr + ) internal pure returns (bytes12 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes13 at `cdPtr` in calldata. + function readBytes13( + CalldataPointer cdPtr + ) internal pure returns (bytes13 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes14 at `cdPtr` in calldata. + function readBytes14( + CalldataPointer cdPtr + ) internal pure returns (bytes14 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes15 at `cdPtr` in calldata. + function readBytes15( + CalldataPointer cdPtr + ) internal pure returns (bytes15 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes16 at `cdPtr` in calldata. + function readBytes16( + CalldataPointer cdPtr + ) internal pure returns (bytes16 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes17 at `cdPtr` in calldata. + function readBytes17( + CalldataPointer cdPtr + ) internal pure returns (bytes17 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes18 at `cdPtr` in calldata. + function readBytes18( + CalldataPointer cdPtr + ) internal pure returns (bytes18 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes19 at `cdPtr` in calldata. + function readBytes19( + CalldataPointer cdPtr + ) internal pure returns (bytes19 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes20 at `cdPtr` in calldata. + function readBytes20( + CalldataPointer cdPtr + ) internal pure returns (bytes20 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes21 at `cdPtr` in calldata. + function readBytes21( + CalldataPointer cdPtr + ) internal pure returns (bytes21 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes22 at `cdPtr` in calldata. + function readBytes22( + CalldataPointer cdPtr + ) internal pure returns (bytes22 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes23 at `cdPtr` in calldata. + function readBytes23( + CalldataPointer cdPtr + ) internal pure returns (bytes23 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes24 at `cdPtr` in calldata. + function readBytes24( + CalldataPointer cdPtr + ) internal pure returns (bytes24 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes25 at `cdPtr` in calldata. + function readBytes25( + CalldataPointer cdPtr + ) internal pure returns (bytes25 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes26 at `cdPtr` in calldata. + function readBytes26( + CalldataPointer cdPtr + ) internal pure returns (bytes26 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes27 at `cdPtr` in calldata. + function readBytes27( + CalldataPointer cdPtr + ) internal pure returns (bytes27 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes28 at `cdPtr` in calldata. + function readBytes28( + CalldataPointer cdPtr + ) internal pure returns (bytes28 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes29 at `cdPtr` in calldata. + function readBytes29( + CalldataPointer cdPtr + ) internal pure returns (bytes29 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes30 at `cdPtr` in calldata. + function readBytes30( + CalldataPointer cdPtr + ) internal pure returns (bytes30 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes31 at `cdPtr` in calldata. + function readBytes31( + CalldataPointer cdPtr + ) internal pure returns (bytes31 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the bytes32 at `cdPtr` in calldata. + function readBytes32( + CalldataPointer cdPtr + ) internal pure returns (bytes32 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint8 at `cdPtr` in calldata. + function readUint8( + CalldataPointer cdPtr + ) internal pure returns (uint8 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint16 at `cdPtr` in calldata. + function readUint16( + CalldataPointer cdPtr + ) internal pure returns (uint16 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint24 at `cdPtr` in calldata. + function readUint24( + CalldataPointer cdPtr + ) internal pure returns (uint24 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint32 at `cdPtr` in calldata. + function readUint32( + CalldataPointer cdPtr + ) internal pure returns (uint32 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint40 at `cdPtr` in calldata. + function readUint40( + CalldataPointer cdPtr + ) internal pure returns (uint40 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint48 at `cdPtr` in calldata. + function readUint48( + CalldataPointer cdPtr + ) internal pure returns (uint48 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint56 at `cdPtr` in calldata. + function readUint56( + CalldataPointer cdPtr + ) internal pure returns (uint56 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint64 at `cdPtr` in calldata. + function readUint64( + CalldataPointer cdPtr + ) internal pure returns (uint64 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint72 at `cdPtr` in calldata. + function readUint72( + CalldataPointer cdPtr + ) internal pure returns (uint72 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint80 at `cdPtr` in calldata. + function readUint80( + CalldataPointer cdPtr + ) internal pure returns (uint80 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint88 at `cdPtr` in calldata. + function readUint88( + CalldataPointer cdPtr + ) internal pure returns (uint88 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint96 at `cdPtr` in calldata. + function readUint96( + CalldataPointer cdPtr + ) internal pure returns (uint96 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint104 at `cdPtr` in calldata. + function readUint104( + CalldataPointer cdPtr + ) internal pure returns (uint104 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint112 at `cdPtr` in calldata. + function readUint112( + CalldataPointer cdPtr + ) internal pure returns (uint112 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint120 at `cdPtr` in calldata. + function readUint120( + CalldataPointer cdPtr + ) internal pure returns (uint120 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint128 at `cdPtr` in calldata. + function readUint128( + CalldataPointer cdPtr + ) internal pure returns (uint128 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint136 at `cdPtr` in calldata. + function readUint136( + CalldataPointer cdPtr + ) internal pure returns (uint136 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint144 at `cdPtr` in calldata. + function readUint144( + CalldataPointer cdPtr + ) internal pure returns (uint144 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint152 at `cdPtr` in calldata. + function readUint152( + CalldataPointer cdPtr + ) internal pure returns (uint152 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint160 at `cdPtr` in calldata. + function readUint160( + CalldataPointer cdPtr + ) internal pure returns (uint160 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint168 at `cdPtr` in calldata. + function readUint168( + CalldataPointer cdPtr + ) internal pure returns (uint168 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint176 at `cdPtr` in calldata. + function readUint176( + CalldataPointer cdPtr + ) internal pure returns (uint176 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint184 at `cdPtr` in calldata. + function readUint184( + CalldataPointer cdPtr + ) internal pure returns (uint184 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint192 at `cdPtr` in calldata. + function readUint192( + CalldataPointer cdPtr + ) internal pure returns (uint192 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint200 at `cdPtr` in calldata. + function readUint200( + CalldataPointer cdPtr + ) internal pure returns (uint200 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint208 at `cdPtr` in calldata. + function readUint208( + CalldataPointer cdPtr + ) internal pure returns (uint208 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint216 at `cdPtr` in calldata. + function readUint216( + CalldataPointer cdPtr + ) internal pure returns (uint216 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint224 at `cdPtr` in calldata. + function readUint224( + CalldataPointer cdPtr + ) internal pure returns (uint224 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint232 at `cdPtr` in calldata. + function readUint232( + CalldataPointer cdPtr + ) internal pure returns (uint232 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint240 at `cdPtr` in calldata. + function readUint240( + CalldataPointer cdPtr + ) internal pure returns (uint240 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint248 at `cdPtr` in calldata. + function readUint248( + CalldataPointer cdPtr + ) internal pure returns (uint248 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the uint256 at `cdPtr` in calldata. + function readUint256( + CalldataPointer cdPtr + ) internal pure returns (uint256 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int8 at `cdPtr` in calldata. + function readInt8( + CalldataPointer cdPtr + ) internal pure returns (int8 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int16 at `cdPtr` in calldata. + function readInt16( + CalldataPointer cdPtr + ) internal pure returns (int16 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int24 at `cdPtr` in calldata. + function readInt24( + CalldataPointer cdPtr + ) internal pure returns (int24 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int32 at `cdPtr` in calldata. + function readInt32( + CalldataPointer cdPtr + ) internal pure returns (int32 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int40 at `cdPtr` in calldata. + function readInt40( + CalldataPointer cdPtr + ) internal pure returns (int40 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int48 at `cdPtr` in calldata. + function readInt48( + CalldataPointer cdPtr + ) internal pure returns (int48 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int56 at `cdPtr` in calldata. + function readInt56( + CalldataPointer cdPtr + ) internal pure returns (int56 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int64 at `cdPtr` in calldata. + function readInt64( + CalldataPointer cdPtr + ) internal pure returns (int64 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int72 at `cdPtr` in calldata. + function readInt72( + CalldataPointer cdPtr + ) internal pure returns (int72 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int80 at `cdPtr` in calldata. + function readInt80( + CalldataPointer cdPtr + ) internal pure returns (int80 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int88 at `cdPtr` in calldata. + function readInt88( + CalldataPointer cdPtr + ) internal pure returns (int88 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int96 at `cdPtr` in calldata. + function readInt96( + CalldataPointer cdPtr + ) internal pure returns (int96 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int104 at `cdPtr` in calldata. + function readInt104( + CalldataPointer cdPtr + ) internal pure returns (int104 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int112 at `cdPtr` in calldata. + function readInt112( + CalldataPointer cdPtr + ) internal pure returns (int112 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int120 at `cdPtr` in calldata. + function readInt120( + CalldataPointer cdPtr + ) internal pure returns (int120 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int128 at `cdPtr` in calldata. + function readInt128( + CalldataPointer cdPtr + ) internal pure returns (int128 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int136 at `cdPtr` in calldata. + function readInt136( + CalldataPointer cdPtr + ) internal pure returns (int136 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int144 at `cdPtr` in calldata. + function readInt144( + CalldataPointer cdPtr + ) internal pure returns (int144 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int152 at `cdPtr` in calldata. + function readInt152( + CalldataPointer cdPtr + ) internal pure returns (int152 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int160 at `cdPtr` in calldata. + function readInt160( + CalldataPointer cdPtr + ) internal pure returns (int160 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int168 at `cdPtr` in calldata. + function readInt168( + CalldataPointer cdPtr + ) internal pure returns (int168 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int176 at `cdPtr` in calldata. + function readInt176( + CalldataPointer cdPtr + ) internal pure returns (int176 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int184 at `cdPtr` in calldata. + function readInt184( + CalldataPointer cdPtr + ) internal pure returns (int184 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int192 at `cdPtr` in calldata. + function readInt192( + CalldataPointer cdPtr + ) internal pure returns (int192 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int200 at `cdPtr` in calldata. + function readInt200( + CalldataPointer cdPtr + ) internal pure returns (int200 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int208 at `cdPtr` in calldata. + function readInt208( + CalldataPointer cdPtr + ) internal pure returns (int208 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int216 at `cdPtr` in calldata. + function readInt216( + CalldataPointer cdPtr + ) internal pure returns (int216 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int224 at `cdPtr` in calldata. + function readInt224( + CalldataPointer cdPtr + ) internal pure returns (int224 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int232 at `cdPtr` in calldata. + function readInt232( + CalldataPointer cdPtr + ) internal pure returns (int232 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int240 at `cdPtr` in calldata. + function readInt240( + CalldataPointer cdPtr + ) internal pure returns (int240 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int248 at `cdPtr` in calldata. + function readInt248( + CalldataPointer cdPtr + ) internal pure returns (int248 value) { + assembly { + value := calldataload(cdPtr) + } + } + + /// @dev Reads the int256 at `cdPtr` in calldata. + function readInt256( + CalldataPointer cdPtr + ) internal pure returns (int256 value) { + assembly { + value := calldataload(cdPtr) + } + } +} + +library ReturndataReaders { + /// @dev Reads value at `rdPtr` & applies a mask to return only last 4 bytes + function readMaskedUint256( + ReturndataPointer rdPtr + ) internal pure returns (uint256 value) { + value = rdPtr.readUint256() & OffsetOrLengthMask; + } + + /// @dev Reads the bool at `rdPtr` in returndata. + function readBool( + ReturndataPointer rdPtr + ) internal pure returns (bool value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the address at `rdPtr` in returndata. + function readAddress( + ReturndataPointer rdPtr + ) internal pure returns (address value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes1 at `rdPtr` in returndata. + function readBytes1( + ReturndataPointer rdPtr + ) internal pure returns (bytes1 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes2 at `rdPtr` in returndata. + function readBytes2( + ReturndataPointer rdPtr + ) internal pure returns (bytes2 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes3 at `rdPtr` in returndata. + function readBytes3( + ReturndataPointer rdPtr + ) internal pure returns (bytes3 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes4 at `rdPtr` in returndata. + function readBytes4( + ReturndataPointer rdPtr + ) internal pure returns (bytes4 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes5 at `rdPtr` in returndata. + function readBytes5( + ReturndataPointer rdPtr + ) internal pure returns (bytes5 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes6 at `rdPtr` in returndata. + function readBytes6( + ReturndataPointer rdPtr + ) internal pure returns (bytes6 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes7 at `rdPtr` in returndata. + function readBytes7( + ReturndataPointer rdPtr + ) internal pure returns (bytes7 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes8 at `rdPtr` in returndata. + function readBytes8( + ReturndataPointer rdPtr + ) internal pure returns (bytes8 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes9 at `rdPtr` in returndata. + function readBytes9( + ReturndataPointer rdPtr + ) internal pure returns (bytes9 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes10 at `rdPtr` in returndata. + function readBytes10( + ReturndataPointer rdPtr + ) internal pure returns (bytes10 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes11 at `rdPtr` in returndata. + function readBytes11( + ReturndataPointer rdPtr + ) internal pure returns (bytes11 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes12 at `rdPtr` in returndata. + function readBytes12( + ReturndataPointer rdPtr + ) internal pure returns (bytes12 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes13 at `rdPtr` in returndata. + function readBytes13( + ReturndataPointer rdPtr + ) internal pure returns (bytes13 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes14 at `rdPtr` in returndata. + function readBytes14( + ReturndataPointer rdPtr + ) internal pure returns (bytes14 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes15 at `rdPtr` in returndata. + function readBytes15( + ReturndataPointer rdPtr + ) internal pure returns (bytes15 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes16 at `rdPtr` in returndata. + function readBytes16( + ReturndataPointer rdPtr + ) internal pure returns (bytes16 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes17 at `rdPtr` in returndata. + function readBytes17( + ReturndataPointer rdPtr + ) internal pure returns (bytes17 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes18 at `rdPtr` in returndata. + function readBytes18( + ReturndataPointer rdPtr + ) internal pure returns (bytes18 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes19 at `rdPtr` in returndata. + function readBytes19( + ReturndataPointer rdPtr + ) internal pure returns (bytes19 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes20 at `rdPtr` in returndata. + function readBytes20( + ReturndataPointer rdPtr + ) internal pure returns (bytes20 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes21 at `rdPtr` in returndata. + function readBytes21( + ReturndataPointer rdPtr + ) internal pure returns (bytes21 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes22 at `rdPtr` in returndata. + function readBytes22( + ReturndataPointer rdPtr + ) internal pure returns (bytes22 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes23 at `rdPtr` in returndata. + function readBytes23( + ReturndataPointer rdPtr + ) internal pure returns (bytes23 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes24 at `rdPtr` in returndata. + function readBytes24( + ReturndataPointer rdPtr + ) internal pure returns (bytes24 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes25 at `rdPtr` in returndata. + function readBytes25( + ReturndataPointer rdPtr + ) internal pure returns (bytes25 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes26 at `rdPtr` in returndata. + function readBytes26( + ReturndataPointer rdPtr + ) internal pure returns (bytes26 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes27 at `rdPtr` in returndata. + function readBytes27( + ReturndataPointer rdPtr + ) internal pure returns (bytes27 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes28 at `rdPtr` in returndata. + function readBytes28( + ReturndataPointer rdPtr + ) internal pure returns (bytes28 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes29 at `rdPtr` in returndata. + function readBytes29( + ReturndataPointer rdPtr + ) internal pure returns (bytes29 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes30 at `rdPtr` in returndata. + function readBytes30( + ReturndataPointer rdPtr + ) internal pure returns (bytes30 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes31 at `rdPtr` in returndata. + function readBytes31( + ReturndataPointer rdPtr + ) internal pure returns (bytes31 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the bytes32 at `rdPtr` in returndata. + function readBytes32( + ReturndataPointer rdPtr + ) internal pure returns (bytes32 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint8 at `rdPtr` in returndata. + function readUint8( + ReturndataPointer rdPtr + ) internal pure returns (uint8 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint16 at `rdPtr` in returndata. + function readUint16( + ReturndataPointer rdPtr + ) internal pure returns (uint16 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint24 at `rdPtr` in returndata. + function readUint24( + ReturndataPointer rdPtr + ) internal pure returns (uint24 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint32 at `rdPtr` in returndata. + function readUint32( + ReturndataPointer rdPtr + ) internal pure returns (uint32 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint40 at `rdPtr` in returndata. + function readUint40( + ReturndataPointer rdPtr + ) internal pure returns (uint40 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint48 at `rdPtr` in returndata. + function readUint48( + ReturndataPointer rdPtr + ) internal pure returns (uint48 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint56 at `rdPtr` in returndata. + function readUint56( + ReturndataPointer rdPtr + ) internal pure returns (uint56 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint64 at `rdPtr` in returndata. + function readUint64( + ReturndataPointer rdPtr + ) internal pure returns (uint64 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint72 at `rdPtr` in returndata. + function readUint72( + ReturndataPointer rdPtr + ) internal pure returns (uint72 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint80 at `rdPtr` in returndata. + function readUint80( + ReturndataPointer rdPtr + ) internal pure returns (uint80 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint88 at `rdPtr` in returndata. + function readUint88( + ReturndataPointer rdPtr + ) internal pure returns (uint88 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint96 at `rdPtr` in returndata. + function readUint96( + ReturndataPointer rdPtr + ) internal pure returns (uint96 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint104 at `rdPtr` in returndata. + function readUint104( + ReturndataPointer rdPtr + ) internal pure returns (uint104 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint112 at `rdPtr` in returndata. + function readUint112( + ReturndataPointer rdPtr + ) internal pure returns (uint112 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint120 at `rdPtr` in returndata. + function readUint120( + ReturndataPointer rdPtr + ) internal pure returns (uint120 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint128 at `rdPtr` in returndata. + function readUint128( + ReturndataPointer rdPtr + ) internal pure returns (uint128 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint136 at `rdPtr` in returndata. + function readUint136( + ReturndataPointer rdPtr + ) internal pure returns (uint136 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint144 at `rdPtr` in returndata. + function readUint144( + ReturndataPointer rdPtr + ) internal pure returns (uint144 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint152 at `rdPtr` in returndata. + function readUint152( + ReturndataPointer rdPtr + ) internal pure returns (uint152 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint160 at `rdPtr` in returndata. + function readUint160( + ReturndataPointer rdPtr + ) internal pure returns (uint160 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint168 at `rdPtr` in returndata. + function readUint168( + ReturndataPointer rdPtr + ) internal pure returns (uint168 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint176 at `rdPtr` in returndata. + function readUint176( + ReturndataPointer rdPtr + ) internal pure returns (uint176 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint184 at `rdPtr` in returndata. + function readUint184( + ReturndataPointer rdPtr + ) internal pure returns (uint184 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint192 at `rdPtr` in returndata. + function readUint192( + ReturndataPointer rdPtr + ) internal pure returns (uint192 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint200 at `rdPtr` in returndata. + function readUint200( + ReturndataPointer rdPtr + ) internal pure returns (uint200 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint208 at `rdPtr` in returndata. + function readUint208( + ReturndataPointer rdPtr + ) internal pure returns (uint208 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint216 at `rdPtr` in returndata. + function readUint216( + ReturndataPointer rdPtr + ) internal pure returns (uint216 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint224 at `rdPtr` in returndata. + function readUint224( + ReturndataPointer rdPtr + ) internal pure returns (uint224 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint232 at `rdPtr` in returndata. + function readUint232( + ReturndataPointer rdPtr + ) internal pure returns (uint232 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint240 at `rdPtr` in returndata. + function readUint240( + ReturndataPointer rdPtr + ) internal pure returns (uint240 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint248 at `rdPtr` in returndata. + function readUint248( + ReturndataPointer rdPtr + ) internal pure returns (uint248 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the uint256 at `rdPtr` in returndata. + function readUint256( + ReturndataPointer rdPtr + ) internal pure returns (uint256 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int8 at `rdPtr` in returndata. + function readInt8( + ReturndataPointer rdPtr + ) internal pure returns (int8 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int16 at `rdPtr` in returndata. + function readInt16( + ReturndataPointer rdPtr + ) internal pure returns (int16 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int24 at `rdPtr` in returndata. + function readInt24( + ReturndataPointer rdPtr + ) internal pure returns (int24 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int32 at `rdPtr` in returndata. + function readInt32( + ReturndataPointer rdPtr + ) internal pure returns (int32 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int40 at `rdPtr` in returndata. + function readInt40( + ReturndataPointer rdPtr + ) internal pure returns (int40 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int48 at `rdPtr` in returndata. + function readInt48( + ReturndataPointer rdPtr + ) internal pure returns (int48 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int56 at `rdPtr` in returndata. + function readInt56( + ReturndataPointer rdPtr + ) internal pure returns (int56 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int64 at `rdPtr` in returndata. + function readInt64( + ReturndataPointer rdPtr + ) internal pure returns (int64 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int72 at `rdPtr` in returndata. + function readInt72( + ReturndataPointer rdPtr + ) internal pure returns (int72 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int80 at `rdPtr` in returndata. + function readInt80( + ReturndataPointer rdPtr + ) internal pure returns (int80 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int88 at `rdPtr` in returndata. + function readInt88( + ReturndataPointer rdPtr + ) internal pure returns (int88 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int96 at `rdPtr` in returndata. + function readInt96( + ReturndataPointer rdPtr + ) internal pure returns (int96 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int104 at `rdPtr` in returndata. + function readInt104( + ReturndataPointer rdPtr + ) internal pure returns (int104 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int112 at `rdPtr` in returndata. + function readInt112( + ReturndataPointer rdPtr + ) internal pure returns (int112 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int120 at `rdPtr` in returndata. + function readInt120( + ReturndataPointer rdPtr + ) internal pure returns (int120 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int128 at `rdPtr` in returndata. + function readInt128( + ReturndataPointer rdPtr + ) internal pure returns (int128 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int136 at `rdPtr` in returndata. + function readInt136( + ReturndataPointer rdPtr + ) internal pure returns (int136 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int144 at `rdPtr` in returndata. + function readInt144( + ReturndataPointer rdPtr + ) internal pure returns (int144 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int152 at `rdPtr` in returndata. + function readInt152( + ReturndataPointer rdPtr + ) internal pure returns (int152 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int160 at `rdPtr` in returndata. + function readInt160( + ReturndataPointer rdPtr + ) internal pure returns (int160 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int168 at `rdPtr` in returndata. + function readInt168( + ReturndataPointer rdPtr + ) internal pure returns (int168 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int176 at `rdPtr` in returndata. + function readInt176( + ReturndataPointer rdPtr + ) internal pure returns (int176 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int184 at `rdPtr` in returndata. + function readInt184( + ReturndataPointer rdPtr + ) internal pure returns (int184 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int192 at `rdPtr` in returndata. + function readInt192( + ReturndataPointer rdPtr + ) internal pure returns (int192 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int200 at `rdPtr` in returndata. + function readInt200( + ReturndataPointer rdPtr + ) internal pure returns (int200 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int208 at `rdPtr` in returndata. + function readInt208( + ReturndataPointer rdPtr + ) internal pure returns (int208 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int216 at `rdPtr` in returndata. + function readInt216( + ReturndataPointer rdPtr + ) internal pure returns (int216 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int224 at `rdPtr` in returndata. + function readInt224( + ReturndataPointer rdPtr + ) internal pure returns (int224 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int232 at `rdPtr` in returndata. + function readInt232( + ReturndataPointer rdPtr + ) internal pure returns (int232 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int240 at `rdPtr` in returndata. + function readInt240( + ReturndataPointer rdPtr + ) internal pure returns (int240 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int248 at `rdPtr` in returndata. + function readInt248( + ReturndataPointer rdPtr + ) internal pure returns (int248 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } + + /// @dev Reads the int256 at `rdPtr` in returndata. + function readInt256( + ReturndataPointer rdPtr + ) internal pure returns (int256 value) { + assembly { + returndatacopy(0, rdPtr, _OneWord) + value := mload(0) + } + } +} + +library MemoryReaders { + /// @dev Reads the memory pointer at `mPtr` in memory. + function readMemoryPointer( + MemoryPointer mPtr + ) internal pure returns (MemoryPointer value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads value at `mPtr` & applies a mask to return only last 4 bytes + function readMaskedUint256( + MemoryPointer mPtr + ) internal pure returns (uint256 value) { + value = mPtr.readUint256() & OffsetOrLengthMask; + } + + /// @dev Reads the bool at `mPtr` in memory. + function readBool(MemoryPointer mPtr) internal pure returns (bool value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the address at `mPtr` in memory. + function readAddress( + MemoryPointer mPtr + ) internal pure returns (address value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes1 at `mPtr` in memory. + function readBytes1( + MemoryPointer mPtr + ) internal pure returns (bytes1 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes2 at `mPtr` in memory. + function readBytes2( + MemoryPointer mPtr + ) internal pure returns (bytes2 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes3 at `mPtr` in memory. + function readBytes3( + MemoryPointer mPtr + ) internal pure returns (bytes3 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes4 at `mPtr` in memory. + function readBytes4( + MemoryPointer mPtr + ) internal pure returns (bytes4 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes5 at `mPtr` in memory. + function readBytes5( + MemoryPointer mPtr + ) internal pure returns (bytes5 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes6 at `mPtr` in memory. + function readBytes6( + MemoryPointer mPtr + ) internal pure returns (bytes6 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes7 at `mPtr` in memory. + function readBytes7( + MemoryPointer mPtr + ) internal pure returns (bytes7 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes8 at `mPtr` in memory. + function readBytes8( + MemoryPointer mPtr + ) internal pure returns (bytes8 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes9 at `mPtr` in memory. + function readBytes9( + MemoryPointer mPtr + ) internal pure returns (bytes9 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes10 at `mPtr` in memory. + function readBytes10( + MemoryPointer mPtr + ) internal pure returns (bytes10 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes11 at `mPtr` in memory. + function readBytes11( + MemoryPointer mPtr + ) internal pure returns (bytes11 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes12 at `mPtr` in memory. + function readBytes12( + MemoryPointer mPtr + ) internal pure returns (bytes12 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes13 at `mPtr` in memory. + function readBytes13( + MemoryPointer mPtr + ) internal pure returns (bytes13 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes14 at `mPtr` in memory. + function readBytes14( + MemoryPointer mPtr + ) internal pure returns (bytes14 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes15 at `mPtr` in memory. + function readBytes15( + MemoryPointer mPtr + ) internal pure returns (bytes15 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes16 at `mPtr` in memory. + function readBytes16( + MemoryPointer mPtr + ) internal pure returns (bytes16 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes17 at `mPtr` in memory. + function readBytes17( + MemoryPointer mPtr + ) internal pure returns (bytes17 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes18 at `mPtr` in memory. + function readBytes18( + MemoryPointer mPtr + ) internal pure returns (bytes18 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes19 at `mPtr` in memory. + function readBytes19( + MemoryPointer mPtr + ) internal pure returns (bytes19 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes20 at `mPtr` in memory. + function readBytes20( + MemoryPointer mPtr + ) internal pure returns (bytes20 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes21 at `mPtr` in memory. + function readBytes21( + MemoryPointer mPtr + ) internal pure returns (bytes21 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes22 at `mPtr` in memory. + function readBytes22( + MemoryPointer mPtr + ) internal pure returns (bytes22 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes23 at `mPtr` in memory. + function readBytes23( + MemoryPointer mPtr + ) internal pure returns (bytes23 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes24 at `mPtr` in memory. + function readBytes24( + MemoryPointer mPtr + ) internal pure returns (bytes24 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes25 at `mPtr` in memory. + function readBytes25( + MemoryPointer mPtr + ) internal pure returns (bytes25 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes26 at `mPtr` in memory. + function readBytes26( + MemoryPointer mPtr + ) internal pure returns (bytes26 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes27 at `mPtr` in memory. + function readBytes27( + MemoryPointer mPtr + ) internal pure returns (bytes27 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes28 at `mPtr` in memory. + function readBytes28( + MemoryPointer mPtr + ) internal pure returns (bytes28 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes29 at `mPtr` in memory. + function readBytes29( + MemoryPointer mPtr + ) internal pure returns (bytes29 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes30 at `mPtr` in memory. + function readBytes30( + MemoryPointer mPtr + ) internal pure returns (bytes30 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes31 at `mPtr` in memory. + function readBytes31( + MemoryPointer mPtr + ) internal pure returns (bytes31 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the bytes32 at `mPtr` in memory. + function readBytes32( + MemoryPointer mPtr + ) internal pure returns (bytes32 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint8 at `mPtr` in memory. + function readUint8(MemoryPointer mPtr) internal pure returns (uint8 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint16 at `mPtr` in memory. + function readUint16( + MemoryPointer mPtr + ) internal pure returns (uint16 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint24 at `mPtr` in memory. + function readUint24( + MemoryPointer mPtr + ) internal pure returns (uint24 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint32 at `mPtr` in memory. + function readUint32( + MemoryPointer mPtr + ) internal pure returns (uint32 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint40 at `mPtr` in memory. + function readUint40( + MemoryPointer mPtr + ) internal pure returns (uint40 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint48 at `mPtr` in memory. + function readUint48( + MemoryPointer mPtr + ) internal pure returns (uint48 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint56 at `mPtr` in memory. + function readUint56( + MemoryPointer mPtr + ) internal pure returns (uint56 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint64 at `mPtr` in memory. + function readUint64( + MemoryPointer mPtr + ) internal pure returns (uint64 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint72 at `mPtr` in memory. + function readUint72( + MemoryPointer mPtr + ) internal pure returns (uint72 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint80 at `mPtr` in memory. + function readUint80( + MemoryPointer mPtr + ) internal pure returns (uint80 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint88 at `mPtr` in memory. + function readUint88( + MemoryPointer mPtr + ) internal pure returns (uint88 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint96 at `mPtr` in memory. + function readUint96( + MemoryPointer mPtr + ) internal pure returns (uint96 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint104 at `mPtr` in memory. + function readUint104( + MemoryPointer mPtr + ) internal pure returns (uint104 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint112 at `mPtr` in memory. + function readUint112( + MemoryPointer mPtr + ) internal pure returns (uint112 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint120 at `mPtr` in memory. + function readUint120( + MemoryPointer mPtr + ) internal pure returns (uint120 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint128 at `mPtr` in memory. + function readUint128( + MemoryPointer mPtr + ) internal pure returns (uint128 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint136 at `mPtr` in memory. + function readUint136( + MemoryPointer mPtr + ) internal pure returns (uint136 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint144 at `mPtr` in memory. + function readUint144( + MemoryPointer mPtr + ) internal pure returns (uint144 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint152 at `mPtr` in memory. + function readUint152( + MemoryPointer mPtr + ) internal pure returns (uint152 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint160 at `mPtr` in memory. + function readUint160( + MemoryPointer mPtr + ) internal pure returns (uint160 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint168 at `mPtr` in memory. + function readUint168( + MemoryPointer mPtr + ) internal pure returns (uint168 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint176 at `mPtr` in memory. + function readUint176( + MemoryPointer mPtr + ) internal pure returns (uint176 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint184 at `mPtr` in memory. + function readUint184( + MemoryPointer mPtr + ) internal pure returns (uint184 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint192 at `mPtr` in memory. + function readUint192( + MemoryPointer mPtr + ) internal pure returns (uint192 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint200 at `mPtr` in memory. + function readUint200( + MemoryPointer mPtr + ) internal pure returns (uint200 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint208 at `mPtr` in memory. + function readUint208( + MemoryPointer mPtr + ) internal pure returns (uint208 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint216 at `mPtr` in memory. + function readUint216( + MemoryPointer mPtr + ) internal pure returns (uint216 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint224 at `mPtr` in memory. + function readUint224( + MemoryPointer mPtr + ) internal pure returns (uint224 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint232 at `mPtr` in memory. + function readUint232( + MemoryPointer mPtr + ) internal pure returns (uint232 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint240 at `mPtr` in memory. + function readUint240( + MemoryPointer mPtr + ) internal pure returns (uint240 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint248 at `mPtr` in memory. + function readUint248( + MemoryPointer mPtr + ) internal pure returns (uint248 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the uint256 at `mPtr` in memory. + function readUint256( + MemoryPointer mPtr + ) internal pure returns (uint256 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int8 at `mPtr` in memory. + function readInt8(MemoryPointer mPtr) internal pure returns (int8 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int16 at `mPtr` in memory. + function readInt16(MemoryPointer mPtr) internal pure returns (int16 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int24 at `mPtr` in memory. + function readInt24(MemoryPointer mPtr) internal pure returns (int24 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int32 at `mPtr` in memory. + function readInt32(MemoryPointer mPtr) internal pure returns (int32 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int40 at `mPtr` in memory. + function readInt40(MemoryPointer mPtr) internal pure returns (int40 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int48 at `mPtr` in memory. + function readInt48(MemoryPointer mPtr) internal pure returns (int48 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int56 at `mPtr` in memory. + function readInt56(MemoryPointer mPtr) internal pure returns (int56 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int64 at `mPtr` in memory. + function readInt64(MemoryPointer mPtr) internal pure returns (int64 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int72 at `mPtr` in memory. + function readInt72(MemoryPointer mPtr) internal pure returns (int72 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int80 at `mPtr` in memory. + function readInt80(MemoryPointer mPtr) internal pure returns (int80 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int88 at `mPtr` in memory. + function readInt88(MemoryPointer mPtr) internal pure returns (int88 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int96 at `mPtr` in memory. + function readInt96(MemoryPointer mPtr) internal pure returns (int96 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int104 at `mPtr` in memory. + function readInt104( + MemoryPointer mPtr + ) internal pure returns (int104 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int112 at `mPtr` in memory. + function readInt112( + MemoryPointer mPtr + ) internal pure returns (int112 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int120 at `mPtr` in memory. + function readInt120( + MemoryPointer mPtr + ) internal pure returns (int120 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int128 at `mPtr` in memory. + function readInt128( + MemoryPointer mPtr + ) internal pure returns (int128 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int136 at `mPtr` in memory. + function readInt136( + MemoryPointer mPtr + ) internal pure returns (int136 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int144 at `mPtr` in memory. + function readInt144( + MemoryPointer mPtr + ) internal pure returns (int144 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int152 at `mPtr` in memory. + function readInt152( + MemoryPointer mPtr + ) internal pure returns (int152 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int160 at `mPtr` in memory. + function readInt160( + MemoryPointer mPtr + ) internal pure returns (int160 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int168 at `mPtr` in memory. + function readInt168( + MemoryPointer mPtr + ) internal pure returns (int168 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int176 at `mPtr` in memory. + function readInt176( + MemoryPointer mPtr + ) internal pure returns (int176 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int184 at `mPtr` in memory. + function readInt184( + MemoryPointer mPtr + ) internal pure returns (int184 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int192 at `mPtr` in memory. + function readInt192( + MemoryPointer mPtr + ) internal pure returns (int192 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int200 at `mPtr` in memory. + function readInt200( + MemoryPointer mPtr + ) internal pure returns (int200 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int208 at `mPtr` in memory. + function readInt208( + MemoryPointer mPtr + ) internal pure returns (int208 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int216 at `mPtr` in memory. + function readInt216( + MemoryPointer mPtr + ) internal pure returns (int216 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int224 at `mPtr` in memory. + function readInt224( + MemoryPointer mPtr + ) internal pure returns (int224 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int232 at `mPtr` in memory. + function readInt232( + MemoryPointer mPtr + ) internal pure returns (int232 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int240 at `mPtr` in memory. + function readInt240( + MemoryPointer mPtr + ) internal pure returns (int240 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int248 at `mPtr` in memory. + function readInt248( + MemoryPointer mPtr + ) internal pure returns (int248 value) { + assembly { + value := mload(mPtr) + } + } + + /// @dev Reads the int256 at `mPtr` in memory. + function readInt256( + MemoryPointer mPtr + ) internal pure returns (int256 value) { + assembly { + value := mload(mPtr) + } + } +} + +library MemoryWriters { + /// @dev Writes `valuePtr` to memory at `mPtr`. + function write(MemoryPointer mPtr, MemoryPointer valuePtr) internal pure { + assembly { + mstore(mPtr, valuePtr) + } + } + + /// @dev Writes a boolean `value` to `mPtr` in memory. + function write(MemoryPointer mPtr, bool value) internal pure { + assembly { + mstore(mPtr, value) + } + } + + /// @dev Writes an address `value` to `mPtr` in memory. + function write(MemoryPointer mPtr, address value) internal pure { + assembly { + mstore(mPtr, value) + } + } + + /// @dev Writes a bytes32 `value` to `mPtr` in memory. + /// Separate name to disambiguate literal write parameters. + function writeBytes32(MemoryPointer mPtr, bytes32 value) internal pure { + assembly { + mstore(mPtr, value) + } + } + + /// @dev Writes a uint256 `value` to `mPtr` in memory. + function write(MemoryPointer mPtr, uint256 value) internal pure { + assembly { + mstore(mPtr, value) + } + } + + /// @dev Writes an int256 `value` to `mPtr` in memory. + /// Separate name to disambiguate literal write parameters. + function writeInt(MemoryPointer mPtr, int256 value) internal pure { + assembly { + mstore(mPtr, value) + } + } +} diff --git a/contracts/helpers/TransferHelper.sol b/contracts/helpers/TransferHelper.sol index 593fd967b..b08ec405e 100644 --- a/contracts/helpers/TransferHelper.sol +++ b/contracts/helpers/TransferHelper.sol @@ -1,38 +1,42 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -import "./TransferHelperStructs.sol"; +import { IERC721Receiver } from "../interfaces/IERC721Receiver.sol"; -import { TokenTransferrer } from "../lib/TokenTransferrer.sol"; +import { + TransferHelperItem, + TransferHelperItemsWithRecipient +} from "./TransferHelperStructs.sol"; + +import { ConduitItemType } from "../conduit/lib/ConduitEnums.sol"; import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; -// prettier-ignore import { ConduitControllerInterface } from "../interfaces/ConduitControllerInterface.sol"; -import { Conduit } from "../conduit/Conduit.sol"; - import { ConduitTransfer } from "../conduit/lib/ConduitStructs.sol"; -// prettier-ignore import { TransferHelperInterface } from "../interfaces/TransferHelperInterface.sol"; +import { TransferHelperErrors } from "../interfaces/TransferHelperErrors.sol"; + /** * @title TransferHelper - * @author stuckinaboot, stephankmin + * @author stephankmin, stuckinaboot, ryanio * @notice TransferHelper is a utility contract for transferring - * ERC20/ERC721/ERC1155 items in bulk to a specific recipient. + * ERC20/ERC721/ERC1155 items in bulk to specific recipients. */ -contract TransferHelper is TransferHelperInterface, TokenTransferrer { +contract TransferHelper is TransferHelperInterface, TransferHelperErrors { // Allow for interaction with the conduit controller. ConduitControllerInterface internal immutable _CONDUIT_CONTROLLER; - // Cache the conduit creation hash used by the conduit controller. + // Set conduit creation code and runtime code hashes as immutable arguments. bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH; + bytes32 internal immutable _CONDUIT_RUNTIME_CODE_HASH; /** * @dev Set the supplied conduit controller and retrieve its @@ -44,123 +48,297 @@ contract TransferHelper is TransferHelperInterface, TokenTransferrer { * ERC20/721/1155 tokens. */ constructor(address conduitController) { - // Get the conduit creation code hash from the supplied conduit - // controller and set it as an immutable. + // Get the conduit creation code and runtime code hashes from the + // supplied conduit controller and set them as an immutable. ConduitControllerInterface controller = ConduitControllerInterface( conduitController ); - (_CONDUIT_CREATION_CODE_HASH, ) = controller.getConduitCodeHashes(); + (_CONDUIT_CREATION_CODE_HASH, _CONDUIT_RUNTIME_CODE_HASH) = controller + .getConduitCodeHashes(); // Set the supplied conduit controller as an immutable. _CONDUIT_CONTROLLER = controller; } /** - * @notice Transfer multiple items to a single recipient. + * @notice Transfer multiple ERC20/ERC721/ERC1155 items to + * specified recipients. * - * @param items The items to transfer. - * @param recipient The address the items should be transferred to. - * @param conduitKey The key of the conduit through which the bulk transfer - * should occur. + * @param items The items to transfer to an intended recipient. + * @param conduitKey A mandatory conduit key referring to a conduit through + * which the bulk transfer should occur. * * @return magicValue A value indicating that the transfers were successful. */ function bulkTransfer( - TransferHelperItem[] calldata items, - address recipient, + TransferHelperItemsWithRecipient[] calldata items, bytes32 conduitKey ) external override returns (bytes4 magicValue) { - // Retrieve total number of transfers and place on stack. - uint256 totalTransfers = items.length; - - // If no conduitKey is given, use TokenTransferrer to perform transfers. + // Ensure that a conduit key has been supplied. if (conduitKey == bytes32(0)) { - // Skip overflow checks: all for loops are indexed starting at zero. - unchecked { - // Iterate over each transfer. - for (uint256 i = 0; i < totalTransfers; ++i) { - // Retrieve the transfer in question. - TransferHelperItem calldata item = items[i]; - - // Perform a transfer based on the transfer's item type. - // Revert if item being transferred is a native token. - if (item.itemType == ConduitItemType.NATIVE) { - revert InvalidItemType(); - } else if (item.itemType == ConduitItemType.ERC20) { - _performERC20Transfer( - item.token, - msg.sender, - recipient, - item.amount - ); - } else if (item.itemType == ConduitItemType.ERC721) { - _performERC721Transfer( - item.token, - msg.sender, - recipient, - item.identifier - ); - } else { - _performERC1155Transfer( - item.token, - msg.sender, - recipient, - item.identifier, - item.amount - ); - } - } - } + revert InvalidConduit(conduitKey, address(0)); } - // Otherwise, a conduitKey was provided. - else { - // Derive the conduit address from the deployer, conduit key - // and creation code hash. - address conduit = address( - uint160( - uint256( - keccak256( - abi.encodePacked( - bytes1(0xff), - address(_CONDUIT_CONTROLLER), - conduitKey, - _CONDUIT_CREATION_CODE_HASH - ) + + // Use conduit derived from supplied conduit key to perform transfers. + _performTransfersWithConduit(items, conduitKey); + + // Return a magic value indicating that the transfers were performed. + magicValue = this.bulkTransfer.selector; + } + + /** + * @notice Perform multiple transfers to specified recipients via the + * conduit derived from the provided conduit key. + * + * @param transfers The items to transfer. + * @param conduitKey The conduit key referring to the conduit through + * which the bulk transfer should occur. + */ + function _performTransfersWithConduit( + TransferHelperItemsWithRecipient[] calldata transfers, + bytes32 conduitKey + ) internal { + // Retrieve total number of transfers and place on stack. + uint256 numTransfers = transfers.length; + + // Derive the conduit address from the deployer, conduit key + // and creation code hash. + address conduit = address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + address(_CONDUIT_CONTROLLER), + conduitKey, + _CONDUIT_CREATION_CODE_HASH ) ) ) - ); + ) + ); - // Declare a new array to populate with each token transfer. - ConduitTransfer[] memory conduitTransfers = new ConduitTransfer[]( - totalTransfers - ); + // Declare a variable to store the sum of all items across transfers. + uint256 sumOfItemsAcrossAllTransfers; + + // Skip overflow checks: all for loops are indexed starting at zero. + unchecked { + // Iterate over each transfer. + for (uint256 i = 0; i < numTransfers; ++i) { + // Retrieve the transfer in question. + TransferHelperItemsWithRecipient calldata transfer = transfers[ + i + ]; + + // Increment totalItems by the number of items in the transfer. + sumOfItemsAcrossAllTransfers += transfer.items.length; + } + } + + // Declare a new array in memory with length totalItems to populate with + // each conduit transfer. + ConduitTransfer[] memory conduitTransfers = new ConduitTransfer[]( + sumOfItemsAcrossAllTransfers + ); + + // Declare an index for storing ConduitTransfers in conduitTransfers. + uint256 itemIndex; + + // Skip overflow checks: all for loops are indexed starting at zero. + unchecked { + // Iterate over each transfer. + for (uint256 i = 0; i < numTransfers; ++i) { + // Retrieve the transfer in question. + TransferHelperItemsWithRecipient calldata transfer = transfers[ + i + ]; + + // Retrieve the items of the transfer in question. + TransferHelperItem[] calldata transferItems = transfer.items; + + // Ensure recipient is not the zero address. + _checkRecipientIsNotZeroAddress(transfer.recipient); - // Skip overflow checks: all for loops are indexed starting at zero. - unchecked { - // Iterate over each transfer. - for (uint256 i = 0; i < totalTransfers; ++i) { - // Retrieve the transfer in question. - TransferHelperItem calldata item = items[i]; + // Create a boolean indicating whether validateERC721Receiver + // is true and recipient is a contract. + bool callERC721Receiver = transfer.validateERC721Receiver && + transfer.recipient.code.length != 0; + + // Retrieve the total number of items in the transfer and + // place on stack. + uint256 numItemsInTransfer = transferItems.length; + + // Iterate over each item in the transfer to create a + // corresponding ConduitTransfer. + for (uint256 j = 0; j < numItemsInTransfer; ++j) { + // Retrieve the item from the transfer. + TransferHelperItem calldata item = transferItems[j]; + + if (item.itemType == ConduitItemType.ERC20) { + // Ensure that the identifier of an ERC20 token is 0. + if (item.identifier != 0) { + revert InvalidERC20Identifier(); + } + } + + // If the item is an ERC721 token and + // callERC721Receiver is true... + if (item.itemType == ConduitItemType.ERC721) { + if (callERC721Receiver) { + // Check if the recipient implements + // onERC721Received for the given tokenId. + _checkERC721Receiver( + conduit, + transfer.recipient, + item.identifier + ); + } + } // Create a ConduitTransfer corresponding to each // TransferHelperItem. - conduitTransfers[i] = ConduitTransfer( + conduitTransfers[itemIndex] = ConduitTransfer( item.itemType, item.token, msg.sender, - recipient, + transfer.recipient, item.identifier, item.amount ); + + // Increment the index for storing ConduitTransfers. + ++itemIndex; + } + } + } + + // Attempt the external call to transfer tokens via the derived conduit. + try ConduitInterface(conduit).execute(conduitTransfers) returns ( + bytes4 conduitMagicValue + ) { + // Check if the value returned from the external call matches + // the conduit `execute` selector. + if (conduitMagicValue != ConduitInterface.execute.selector) { + // If the external call fails, revert with the conduit key + // and conduit address. + revert InvalidConduit(conduitKey, conduit); + } + } catch Error(string memory reason) { + // Catch reverts with a provided reason string and + // revert with the reason, conduit key and conduit address. + revert ConduitErrorRevertString(reason, conduitKey, conduit); + } catch (bytes memory data) { + // Conduits will throw a custom error when attempting to transfer + // native token item types or an ERC721 item amount other than 1. + // Bubble up these custom errors when encountered. Note that the + // conduit itself will bubble up revert reasons from transfers as + // well, meaning that these errors are not necessarily indicative of + // an issue with the item type or amount in cases where the same + // custom error signature is encountered during a conduit transfer. + + // Set initial value of first four bytes of revert data to the mask. + bytes4 customErrorSelector = bytes4(0xffffffff); + + // Utilize assembly to read first four bytes (if present) directly. + assembly { + // Combine original mask with first four bytes of revert data. + customErrorSelector := and( + mload(add(data, 0x20)), // Data begins after length offset. + customErrorSelector + ) + } + + // Pass through the custom error in question if the revert data is + // the correct length and matches an expected custom error selector. + if ( + data.length == 4 && + customErrorSelector == InvalidItemType.selector + ) { + // "Bubble up" the revert reason. + assembly { + revert(add(data, 0x20), 0x04) + } + } else if ( + data.length == 36 && + customErrorSelector == InvalidERC721TransferAmount.selector + ) { + // "Bubble up" the revert reason. + assembly { + revert(add(data, 0x20), 0x24) } } - // Call the conduit and execute bulk transfers. - ConduitInterface(conduit).execute(conduitTransfers); + // Catch all other reverts from the external call to the conduit and + // include the conduit's raw revert reason as a data argument to a + // new custom error. + revert ConduitErrorRevertBytes(data, conduitKey, conduit); } + } - // Return a magic value indicating that the transfers were performed. - magicValue = this.bulkTransfer.selector; + /** + * @notice An internal function to check if a recipient address implements + * onERC721Received for a given tokenId. Note that this check does + * not adhere to the safe transfer specification and is only meant + * to provide an additional layer of assurance that the recipient + * can receive the tokens — any hooks or post-transfer checks will + * fail and the caller will be the transfer helper rather than the + * ERC721 contract. Note that the conduit is set as the operator, as + * it will be the caller once the transfer is performed. + * + * @param conduit The conduit to provide as the operator when calling + * onERC721Received. + * @param recipient The ERC721 recipient on which to call onERC721Received. + * @param tokenId The ERC721 tokenId of the token being transferred. + */ + function _checkERC721Receiver( + address conduit, + address recipient, + uint256 tokenId + ) internal { + // Check if recipient can receive ERC721 tokens. + try + IERC721Receiver(recipient).onERC721Received( + conduit, + msg.sender, + tokenId, + "" + ) + returns (bytes4 selector) { + // Check if onERC721Received selector is valid. + if (selector != IERC721Receiver.onERC721Received.selector) { + // Revert if recipient cannot accept + // ERC721 tokens. + revert InvalidERC721Recipient(recipient); + } + } catch (bytes memory data) { + // "Bubble up" recipient's revert reason. + revert ERC721ReceiverErrorRevertBytes( + data, + recipient, + msg.sender, + tokenId + ); + } catch Error(string memory reason) { + // "Bubble up" recipient's revert reason. + revert ERC721ReceiverErrorRevertString( + reason, + recipient, + msg.sender, + tokenId + ); + } + } + + /** + * @notice An internal function that reverts if the passed-in recipient + * is the zero address. + * + * @param recipient The recipient on which to perform the check. + */ + function _checkRecipientIsNotZeroAddress(address recipient) internal pure { + // Revert if the recipient is the zero address. + if (recipient == address(0x0)) { + revert RecipientCannotBeZeroAddress(); + } } } diff --git a/contracts/helpers/TransferHelperStructs.sol b/contracts/helpers/TransferHelperStructs.sol index 35aeec140..b58bee2c4 100644 --- a/contracts/helpers/TransferHelperStructs.sol +++ b/contracts/helpers/TransferHelperStructs.sol @@ -1,11 +1,29 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; import { ConduitItemType } from "../conduit/lib/ConduitEnums.sol"; +/** + * @dev A TransferHelperItem specifies the itemType (ERC20/ERC721/ERC1155), + * token address, token identifier, and amount of the token to be + * transferred via the TransferHelper. For ERC20 tokens, identifier + * must be 0. For ERC721 tokens, amount must be 1. + */ struct TransferHelperItem { ConduitItemType itemType; address token; uint256 identifier; uint256 amount; } + +/** + * @dev A TransferHelperItemsWithRecipient specifies the tokens to transfer + * via the TransferHelper, their intended recipient, and a boolean flag + * indicating whether onERC721Received should be called on a recipient + * contract. + */ +struct TransferHelperItemsWithRecipient { + TransferHelperItem[] items; + address recipient; + bool validateERC721Receiver; +} diff --git a/contracts/interfaces/AbridgedTokenInterfaces.sol b/contracts/interfaces/AbridgedTokenInterfaces.sol index fc39bdae0..914279c7b 100644 --- a/contracts/interfaces/AbridgedTokenInterfaces.sol +++ b/contracts/interfaces/AbridgedTokenInterfaces.sol @@ -1,23 +1,88 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; +/** + * @title ERC20Interface + * @notice Contains the minimum interfaces needed to interact with ERC20s. + */ interface ERC20Interface { + /** + * @dev Allows an operator to transfer tokens on behalf of an owner. + * + * @param from The address of the owner. + * @param to The address of the recipient. + * @param value The amount of tokens to transfer. + * + * @return success True if the transfer was successful. + */ function transferFrom( - address, - address, - uint256 - ) external returns (bool); + address from, + address to, + uint256 value + ) external returns (bool success); + + /** + * @dev Allows an operator to approve a spender to transfer tokens on behalf + * of a user. + * + * @param spender The address of the spender. + * @param value The amount of tokens to approve. + * + * @return success True if the approval was successful. + */ + function approve( + address spender, + uint256 value + ) external returns (bool success); } +/** + * @title ERC721Interface + * @notice Contains the minimum interfaces needed to interact with ERC721s. + */ interface ERC721Interface { - function transferFrom( - address, - address, - uint256 - ) external; + /** + * @dev Allows an operator to transfer tokens on behalf of an owner. + * + * @param from The address of the owner. + * @param to The address of the recipient. + * @param tokenId The ID of the token to transfer. + */ + function transferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Allows an owner to approve an operator to transfer all tokens on a + * contract on behalf of the owner. + * + * @param to The address of the operator. + * @param approved Whether the operator is approved. + */ + function setApprovalForAll(address to, bool approved) external; + + /** + * @dev Returns the owner of a given token ID. + * + * @param tokenId The token ID. + * + * @return owner The owner of the token. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); } +/** + * @title ERC1155Interface + * @notice Contains the minimum interfaces needed to interact with ERC1155s. + */ interface ERC1155Interface { + /** + * @dev Allows an operator to transfer tokens on behalf of an owner. + * + * @param from The address of the owner. + * @param to The address of the recipient. + * @param id The ID of the token(s) to transfer. + * @param amount The amount of tokens to transfer. + * @param data Additional data. + */ function safeTransferFrom( address from, address to, @@ -26,6 +91,15 @@ interface ERC1155Interface { bytes calldata data ) external; + /** + * @dev Allows an operator to transfer tokens on behalf of an owner. + * + * @param from The address of the owner. + * @param to The address of the recipient. + * @param ids The IDs of the token(s) to transfer. + * @param amounts The amounts of tokens to transfer. + * @param data Additional data. + */ function safeBatchTransferFrom( address from, address to, @@ -33,4 +107,13 @@ interface ERC1155Interface { uint256[] calldata amounts, bytes calldata data ) external; + + /** + * @dev Allows an owner to approve an operator to transfer all tokens on a + * contract on behalf of the owner. + * + * @param to The address of the operator. + * @param approved Whether the operator is approved. + */ + function setApprovalForAll(address to, bool approved) external; } diff --git a/contracts/interfaces/AmountDerivationErrors.sol b/contracts/interfaces/AmountDerivationErrors.sol index 718809956..31b62e5b8 100644 --- a/contracts/interfaces/AmountDerivationErrors.sol +++ b/contracts/interfaces/AmountDerivationErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title AmountDerivationErrors diff --git a/contracts/interfaces/ConduitControllerInterface.sol b/contracts/interfaces/ConduitControllerInterface.sol index 83561ae54..6de8f8885 100644 --- a/contracts/interfaces/ConduitControllerInterface.sol +++ b/contracts/interfaces/ConduitControllerInterface.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title ConduitControllerInterface @@ -130,9 +130,10 @@ interface ConduitControllerInterface { * * @return conduit The address of the newly deployed conduit. */ - function createConduit(bytes32 conduitKey, address initialOwner) - external - returns (address conduit); + function createConduit( + bytes32 conduitKey, + address initialOwner + ) external returns (address conduit); /** * @notice Open or close a channel on a given conduit, thereby allowing the @@ -161,8 +162,10 @@ interface ConduitControllerInterface { * @param conduit The conduit for which to initiate ownership transfer. * @param newPotentialOwner The new potential owner of the conduit. */ - function transferOwnership(address conduit, address newPotentialOwner) - external; + function transferOwnership( + address conduit, + address newPotentialOwner + ) external; /** * @notice Clear the currently set potential owner, if any, from a conduit. @@ -212,10 +215,9 @@ interface ConduitControllerInterface { * @return exists A boolean indicating whether the derived conduit has been * deployed or not. */ - function getConduit(bytes32 conduitKey) - external - view - returns (address conduit, bool exists); + function getConduit( + bytes32 conduitKey + ) external view returns (address conduit, bool exists); /** * @notice Retrieve the potential owner, if any, for a given conduit. The @@ -227,10 +229,9 @@ interface ConduitControllerInterface { * * @return potentialOwner The potential owner, if any, for the conduit. */ - function getPotentialOwner(address conduit) - external - view - returns (address potentialOwner); + function getPotentialOwner( + address conduit + ) external view returns (address potentialOwner); /** * @notice Retrieve the status (either open or closed) of a given channel on @@ -241,10 +242,10 @@ interface ConduitControllerInterface { * * @return isOpen The status of the channel on the given conduit. */ - function getChannelStatus(address conduit, address channel) - external - view - returns (bool isOpen); + function getChannelStatus( + address conduit, + address channel + ) external view returns (bool isOpen); /** * @notice Retrieve the total number of open channels for a given conduit. @@ -253,10 +254,9 @@ interface ConduitControllerInterface { * * @return totalChannels The total number of open channels for the conduit. */ - function getTotalChannels(address conduit) - external - view - returns (uint256 totalChannels); + function getTotalChannels( + address conduit + ) external view returns (uint256 totalChannels); /** * @notice Retrieve an open channel at a specific index for a given conduit. @@ -268,10 +268,10 @@ interface ConduitControllerInterface { * * @return channel The open channel, if any, at the specified channel index. */ - function getChannel(address conduit, uint256 channelIndex) - external - view - returns (address channel); + function getChannel( + address conduit, + uint256 channelIndex + ) external view returns (address channel); /** * @notice Retrieve all open channels for a given conduit. Note that calling @@ -282,10 +282,9 @@ interface ConduitControllerInterface { * * @return channels An array of open channels on the given conduit. */ - function getChannels(address conduit) - external - view - returns (address[] memory channels); + function getChannels( + address conduit + ) external view returns (address[] memory channels); /** * @dev Retrieve the conduit creation code and runtime code hashes. diff --git a/contracts/interfaces/ConduitInterface.sol b/contracts/interfaces/ConduitInterface.sol index d988fcc83..113b464ba 100644 --- a/contracts/interfaces/ConduitInterface.sol +++ b/contracts/interfaces/ConduitInterface.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { - ConduitTransfer, - ConduitBatch1155Transfer + ConduitBatch1155Transfer, + ConduitTransfer } from "../conduit/lib/ConduitStructs.sol"; /** @@ -55,9 +54,9 @@ interface ConduitInterface { * @return magicValue A magic value indicating that the transfers were * performed successfully. */ - function execute(ConduitTransfer[] calldata transfers) - external - returns (bytes4 magicValue); + function execute( + ConduitTransfer[] calldata transfers + ) external returns (bytes4 magicValue); /** * @notice Execute a sequence of batch 1155 transfers. Only a caller with an diff --git a/contracts/interfaces/ConsiderationEventsAndErrors.sol b/contracts/interfaces/ConsiderationEventsAndErrors.sol index 13682ecda..b13abf590 100644 --- a/contracts/interfaces/ConsiderationEventsAndErrors.sol +++ b/contracts/interfaces/ConsiderationEventsAndErrors.sol @@ -1,7 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -import { SpentItem, ReceivedItem } from "../lib/ConsiderationStructs.sol"; +import { + OrderParameters, + ReceivedItem, + SpentItem +} from "../lib/ConsiderationStructs.sol"; /** * @title ConsiderationEventsAndErrors @@ -51,21 +55,24 @@ interface ConsiderationEventsAndErrors { * this event will not be emitted on partial fills even though they do * validate the order as part of partial fulfillment. * - * @param orderHash The hash of the validated order. - * @param offerer The offerer of the validated order. - * @param zone The zone of the validated order. + * @param orderHash The hash of the validated order. + * @param orderParameters The parameters of the validated order. */ - event OrderValidated( - bytes32 orderHash, - address indexed offerer, - address indexed zone - ); + event OrderValidated(bytes32 orderHash, OrderParameters orderParameters); + + /** + * @dev Emit an event whenever one or more orders are matched using either + * matchOrders or matchAdvancedOrders. + * + * @param orderHashes The order hashes of the matched orders. + */ + event OrdersMatched(bytes32[] orderHashes); /** * @dev Emit an event whenever a counter for a given offerer is incremented. * * @param newCounter The new counter for the offerer. - * @param offerer The offerer in question. + * @param offerer The offerer in question. */ event CounterIncremented(uint256 newCounter, address indexed offerer); @@ -80,8 +87,11 @@ interface ConsiderationEventsAndErrors { /** * @dev Revert with an error when attempting to fill an order outside the * specified start time and end time. + * + * @param startTime The time at which the order becomes active. + * @param endTime The time at which the order becomes inactive. */ - error InvalidTime(); + error InvalidTime(uint256 startTime, uint256 endTime); /** * @dev Revert with an error when attempting to fill an order referencing an @@ -95,6 +105,15 @@ interface ConsiderationEventsAndErrors { */ error MissingOriginalConsiderationItems(); + /** + * @dev Revert with an error when an order is validated and the length of + * the consideration array is not equal to the supplied total original + * consideration items value. This error is also thrown when contract + * orders supply a total original consideration items value that does + * not match the supplied consideration array length. + */ + error ConsiderationLengthNotEqualToTotalOriginal(); + /** * @dev Revert with an error when a call to a conduit fails with revert data * that is too expensive to return. @@ -118,15 +137,15 @@ interface ConsiderationEventsAndErrors { ); /** - * @dev Revert with an error when insufficient ether is supplied as part of - * msg.value when fulfilling orders. + * @dev Revert with an error when insufficient native tokens are supplied as + * part of msg.value when fulfilling orders. */ - error InsufficientEtherSupplied(); + error InsufficientNativeTokensSupplied(); /** - * @dev Revert with an error when an ether transfer reverts. + * @dev Revert with an error when a native token transfer reverts. */ - error EtherTransferGenericFailure(address account, uint256 amount); + error NativeTokenTransferGenericFailure(address account, uint256 amount); /** * @dev Revert with an error when a partial fill is attempted on an order @@ -152,9 +171,10 @@ interface ConsiderationEventsAndErrors { /** * @dev Revert with an error when attempting to cancel an order as a caller - * other than the indicated offerer or zone. + * other than the indicated offerer or zone or when attempting to + * cancel a contract order. */ - error InvalidCanceller(); + error CannotCancelOrder(); /** * @dev Revert with an error when supplying a fraction with a value of zero @@ -184,7 +204,7 @@ interface ConsiderationEventsAndErrors { /** * @dev Revert with an error when attempting to fulfill an order with an - * offer for ETH outside of matching orders. + * offer for a native token outside of matching orders. */ error InvalidNativeOfferItem(); } diff --git a/contracts/interfaces/ConsiderationInterface.sol b/contracts/interfaces/ConsiderationInterface.sol index 1c6d5c9fc..4132fad1b 100644 --- a/contracts/interfaces/ConsiderationInterface.sol +++ b/contracts/interfaces/ConsiderationInterface.sol @@ -1,27 +1,25 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { + AdvancedOrder, BasicOrderParameters, - OrderComponents, + CriteriaResolver, + Execution, Fulfillment, FulfillmentComponent, - Execution, Order, - AdvancedOrder, - OrderStatus, - CriteriaResolver + OrderComponents } from "../lib/ConsiderationStructs.sol"; /** * @title ConsiderationInterface * @author 0age - * @custom:version 1.1 - * @notice Consideration is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. - * It minimizes external calls to the greatest extent possible and - * provides lightweight methods for common routes as well as more - * flexible methods for composing advanced orders. + * @custom:version 1.2 + * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155 + * marketplace. It minimizes external calls to the greatest extent + * possible and provides lightweight methods for common routes as well + * as more flexible methods for composing advanced orders. * * @dev ConsiderationInterface contains all external function interfaces for * Consideration. @@ -42,10 +40,9 @@ interface ConsiderationInterface { * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillBasicOrder(BasicOrderParameters calldata parameters) - external - payable - returns (bool fulfilled); + function fulfillBasicOrder( + BasicOrderParameters calldata parameters + ) external payable returns (bool fulfilled); /** * @notice Fulfill an order with an arbitrary number of items for offer and @@ -69,10 +66,10 @@ interface ConsiderationInterface { * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey) - external - payable - returns (bool fulfilled); + function fulfillOrder( + Order calldata order, + bytes32 fulfillerConduitKey + ) external payable returns (bool fulfilled); /** * @notice Fill an order, fully or partially, with an arbitrary number of @@ -162,7 +159,9 @@ interface ConsiderationInterface { * returned boolean was fulfillable or not. * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of + * this array. */ function fulfillAvailableOrders( Order[] calldata orders, @@ -237,7 +236,9 @@ interface ConsiderationInterface { * returned boolean was fulfillable or not. * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of + * this array. */ function fulfillAvailableAdvancedOrders( AdvancedOrder[] calldata advancedOrders, @@ -254,11 +255,13 @@ interface ConsiderationInterface { /** * @notice Match an arbitrary number of orders, each with an arbitrary - * number of items for offer and consideration along with as set of + * number of items for offer and consideration along with a set of * fulfillments allocating offer components to consideration * components. Note that this function does not support * criteria-based or partial filling of orders (though filling the - * remainder of a partially-filled order is supported). + * remainder of a partially-filled order is supported). Any unspent + * offer item amounts or native tokens will be transferred to the + * caller. * * @param orders The orders to match. Note that both the offerer and * fulfiller on each order must first approve this @@ -273,7 +276,9 @@ interface ConsiderationInterface { * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of this + * array. */ function matchOrders( Order[] calldata orders, @@ -285,7 +290,10 @@ interface ConsiderationInterface { * arbitrary number of items for offer and consideration, supplying * criteria resolvers containing specific token identifiers and * associated proofs as well as fulfillments allocating offer - * components to consideration components. + * components to consideration components. Any unspent offer item + * amounts will be transferred to the designated recipient (with the + * null address signifying to use the caller) and any unspent native + * tokens will be returned to the caller. * * @param orders The advanced orders to match. Note that both the * offerer and fulfiller on each order must first @@ -311,15 +319,20 @@ interface ConsiderationInterface { * to consideration components. Note that each * consideration component must be fully met in * order for the match operation to be valid. + * @param recipient The intended recipient for all unspent offer + * item amounts, or the caller if the null address + * is supplied. * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or native + * tokens will not be reflected as part of this array. */ function matchAdvancedOrders( AdvancedOrder[] calldata orders, CriteriaResolver[] calldata criteriaResolvers, - Fulfillment[] calldata fulfillments + Fulfillment[] calldata fulfillments, + address recipient ) external payable returns (Execution[] memory executions); /** @@ -333,9 +346,9 @@ interface ConsiderationInterface { * @return cancelled A boolean indicating whether the supplied orders have * been successfully cancelled. */ - function cancel(OrderComponents[] calldata orders) - external - returns (bool cancelled); + function cancel( + OrderComponents[] calldata orders + ) external returns (bool cancelled); /** * @notice Validate an arbitrary number of orders, thereby registering their @@ -352,9 +365,9 @@ interface ConsiderationInterface { * @return validated A boolean indicating whether the supplied orders have * been successfully validated. */ - function validate(Order[] calldata orders) - external - returns (bool validated); + function validate( + Order[] calldata orders + ) external returns (bool validated); /** * @notice Cancel all orders from a given offerer with a given zone in bulk @@ -365,6 +378,28 @@ interface ConsiderationInterface { */ function incrementCounter() external returns (uint256 newCounter); + /** + * @notice Fulfill an order offering an ERC721 token by supplying Ether (or + * the native token for the given chain) as consideration for the + * order. An arbitrary number of "additional recipients" may also be + * supplied which will each receive native tokens from the fulfiller + * as consideration. Note that this function costs less gas than + * `fulfillBasicOrder` due to the zero bytes in the function + * selector (0x00000000) which also results in earlier function + * dispatch. + * + * @param parameters Additional information on the fulfilled order. Note + * that the offerer must first approve this contract (or + * their preferred conduit if indicated by the order) for + * their offered ERC721 token to be transferred. + * + * @return fulfilled A boolean indicating whether the order has been + * successfully fulfilled. + */ + function fulfillBasicOrder_efficient_6GL6yc( + BasicOrderParameters calldata parameters + ) external payable returns (bool fulfilled); + /** * @notice Retrieve the order hash for a given order. * @@ -372,10 +407,9 @@ interface ConsiderationInterface { * * @return orderHash The order hash. */ - function getOrderHash(OrderComponents calldata order) - external - view - returns (bytes32 orderHash); + function getOrderHash( + OrderComponents calldata order + ) external view returns (bytes32 orderHash); /** * @notice Retrieve the status of a given order by hash, including whether @@ -394,7 +428,9 @@ interface ConsiderationInterface { * @return totalSize The total size of the order that is either filled or * unfilled (i.e. the "denominator"). */ - function getOrderStatus(bytes32 orderHash) + function getOrderStatus( + bytes32 orderHash + ) external view returns ( @@ -411,10 +447,9 @@ interface ConsiderationInterface { * * @return counter The current counter. */ - function getCounter(address offerer) - external - view - returns (uint256 counter); + function getCounter( + address offerer + ) external view returns (uint256 counter); /** * @notice Retrieve configuration information for this contract. @@ -432,6 +467,10 @@ interface ConsiderationInterface { address conduitController ); + function getContractOffererNonce( + address contractOfferer + ) external view returns (uint256 nonce); + /** * @notice Retrieve the name of this contract. * diff --git a/contracts/interfaces/ContractOffererInterface.sol b/contracts/interfaces/ContractOffererInterface.sol new file mode 100644 index 000000000..e38823cfa --- /dev/null +++ b/contracts/interfaces/ContractOffererInterface.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ReceivedItem, + Schema, + SpentItem +} from "../lib/ConsiderationStructs.sol"; + +/** + * @title ContractOffererInterface + * @notice Contains the minimum interfaces needed to interact with a contract + * offerer. + */ +interface ContractOffererInterface { + /** + * @dev Generates an order with the specified minimum and maximum spent + * items, and optional context (supplied as extraData). + * + * @param fulfiller The address of the fulfiller. + * @param minimumReceived The minimum items that the caller is willing to + * receive. + * @param maximumSpent The maximum items the caller is willing to spend. + * @param context Additional context of the order. + * + * @return offer A tuple containing the offer items. + * @return consideration A tuple containing the consideration items. + */ + function generateOrder( + address fulfiller, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata context // encoded based on the schemaID + ) + external + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration); + + /** + * @dev Ratifies an order with the specified offer, consideration, and + * optional context (supplied as extraData). + * + * @param offer The offer items. + * @param consideration The consideration items. + * @param context Additional context of the order. + * @param orderHashes The hashes to ratify. + * @param contractNonce The nonce of the contract. + * + * @return ratifyOrderMagicValue The magic value returned by the contract + * offerer. + */ + function ratifyOrder( + SpentItem[] calldata offer, + ReceivedItem[] calldata consideration, + bytes calldata context, // encoded based on the schemaID + bytes32[] calldata orderHashes, + uint256 contractNonce + ) external returns (bytes4 ratifyOrderMagicValue); + + /** + * @dev View function to preview an order generated in response to a minimum + * set of received items, maximum set of spent items, and context + * (supplied as extraData). + * + * @param caller The address of the caller (e.g. Seaport). + * @param fulfiller The address of the fulfiller (e.g. the account + * calling Seaport). + * @param minimumReceived The minimum items that the caller is willing to + * receive. + * @param maximumSpent The maximum items the caller is willing to spend. + * @param context Additional context of the order. + * + * @return offer A tuple containing the offer items. + * @return consideration A tuple containing the consideration items. + */ + function previewOrder( + address caller, + address fulfiller, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata context // encoded based on the schemaID + ) + external + view + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration); + + /** + * @dev Gets the metadata for this contract offerer. + * + * @return name The name of the contract offerer. + * @return schemas The schemas supported by the contract offerer. + */ + function getSeaportMetadata() + external + view + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ); + + // Additional functions and/or events based on implemented schemaIDs +} diff --git a/contracts/interfaces/CriteriaResolutionErrors.sol b/contracts/interfaces/CriteriaResolutionErrors.sol index dfbe88c26..641032eec 100644 --- a/contracts/interfaces/CriteriaResolutionErrors.sol +++ b/contracts/interfaces/CriteriaResolutionErrors.sol @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; + +import { Side } from "../lib/ConsiderationEnums.sol"; /** * @title CriteriaResolutionErrors @@ -11,20 +13,34 @@ interface CriteriaResolutionErrors { /** * @dev Revert with an error when providing a criteria resolver that refers * to an order that has not been supplied. + * + * @param side The side of the order that was not supplied. */ - error OrderCriteriaResolverOutOfRange(); + error OrderCriteriaResolverOutOfRange(Side side); /** * @dev Revert with an error if an offer item still has unresolved criteria * after applying all criteria resolvers. + * + * @param orderIndex The index of the order that contains the offer item. + * @param offerIndex The index of the offer item that still has unresolved + * criteria. */ - error UnresolvedOfferCriteria(); + error UnresolvedOfferCriteria(uint256 orderIndex, uint256 offerIndex); /** * @dev Revert with an error if a consideration item still has unresolved * criteria after applying all criteria resolvers. + * + * @param orderIndex The index of the order that contains the + * consideration item. + * @param considerationIndex The index of the consideration item that still + * has unresolved criteria. */ - error UnresolvedConsiderationCriteria(); + error UnresolvedConsiderationCriteria( + uint256 orderIndex, + uint256 considerationIndex + ); /** * @dev Revert with an error when providing a criteria resolver that refers diff --git a/contracts/interfaces/EIP1271Interface.sol b/contracts/interfaces/EIP1271Interface.sol index 21c9cf1c6..cbc54ea4f 100644 --- a/contracts/interfaces/EIP1271Interface.sol +++ b/contracts/interfaces/EIP1271Interface.sol @@ -1,9 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; +/** + * @title EIP1271Interface + * @notice Interface for the EIP-1271 standard signature validation method for + * contracts. + */ interface EIP1271Interface { - function isValidSignature(bytes32 digest, bytes calldata signature) - external - view - returns (bytes4); + /** + * @dev Validates a smart contract signature + * + * @param digest bytes32 The digest of the data to be signed. + * @param signature bytes The signature of the data to be validated. + * + * @return bytes4 The magic value, if the signature is valid. + */ + function isValidSignature( + bytes32 digest, + bytes calldata signature + ) external view returns (bytes4); } diff --git a/contracts/interfaces/FulfillmentApplicationErrors.sol b/contracts/interfaces/FulfillmentApplicationErrors.sol index 776a0f6be..6cd5bab8a 100644 --- a/contracts/interfaces/FulfillmentApplicationErrors.sol +++ b/contracts/interfaces/FulfillmentApplicationErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; import { Side } from "../lib/ConsiderationEnums.sol"; @@ -28,8 +28,13 @@ interface FulfillmentApplicationErrors { * @dev Revert with an error when the initial offer item named by a * fulfillment component does not match the type, token, identifier, * or conduit preference of the initial consideration item. + * + * @param fulfillmentIndex The index of the fulfillment component that + * does not match the initial offer item. */ - error MismatchedFulfillmentOfferAndConsiderationComponents(); + error MismatchedFulfillmentOfferAndConsiderationComponents( + uint256 fulfillmentIndex + ); /** * @dev Revert with an error when an order or item index are out of range diff --git a/contracts/interfaces/IERC721Receiver.sol b/contracts/interfaces/IERC721Receiver.sol new file mode 100644 index 000000000..01027abd1 --- /dev/null +++ b/contracts/interfaces/IERC721Receiver.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an ERC721 token is transferred to this contract via + * safeTransferFrom, this function is called. + * + * @param operator The address of the operator. + * @param from The address of the sender. + * @param tokenId The ID of the ERC721. + * @param data Additional data. + * + * @return bytes4 The magic value, unless throwing. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} diff --git a/contracts/interfaces/ImmutableCreate2FactoryInterface.sol b/contracts/interfaces/ImmutableCreate2FactoryInterface.sol index a8a34a590..a1bd08a0e 100644 --- a/contracts/interfaces/ImmutableCreate2FactoryInterface.sol +++ b/contracts/interfaces/ImmutableCreate2FactoryInterface.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title ImmutableCreate2FactoryInterface @@ -30,10 +30,10 @@ interface ImmutableCreate2FactoryInterface { * * @return deploymentAddress Address of the contract that will be created. */ - function safeCreate2(bytes32 salt, bytes calldata initializationCode) - external - payable - returns (address deploymentAddress); + function safeCreate2( + bytes32 salt, + bytes calldata initializationCode + ) external payable returns (address deploymentAddress); /** * @dev Compute the address of the contract that will be created when @@ -53,10 +53,10 @@ interface ImmutableCreate2FactoryInterface { * or the null address if a contract already * exists at that address. */ - function findCreate2Address(bytes32 salt, bytes calldata initCode) - external - view - returns (address deploymentAddress); + function findCreate2Address( + bytes32 salt, + bytes calldata initCode + ) external view returns (address deploymentAddress); /** * @dev Compute the address of the contract that will be created when @@ -77,10 +77,10 @@ interface ImmutableCreate2FactoryInterface { * or the null address if a contract already * exists at that address. */ - function findCreate2AddressViaHash(bytes32 salt, bytes32 initCodeHash) - external - view - returns (address deploymentAddress); + function findCreate2AddressViaHash( + bytes32 salt, + bytes32 initCodeHash + ) external view returns (address deploymentAddress); /** * @dev Determine if a contract has already been deployed by the factory to @@ -90,8 +90,7 @@ interface ImmutableCreate2FactoryInterface { * * @return True if the contract has been deployed, false otherwise. */ - function hasBeenDeployed(address deploymentAddress) - external - view - returns (bool); + function hasBeenDeployed( + address deploymentAddress + ) external view returns (bool); } diff --git a/contracts/interfaces/ReentrancyErrors.sol b/contracts/interfaces/ReentrancyErrors.sol index 042654f5c..9e3685f1d 100644 --- a/contracts/interfaces/ReentrancyErrors.sol +++ b/contracts/interfaces/ReentrancyErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title ReentrancyErrors diff --git a/contracts/interfaces/SeaportInterface.sol b/contracts/interfaces/SeaportInterface.sol index 6593f8658..f44a3d0d7 100644 --- a/contracts/interfaces/SeaportInterface.sol +++ b/contracts/interfaces/SeaportInterface.sol @@ -1,27 +1,25 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { + AdvancedOrder, BasicOrderParameters, - OrderComponents, + CriteriaResolver, + Execution, Fulfillment, FulfillmentComponent, - Execution, Order, - AdvancedOrder, - OrderStatus, - CriteriaResolver + OrderComponents } from "../lib/ConsiderationStructs.sol"; /** * @title SeaportInterface * @author 0age - * @custom:version 1.1 - * @notice Seaport is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. It - * minimizes external calls to the greatest extent possible and provides - * lightweight methods for common routes as well as more flexible - * methods for composing advanced orders. + * @custom:version 1.2 + * @notice Seaport is a generalized native token/ERC20/ERC721/ERC1155 + * marketplace. It minimizes external calls to the greatest extent + * possible and provides lightweight methods for common routes as well + * as more flexible methods for composing advanced orders. * * @dev SeaportInterface contains all external function interfaces for Seaport. */ @@ -41,10 +39,9 @@ interface SeaportInterface { * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillBasicOrder(BasicOrderParameters calldata parameters) - external - payable - returns (bool fulfilled); + function fulfillBasicOrder( + BasicOrderParameters calldata parameters + ) external payable returns (bool fulfilled); /** * @notice Fulfill an order with an arbitrary number of items for offer and @@ -68,10 +65,10 @@ interface SeaportInterface { * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey) - external - payable - returns (bool fulfilled); + function fulfillOrder( + Order calldata order, + bytes32 fulfillerConduitKey + ) external payable returns (bool fulfilled); /** * @notice Fill an order, fully or partially, with an arbitrary number of @@ -161,7 +158,9 @@ interface SeaportInterface { * returned boolean was fulfillable or not. * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of + * this array. */ function fulfillAvailableOrders( Order[] calldata orders, @@ -236,7 +235,9 @@ interface SeaportInterface { * returned boolean was fulfillable or not. * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of + * this array. */ function fulfillAvailableAdvancedOrders( AdvancedOrder[] calldata advancedOrders, @@ -253,11 +254,13 @@ interface SeaportInterface { /** * @notice Match an arbitrary number of orders, each with an arbitrary - * number of items for offer and consideration along with as set of + * number of items for offer and consideration along with a set of * fulfillments allocating offer components to consideration * components. Note that this function does not support * criteria-based or partial filling of orders (though filling the - * remainder of a partially-filled order is supported). + * remainder of a partially-filled order is supported). Any unspent + * offer item amounts or native tokens will be transferred to the + * caller. * * @param orders The orders to match. Note that both the offerer and * fulfiller on each order must first approve this @@ -272,7 +275,9 @@ interface SeaportInterface { * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of this + * array. */ function matchOrders( Order[] calldata orders, @@ -284,7 +289,10 @@ interface SeaportInterface { * arbitrary number of items for offer and consideration, supplying * criteria resolvers containing specific token identifiers and * associated proofs as well as fulfillments allocating offer - * components to consideration components. + * components to consideration components. Any unspent offer item + * amounts will be transferred to the designated recipient (with the + * null address signifying to use the caller) and any unspent native + * tokens will be returned to the caller. * * @param orders The advanced orders to match. Note that both the * offerer and fulfiller on each order must first @@ -310,15 +318,20 @@ interface SeaportInterface { * to consideration components. Note that each * consideration component must be fully met in * order for the match operation to be valid. + * @param recipient The intended recipient for all unspent offer + * item amounts, or the caller if the null address + * is supplied. * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or native + * tokens will not be reflected as part of this array. */ function matchAdvancedOrders( AdvancedOrder[] calldata orders, CriteriaResolver[] calldata criteriaResolvers, - Fulfillment[] calldata fulfillments + Fulfillment[] calldata fulfillments, + address recipient ) external payable returns (Execution[] memory executions); /** @@ -332,9 +345,9 @@ interface SeaportInterface { * @return cancelled A boolean indicating whether the supplied orders have * been successfully cancelled. */ - function cancel(OrderComponents[] calldata orders) - external - returns (bool cancelled); + function cancel( + OrderComponents[] calldata orders + ) external returns (bool cancelled); /** * @notice Validate an arbitrary number of orders, thereby registering their @@ -351,9 +364,9 @@ interface SeaportInterface { * @return validated A boolean indicating whether the supplied orders have * been successfully validated. */ - function validate(Order[] calldata orders) - external - returns (bool validated); + function validate( + Order[] calldata orders + ) external returns (bool validated); /** * @notice Cancel all orders from a given offerer with a given zone in bulk @@ -364,6 +377,28 @@ interface SeaportInterface { */ function incrementCounter() external returns (uint256 newCounter); + /** + * @notice Fulfill an order offering an ERC721 token by supplying Ether (or + * the native token for the given chain) as consideration for the + * order. An arbitrary number of "additional recipients" may also be + * supplied which will each receive native tokens from the fulfiller + * as consideration. Note that this function costs less gas than + * `fulfillBasicOrder` due to the zero bytes in the function + * selector (0x00000000) which also results in earlier function + * dispatch. + * + * @param parameters Additional information on the fulfilled order. Note + * that the offerer must first approve this contract (or + * their preferred conduit if indicated by the order) for + * their offered ERC721 token to be transferred. + * + * @return fulfilled A boolean indicating whether the order has been + * successfully fulfilled. + */ + function fulfillBasicOrder_efficient_6GL6yc( + BasicOrderParameters calldata parameters + ) external payable returns (bool fulfilled); + /** * @notice Retrieve the order hash for a given order. * @@ -371,10 +406,9 @@ interface SeaportInterface { * * @return orderHash The order hash. */ - function getOrderHash(OrderComponents calldata order) - external - view - returns (bytes32 orderHash); + function getOrderHash( + OrderComponents calldata order + ) external view returns (bytes32 orderHash); /** * @notice Retrieve the status of a given order by hash, including whether @@ -393,7 +427,9 @@ interface SeaportInterface { * @return totalSize The total size of the order that is either filled or * unfilled (i.e. the "denominator"). */ - function getOrderStatus(bytes32 orderHash) + function getOrderStatus( + bytes32 orderHash + ) external view returns ( @@ -410,10 +446,9 @@ interface SeaportInterface { * * @return counter The current counter. */ - function getCounter(address offerer) - external - view - returns (uint256 counter); + function getCounter( + address offerer + ) external view returns (uint256 counter); /** * @notice Retrieve configuration information for this contract. @@ -431,6 +466,10 @@ interface SeaportInterface { address conduitController ); + function getContractOffererNonce( + address contractOfferer + ) external view returns (uint256 nonce); + /** * @notice Retrieve the name of this contract. * diff --git a/contracts/interfaces/SignatureVerificationErrors.sol b/contracts/interfaces/SignatureVerificationErrors.sol index 90a2a4c97..f0fdd04c1 100644 --- a/contracts/interfaces/SignatureVerificationErrors.sol +++ b/contracts/interfaces/SignatureVerificationErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title SignatureVerificationErrors diff --git a/contracts/interfaces/TokenTransferrerErrors.sol b/contracts/interfaces/TokenTransferrerErrors.sol index 21887650b..954d85f12 100644 --- a/contracts/interfaces/TokenTransferrerErrors.sol +++ b/contracts/interfaces/TokenTransferrerErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title TokenTransferrerErrors @@ -8,8 +8,10 @@ interface TokenTransferrerErrors { /** * @dev Revert with an error when an ERC721 transfer with amount other than * one is attempted. + * + * @param amount The amount of the ERC721 tokens to transfer. */ - error InvalidERC721TransferAmount(); + error InvalidERC721TransferAmount(uint256 amount); /** * @dev Revert with an error when attempting to fulfill an order where an diff --git a/contracts/interfaces/TransferHelperErrors.sol b/contracts/interfaces/TransferHelperErrors.sol new file mode 100644 index 000000000..88ffc106a --- /dev/null +++ b/contracts/interfaces/TransferHelperErrors.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/** + * @title TransferHelperErrors + */ +interface TransferHelperErrors { + /** + * @dev Revert with an error when attempting to execute transfers with a + * NATIVE itemType. + */ + error InvalidItemType(); + + /** + * @dev Revert with an error when an ERC721 transfer with amount other than + * one is attempted. + * + * @param amount The amount of the ERC721 tokens to transfer. + */ + error InvalidERC721TransferAmount(uint256 amount); + + /** + * @dev Revert with an error when attempting to execute an ERC721 transfer + * to an invalid recipient. + */ + error InvalidERC721Recipient(address recipient); + + /** + * @dev Revert with an error when a call to an ERC721 receiver reverts with + * bytes data. + */ + error ERC721ReceiverErrorRevertBytes( + bytes reason, + address receiver, + address sender, + uint256 identifier + ); + + /** + * @dev Revert with an error when a call to an ERC721 receiver reverts with + * string reason. + */ + error ERC721ReceiverErrorRevertString( + string reason, + address receiver, + address sender, + uint256 identifier + ); + + /** + * @dev Revert with an error when an ERC20 token has an invalid identifier. + */ + error InvalidERC20Identifier(); + + /** + * @dev Revert with an error if the recipient is the zero address. + */ + error RecipientCannotBeZeroAddress(); + + /** + * @dev Revert with an error when attempting to fill an order referencing an + * invalid conduit (i.e. one that has not been deployed). + */ + error InvalidConduit(bytes32 conduitKey, address conduit); + + /** + * @dev Revert with an error when a call to a conduit reverts with a + * reason string. + */ + error ConduitErrorRevertString( + string reason, + bytes32 conduitKey, + address conduit + ); + + /** + * @dev Revert with an error when a call to a conduit reverts with bytes + * data. + */ + error ConduitErrorRevertBytes( + bytes reason, + bytes32 conduitKey, + address conduit + ); +} diff --git a/contracts/interfaces/TransferHelperInterface.sol b/contracts/interfaces/TransferHelperInterface.sol index c579868fd..c9478f32c 100644 --- a/contracts/interfaces/TransferHelperInterface.sol +++ b/contracts/interfaces/TransferHelperInterface.sol @@ -1,25 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -import { TransferHelperItem } from "../helpers/TransferHelperStructs.sol"; +import { + TransferHelperItemsWithRecipient +} from "../helpers/TransferHelperStructs.sol"; interface TransferHelperInterface { - /** - * @dev Revert with an error when attempting to execute transfers with a - * NATIVE itemType. - */ - error InvalidItemType(); - /** * @notice Transfer multiple items to a single recipient. * * @param items The items to transfer. - * @param recipient The address the items should be transferred to. * @param conduitKey The key of the conduit performing the bulk transfer. */ function bulkTransfer( - TransferHelperItem[] calldata items, - address recipient, + TransferHelperItemsWithRecipient[] calldata items, bytes32 conduitKey ) external returns (bytes4); } diff --git a/contracts/interfaces/ZoneInteractionErrors.sol b/contracts/interfaces/ZoneInteractionErrors.sol index f7b271c4c..786a2ca21 100644 --- a/contracts/interfaces/ZoneInteractionErrors.sol +++ b/contracts/interfaces/ZoneInteractionErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /** * @title ZoneInteractionErrors @@ -11,9 +11,19 @@ interface ZoneInteractionErrors { * @dev Revert with an error when attempting to fill an order that specifies * a restricted submitter as its order type when not submitted by * either the offerer or the order's zone or approved as valid by the - * zone in question via a staticcall to `isValidOrder`. + * zone in question via a call to `isValidOrder`. * * @param orderHash The order hash for the invalid restricted order. */ error InvalidRestrictedOrder(bytes32 orderHash); + + /** + * @dev Revert with an error when attempting to fill a contract order that + * fails to generate an order successfully, that does not adhere to the + * requirements for minimum spent or maximum received supplied by the + * fulfiller, or that fails the post-execution `ratifyOrder` check.. + * + * @param orderHash The order hash for the invalid contract order. + */ + error InvalidContractOrder(bytes32 orderHash); } diff --git a/contracts/interfaces/ZoneInterface.sol b/contracts/interfaces/ZoneInterface.sol index 94dd037f2..d64a5d419 100644 --- a/contracts/interfaces/ZoneInterface.sol +++ b/contracts/interfaces/ZoneInterface.sol @@ -1,27 +1,37 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore -import { - AdvancedOrder, - CriteriaResolver -} from "../lib/ConsiderationStructs.sol"; +import { ZoneParameters, Schema } from "../lib/ConsiderationStructs.sol"; +/** + * @title ZoneInterface + * @notice Contains functions exposed by a zone. + */ interface ZoneInterface { - // Called by Consideration whenever extraData is not provided by the caller. - function isValidOrder( - bytes32 orderHash, - address caller, - address offerer, - bytes32 zoneHash - ) external view returns (bytes4 validOrderMagicValue); + /** + * @dev Validates an order. + * + * @param zoneParameters The context about the order fulfillment and any + * supplied extraData. + * + * @return validOrderMagicValue The magic value that indicates a valid + * order. + */ + function validateOrder( + ZoneParameters calldata zoneParameters + ) external returns (bytes4 validOrderMagicValue); - // Called by Consideration whenever any extraData is provided by the caller. - function isValidOrderIncludingExtraData( - bytes32 orderHash, - address caller, - AdvancedOrder calldata order, - bytes32[] calldata priorOrderHashes, - CriteriaResolver[] calldata criteriaResolvers - ) external view returns (bytes4 validOrderMagicValue); + /** + * @dev Returns the metadata for this zone. + * + * @return name The name of the zone. + * @return schemas The schemas that the zone implements. + */ + function getSeaportMetadata() + external + view + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ); } diff --git a/contracts/interfaces/legacy/ConsiderationInterface1_1.sol b/contracts/interfaces/legacy/ConsiderationInterface1_1.sol new file mode 100644 index 000000000..1590472bc --- /dev/null +++ b/contracts/interfaces/legacy/ConsiderationInterface1_1.sol @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +import { + BasicOrderParameters, + OrderComponents, + Fulfillment, + FulfillmentComponent, + Execution, + Order, + AdvancedOrder, + OrderStatus, + CriteriaResolver +} from "../../lib/ConsiderationStructs.sol"; + +/** + * @title ConsiderationInterface1.1 + * @author 0age + * @custom:version 1.1 + * * + * @dev This is the legacy ConsiderationInterface from Seaport 1.1 + */ +interface ConsiderationInterface1_1 { + /** + * @notice Fulfill an order offering an ERC721 token by supplying Ether (or + * the native token for the given chain) as consideration for the + * order. An arbitrary number of "additional recipients" may also be + * supplied which will each receive native tokens from the fulfiller + * as consideration. + * + * @param parameters Additional information on the fulfilled order. Note + * that the offerer must first approve this contract (or + * their preferred conduit if indicated by the order) for + * their offered ERC721 token to be transferred. + * + * @return fulfilled A boolean indicating whether the order has been + * successfully fulfilled. + */ + function fulfillBasicOrder( + BasicOrderParameters calldata parameters + ) external payable returns (bool fulfilled); + + /** + * @notice Fulfill an order with an arbitrary number of items for offer and + * consideration. Note that this function does not support + * criteria-based orders or partial filling of orders (though + * filling the remainder of a partially-filled order is supported). + * + * @param order The order to fulfill. Note that both the + * offerer and the fulfiller must first approve + * this contract (or the corresponding conduit if + * indicated) to transfer any relevant tokens on + * their behalf and that contracts must implement + * `onERC1155Received` to receive ERC1155 tokens + * as consideration. + * @param fulfillerConduitKey A bytes32 value indicating what conduit, if + * any, to source the fulfiller's token approvals + * from. The zero hash signifies that no conduit + * should be used, with direct approvals set on + * Consideration. + * + * @return fulfilled A boolean indicating whether the order has been + * successfully fulfilled. + */ + function fulfillOrder( + Order calldata order, + bytes32 fulfillerConduitKey + ) external payable returns (bool fulfilled); + + /** + * @notice Fill an order, fully or partially, with an arbitrary number of + * items for offer and consideration alongside criteria resolvers + * containing specific token identifiers and associated proofs. + * + * @param advancedOrder The order to fulfill along with the fraction + * of the order to attempt to fill. Note that + * both the offerer and the fulfiller must first + * approve this contract (or their preferred + * conduit if indicated by the order) to transfer + * any relevant tokens on their behalf and that + * contracts must implement `onERC1155Received` + * to receive ERC1155 tokens as consideration. + * Also note that all offer and consideration + * components must have no remainder after + * multiplication of the respective amount with + * the supplied fraction for the partial fill to + * be considered valid. + * @param criteriaResolvers An array where each element contains a + * reference to a specific offer or + * consideration, a token identifier, and a proof + * that the supplied token identifier is + * contained in the merkle root held by the item + * in question's criteria element. Note that an + * empty criteria indicates that any + * (transferable) token identifier on the token + * in question is valid and that no associated + * proof needs to be supplied. + * @param fulfillerConduitKey A bytes32 value indicating what conduit, if + * any, to source the fulfiller's token approvals + * from. The zero hash signifies that no conduit + * should be used, with direct approvals set on + * Consideration. + * @param recipient The intended recipient for all received items, + * with `address(0)` indicating that the caller + * should receive the items. + * + * @return fulfilled A boolean indicating whether the order has been + * successfully fulfilled. + */ + function fulfillAdvancedOrder( + AdvancedOrder calldata advancedOrder, + CriteriaResolver[] calldata criteriaResolvers, + bytes32 fulfillerConduitKey, + address recipient + ) external payable returns (bool fulfilled); + + /** + * @notice Attempt to fill a group of orders, each with an arbitrary number + * of items for offer and consideration. Any order that is not + * currently active, has already been fully filled, or has been + * cancelled will be omitted. Remaining offer and consideration + * items will then be aggregated where possible as indicated by the + * supplied offer and consideration component arrays and aggregated + * items will be transferred to the fulfiller or to each intended + * recipient, respectively. Note that a failing item transfer or an + * issue with order formatting will cause the entire batch to fail. + * Note that this function does not support criteria-based orders or + * partial filling of orders (though filling the remainder of a + * partially-filled order is supported). + * + * @param orders The orders to fulfill. Note that both + * the offerer and the fulfiller must first + * approve this contract (or the + * corresponding conduit if indicated) to + * transfer any relevant tokens on their + * behalf and that contracts must implement + * `onERC1155Received` to receive ERC1155 + * tokens as consideration. + * @param offerFulfillments An array of FulfillmentComponent arrays + * indicating which offer items to attempt + * to aggregate when preparing executions. + * @param considerationFulfillments An array of FulfillmentComponent arrays + * indicating which consideration items to + * attempt to aggregate when preparing + * executions. + * @param fulfillerConduitKey A bytes32 value indicating what conduit, + * if any, to source the fulfiller's token + * approvals from. The zero hash signifies + * that no conduit should be used, with + * direct approvals set on this contract. + * @param maximumFulfilled The maximum number of orders to fulfill. + * + * @return availableOrders An array of booleans indicating if each order + * with an index corresponding to the index of the + * returned boolean was fulfillable or not. + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function fulfillAvailableOrders( + Order[] calldata orders, + FulfillmentComponent[][] calldata offerFulfillments, + FulfillmentComponent[][] calldata considerationFulfillments, + bytes32 fulfillerConduitKey, + uint256 maximumFulfilled + ) + external + payable + returns (bool[] memory availableOrders, Execution[] memory executions); + + /** + * @notice Attempt to fill a group of orders, fully or partially, with an + * arbitrary number of items for offer and consideration per order + * alongside criteria resolvers containing specific token + * identifiers and associated proofs. Any order that is not + * currently active, has already been fully filled, or has been + * cancelled will be omitted. Remaining offer and consideration + * items will then be aggregated where possible as indicated by the + * supplied offer and consideration component arrays and aggregated + * items will be transferred to the fulfiller or to each intended + * recipient, respectively. Note that a failing item transfer or an + * issue with order formatting will cause the entire batch to fail. + * + * @param advancedOrders The orders to fulfill along with the + * fraction of those orders to attempt to + * fill. Note that both the offerer and the + * fulfiller must first approve this + * contract (or their preferred conduit if + * indicated by the order) to transfer any + * relevant tokens on their behalf and that + * contracts must implement + * `onERC1155Received` to enable receipt of + * ERC1155 tokens as consideration. Also + * note that all offer and consideration + * components must have no remainder after + * multiplication of the respective amount + * with the supplied fraction for an + * order's partial fill amount to be + * considered valid. + * @param criteriaResolvers An array where each element contains a + * reference to a specific offer or + * consideration, a token identifier, and a + * proof that the supplied token identifier + * is contained in the merkle root held by + * the item in question's criteria element. + * Note that an empty criteria indicates + * that any (transferable) token + * identifier on the token in question is + * valid and that no associated proof needs + * to be supplied. + * @param offerFulfillments An array of FulfillmentComponent arrays + * indicating which offer items to attempt + * to aggregate when preparing executions. + * @param considerationFulfillments An array of FulfillmentComponent arrays + * indicating which consideration items to + * attempt to aggregate when preparing + * executions. + * @param fulfillerConduitKey A bytes32 value indicating what conduit, + * if any, to source the fulfiller's token + * approvals from. The zero hash signifies + * that no conduit should be used, with + * direct approvals set on this contract. + * @param recipient The intended recipient for all received + * items, with `address(0)` indicating that + * the caller should receive the items. + * @param maximumFulfilled The maximum number of orders to fulfill. + * + * @return availableOrders An array of booleans indicating if each order + * with an index corresponding to the index of the + * returned boolean was fulfillable or not. + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function fulfillAvailableAdvancedOrders( + AdvancedOrder[] calldata advancedOrders, + CriteriaResolver[] calldata criteriaResolvers, + FulfillmentComponent[][] calldata offerFulfillments, + FulfillmentComponent[][] calldata considerationFulfillments, + bytes32 fulfillerConduitKey, + address recipient, + uint256 maximumFulfilled + ) + external + payable + returns (bool[] memory availableOrders, Execution[] memory executions); + + /** + * @notice Match an arbitrary number of orders, each with an arbitrary + * number of items for offer and consideration along with as set of + * fulfillments allocating offer components to consideration + * components. Note that this function does not support + * criteria-based or partial filling of orders (though filling the + * remainder of a partially-filled order is supported). + * + * @param orders The orders to match. Note that both the offerer and + * fulfiller on each order must first approve this + * contract (or their conduit if indicated by the order) + * to transfer any relevant tokens on their behalf and + * each consideration recipient must implement + * `onERC1155Received` to enable ERC1155 token receipt. + * @param fulfillments An array of elements allocating offer components to + * consideration components. Note that each + * consideration component must be fully met for the + * match operation to be valid. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function matchOrders( + Order[] calldata orders, + Fulfillment[] calldata fulfillments + ) external payable returns (Execution[] memory executions); + + /** + * @notice Match an arbitrary number of full or partial orders, each with an + * arbitrary number of items for offer and consideration, supplying + * criteria resolvers containing specific token identifiers and + * associated proofs as well as fulfillments allocating offer + * components to consideration components. + * + * @param orders The advanced orders to match. Note that both the + * offerer and fulfiller on each order must first + * approve this contract (or a preferred conduit if + * indicated by the order) to transfer any relevant + * tokens on their behalf and each consideration + * recipient must implement `onERC1155Received` in + * order to receive ERC1155 tokens. Also note that + * the offer and consideration components for each + * order must have no remainder after multiplying + * the respective amount with the supplied fraction + * in order for the group of partial fills to be + * considered valid. + * @param criteriaResolvers An array where each element contains a reference + * to a specific order as well as that order's + * offer or consideration, a token identifier, and + * a proof that the supplied token identifier is + * contained in the order's merkle root. Note that + * an empty root indicates that any (transferable) + * token identifier is valid and that no associated + * proof needs to be supplied. + * @param fulfillments An array of elements allocating offer components + * to consideration components. Note that each + * consideration component must be fully met in + * order for the match operation to be valid. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function matchAdvancedOrders( + AdvancedOrder[] calldata orders, + CriteriaResolver[] calldata criteriaResolvers, + Fulfillment[] calldata fulfillments + ) external payable returns (Execution[] memory executions); + + /** + * @notice Cancel an arbitrary number of orders. Note that only the offerer + * or the zone of a given order may cancel it. Callers should ensure + * that the intended order was cancelled by calling `getOrderStatus` + * and confirming that `isCancelled` returns `true`. + * + * @param orders The orders to cancel. + * + * @return cancelled A boolean indicating whether the supplied orders have + * been successfully cancelled. + */ + function cancel( + OrderComponents[] calldata orders + ) external returns (bool cancelled); + + /** + * @notice Validate an arbitrary number of orders, thereby registering their + * signatures as valid and allowing the fulfiller to skip signature + * verification on fulfillment. Note that validated orders may still + * be unfulfillable due to invalid item amounts or other factors; + * callers should determine whether validated orders are fulfillable + * by simulating the fulfillment call prior to execution. Also note + * that anyone can validate a signed order, but only the offerer can + * validate an order without supplying a signature. + * + * @param orders The orders to validate. + * + * @return validated A boolean indicating whether the supplied orders have + * been successfully validated. + */ + function validate( + Order[] calldata orders + ) external returns (bool validated); + + /** + * @notice Cancel all orders from a given offerer with a given zone in bulk + * by incrementing a counter. Note that only the offerer may + * increment the counter. + * + * @return newCounter The new counter. + */ + function incrementCounter() external returns (uint256 newCounter); + + /** + * @notice Retrieve the order hash for a given order. + * + * @param order The components of the order. + * + * @return orderHash The order hash. + */ + function getOrderHash( + OrderComponents calldata order + ) external view returns (bytes32 orderHash); + + /** + * @notice Retrieve the status of a given order by hash, including whether + * the order has been cancelled or validated and the fraction of the + * order that has been filled. + * + * @param orderHash The order hash in question. + * + * @return isValidated A boolean indicating whether the order in question + * has been validated (i.e. previously approved or + * partially filled). + * @return isCancelled A boolean indicating whether the order in question + * has been cancelled. + * @return totalFilled The total portion of the order that has been filled + * (i.e. the "numerator"). + * @return totalSize The total size of the order that is either filled or + * unfilled (i.e. the "denominator"). + */ + function getOrderStatus( + bytes32 orderHash + ) + external + view + returns ( + bool isValidated, + bool isCancelled, + uint256 totalFilled, + uint256 totalSize + ); + + /** + * @notice Retrieve the current counter for a given offerer. + * + * @param offerer The offerer in question. + * + * @return counter The current counter. + */ + function getCounter( + address offerer + ) external view returns (uint256 counter); + + /** + * @notice Retrieve configuration information for this contract. + * + * @return version The contract version. + * @return domainSeparator The domain separator for this contract. + * @return conduitController The conduit Controller set for this contract. + */ + function information() + external + view + returns ( + string memory version, + bytes32 domainSeparator, + address conduitController + ); + + /** + * @notice Retrieve the name of this contract. + * + * @return contractName The name of this contract. + */ + function name() external view returns (string memory contractName); +} diff --git a/contracts/interfaces/legacy/ZoneInterface1_1.sol b/contracts/interfaces/legacy/ZoneInterface1_1.sol new file mode 100644 index 000000000..75e1d289a --- /dev/null +++ b/contracts/interfaces/legacy/ZoneInterface1_1.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +import { + AdvancedOrder, + CriteriaResolver +} from "../../lib/ConsiderationStructs.sol"; + +/** + * @title ZoneInterface1.1 + * @author 0age + * @custom:version 1.1 + * + * @dev This is the legacy ZoneInterface from Seaport 1.1 + */ +interface ZoneInterface1_1 { + // Called by Consideration whenever extraData is not provided by the caller. + function isValidOrder( + bytes32 orderHash, + address caller, + address offerer, + bytes32 zoneHash + ) external view returns (bytes4 validOrderMagicValue); + + // Called by Consideration whenever any extraData is provided by the caller. + function isValidOrderIncludingExtraData( + bytes32 orderHash, + address caller, + AdvancedOrder calldata order, + bytes32[] calldata priorOrderHashes, + CriteriaResolver[] calldata criteriaResolvers + ) external view returns (bytes4 validOrderMagicValue); +} diff --git a/contracts/lib/AmountDeriver.sol b/contracts/lib/AmountDeriver.sol index f35b2cb73..e03514af8 100644 --- a/contracts/lib/AmountDeriver.sol +++ b/contracts/lib/AmountDeriver.sol @@ -1,12 +1,15 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -// prettier-ignore import { AmountDerivationErrors } from "../interfaces/AmountDerivationErrors.sol"; -import "./ConsiderationConstants.sol"; +import { + Error_selector_offset, + InexactFraction_error_length, + InexactFraction_error_selector +} from "./ConsiderationErrorConstants.sol"; /** * @title AmountDeriver @@ -123,8 +126,11 @@ contract AmountDeriver is AmountDerivationErrors { // Ensure new value contains no remainder via mulmod operator. // Credit to @hrkrshnn + @axic for proposing this optimal solution. if mulmod(value, numerator, denominator) { - mstore(0, InexactFraction_error_signature) - revert(0, InexactFraction_error_len) + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, InexactFraction_error_selector) + + // revert(abi.encodeWithSignature("InexactFraction()")) + revert(Error_selector_offset, InexactFraction_error_length) } } diff --git a/contracts/lib/Assertions.sol b/contracts/lib/Assertions.sol index ec10a11fe..d234f032e 100644 --- a/contracts/lib/Assertions.sol +++ b/contracts/lib/Assertions.sol @@ -1,18 +1,44 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { OrderParameters } from "./ConsiderationStructs.sol"; import { GettersAndDerivers } from "./GettersAndDerivers.sol"; -// prettier-ignore import { TokenTransferrerErrors } from "../interfaces/TokenTransferrerErrors.sol"; import { CounterManager } from "./CounterManager.sol"; -import "./ConsiderationConstants.sol"; +import { + AdditionalRecipient_size_shift, + AddressDirtyUpperBitThreshold, + BasicOrder_additionalRecipients_head_cdPtr, + BasicOrder_additionalRecipients_head_ptr, + BasicOrder_additionalRecipients_length_cdPtr, + BasicOrder_basicOrderType_cdPtr, + BasicOrder_basicOrderType_range, + BasicOrder_considerationToken_cdPtr, + BasicOrder_offerer_cdPtr, + BasicOrder_offerToken_cdPtr, + BasicOrder_parameters_cdPtr, + BasicOrder_parameters_ptr, + BasicOrder_signature_cdPtr, + BasicOrder_signature_ptr, + BasicOrder_zone_cdPtr +} from "./ConsiderationConstants.sol"; + +import { + Error_selector_offset, + MissingItemAmount_error_length, + MissingItemAmount_error_selector +} from "./ConsiderationErrorConstants.sol"; + +import { + _revertInvalidBasicOrderParameterEncoding, + _revertMissingOriginalConsiderationItems +} from "./ConsiderationErrors.sol"; /** * @title Assertions @@ -33,9 +59,9 @@ contract Assertions is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - GettersAndDerivers(conduitController) - {} + constructor( + address conduitController + ) GettersAndDerivers(conduitController) {} /** * @dev Internal view function to ensure that the supplied consideration @@ -81,7 +107,7 @@ contract Assertions is ) internal pure { // Ensure supplied consideration array length is not less than original. if (suppliedConsiderationItemTotal < originalConsiderationItemTotal) { - revert MissingOriginalConsiderationItems(); + _revertMissingOriginalConsiderationItems(); } } @@ -92,9 +118,14 @@ contract Assertions is * @param amount The amount to check. */ function _assertNonZeroAmount(uint256 amount) internal pure { - // Revert if the supplied amount is equal to zero. - if (amount == 0) { - revert MissingItemAmount(); + assembly { + if iszero(amount) { + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, MissingItemAmount_error_selector) + + // revert(abi.encodeWithSignature("MissingItemAmount()")) + revert(Error_selector_offset, MissingItemAmount_error_length) + } } } @@ -119,54 +150,79 @@ contract Assertions is * 2. Additional recipients arr offset == 0x240 * 3. Signature offset == 0x260 + (recipients.length * 0x40) * 4. BasicOrderType between 0 and 23 (i.e. < 24) + * 5. Offerer, zone, offer token, and consideration token have no + * upper dirty bits — each argument is type(uint160).max or less */ validOffsets := and( - // Order parameters at calldata 0x04 must have offset of 0x20. - eq( - calldataload(BasicOrder_parameters_cdPtr), - BasicOrder_parameters_ptr - ), - // Additional recipients at cd 0x224 must have offset of 0x240. - eq( - calldataload(BasicOrder_additionalRecipients_head_cdPtr), - BasicOrder_additionalRecipients_head_ptr - ) - ) - - validOffsets := and( - validOffsets, - eq( - // Load signature offset from calldata 0x244. - calldataload(BasicOrder_signature_cdPtr), - // Derive expected offset as start of recipients + len * 64. - add( - BasicOrder_signature_ptr, - mul( - // Additional recipients length at calldata 0x264. + and( + and( + // Order parameters at cd 0x04 must have offset of 0x20. + eq( + calldataload(BasicOrder_parameters_cdPtr), + BasicOrder_parameters_ptr + ), + // Additional recipients (cd 0x224) arr offset == 0x240. + eq( calldataload( - BasicOrder_additionalRecipients_length_cdPtr + BasicOrder_additionalRecipients_head_cdPtr ), - // Each additional recipient has a length of 0x40. - AdditionalRecipients_size + BasicOrder_additionalRecipients_head_ptr + ) + ), + // Signature offset == 0x260 + (recipients.length * 0x40). + eq( + // Load signature offset from calldata 0x244. + calldataload(BasicOrder_signature_cdPtr), + // Expected offset is start of recipients + len * 64. + add( + BasicOrder_signature_ptr, + shl( + // Each additional recipient has length of 0x40. + AdditionalRecipient_size_shift, + // Additional recipients length at cd 0x264. + calldataload( + BasicOrder_additionalRecipients_length_cdPtr + ) + ) ) ) - ) - ) - - validOffsets := and( - validOffsets, - lt( - // BasicOrderType parameter at calldata offset 0x124. - calldataload(BasicOrder_basicOrderType_cdPtr), - // Value should be less than 24. - BasicOrder_basicOrderType_range + ), + and( + // Ensure BasicOrderType parameter is less than 0x18. + lt( + // BasicOrderType parameter at calldata offset 0x124. + calldataload(BasicOrder_basicOrderType_cdPtr), + // Value should be less than 24. + BasicOrder_basicOrderType_range + ), + // Ensure no dirty upper bits are present on offerer, zone, + // offer token, or consideration token. + lt( + or( + or( + // Offerer parameter at calldata offset 0x84. + calldataload(BasicOrder_offerer_cdPtr), + // Zone parameter at calldata offset 0xa4. + calldataload(BasicOrder_zone_cdPtr) + ), + or( + // Offer token parameter at cd offset 0xc4. + calldataload(BasicOrder_offerToken_cdPtr), + // Consideration token parameter at offset 0x24. + calldataload( + BasicOrder_considerationToken_cdPtr + ) + ) + ), + AddressDirtyUpperBitThreshold + ) ) ) } // Revert with an error if basic order parameter offsets are invalid. if (!validOffsets) { - revert InvalidBasicOrderParameterEncoding(); + _revertInvalidBasicOrderParameterEncoding(); } } } diff --git a/contracts/lib/BasicOrderFulfiller.sol b/contracts/lib/BasicOrderFulfiller.sol index 62dcf1816..14aa0befc 100644 --- a/contracts/lib/BasicOrderFulfiller.sol +++ b/contracts/lib/BasicOrderFulfiller.sol @@ -1,28 +1,117 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; - -// prettier-ignore import { - OrderType, + BasicOrderRouteType, ItemType, - BasicOrderRouteType + OrderType } from "./ConsiderationEnums.sol"; -// prettier-ignore -import { - AdditionalRecipient, - BasicOrderParameters, - OfferItem, - ConsiderationItem, - SpentItem, - ReceivedItem -} from "./ConsiderationStructs.sol"; +import { BasicOrderParameters } from "./ConsiderationStructs.sol"; import { OrderValidator } from "./OrderValidator.sol"; -import "./ConsiderationConstants.sol"; +import { + _revertInsufficientNativeTokensSupplied, + _revertInvalidMsgValue, + _revertInvalidERC721TransferAmount, + _revertUnusedItemParameters +} from "./ConsiderationErrors.sol"; + +import { + AccumulatorDisarmed, + AdditionalRecipient_size_shift, + AdditionalRecipient_size, + BasicOrder_additionalRecipients_data_cdPtr, + BasicOrder_additionalRecipients_length_cdPtr, + BasicOrder_basicOrderType_cdPtr, + BasicOrder_common_params_size, + BasicOrder_considerationAmount_cdPtr, + BasicOrder_considerationHashesArray_ptr, + BasicOrder_considerationIdentifier_cdPtr, + BasicOrder_considerationItem_endAmount_ptr, + BasicOrder_considerationItem_identifier_ptr, + BasicOrder_considerationItem_itemType_ptr, + BasicOrder_considerationItem_startAmount_ptr, + BasicOrder_considerationItem_token_ptr, + BasicOrder_considerationItem_typeHash_ptr, + BasicOrder_considerationToken_cdPtr, + BasicOrder_endTime_cdPtr, + BasicOrder_fulfillerConduit_cdPtr, + BasicOrder_offerAmount_cdPtr, + BasicOrder_offeredItemByteMap, + BasicOrder_offerer_cdPtr, + BasicOrder_offererConduit_cdPtr, + BasicOrder_offerIdentifier_cdPtr, + BasicOrder_offerItem_endAmount_ptr, + BasicOrder_offerItem_itemType_ptr, + BasicOrder_offerItem_token_ptr, + BasicOrder_offerItem_typeHash_ptr, + BasicOrder_offerToken_cdPtr, + BasicOrder_order_considerationHashes_ptr, + BasicOrder_order_counter_ptr, + BasicOrder_order_offerer_ptr, + BasicOrder_order_offerHashes_ptr, + BasicOrder_order_orderType_ptr, + BasicOrder_order_startTime_ptr, + BasicOrder_order_typeHash_ptr, + BasicOrder_receivedItemByteMap, + BasicOrder_startTime_cdPtr, + BasicOrder_totalOriginalAdditionalRecipients_cdPtr, + BasicOrder_zone_cdPtr, + Common_token_offset, + Conduit_execute_ConduitTransfer_length_ptr, + Conduit_execute_ConduitTransfer_length, + Conduit_execute_ConduitTransfer_offset_ptr, + Conduit_execute_ConduitTransfer_ptr, + Conduit_execute_signature, + Conduit_execute_transferAmount_ptr, + Conduit_execute_transferIdentifier_ptr, + Conduit_execute_transferFrom_ptr, + Conduit_execute_transferItemType_ptr, + Conduit_execute_transferTo_ptr, + Conduit_execute_transferToken_ptr, + EIP712_ConsiderationItem_size, + EIP712_OfferItem_size, + EIP712_Order_size, + FiveWords, + FourWords, + FreeMemoryPointerSlot, + MaskOverLastTwentyBytes, + OneConduitExecute_size, + OneWord, + OneWordShift, + OrderFulfilled_baseOffset, + OrderFulfilled_baseSize, + OrderFulfilled_consideration_body_offset, + OrderFulfilled_consideration_head_offset, + OrderFulfilled_consideration_length_baseOffset, + OrderFulfilled_fulfiller_offset, + OrderFulfilled_offer_body_offset, + OrderFulfilled_offer_head_offset, + OrderFulfilled_offer_length_baseOffset, + OrderFulfilled_selector, + ReceivedItem_amount_offset, + ReceivedItem_size, + receivedItemsHash_ptr, + ThreeWords, + TwoWords, + ZeroSlot +} from "./ConsiderationConstants.sol"; + +import { + Error_selector_offset, + InvalidBasicOrderParameterEncoding_error_length, + InvalidBasicOrderParameterEncoding_error_selector, + InvalidTime_error_endTime_ptr, + InvalidTime_error_length, + InvalidTime_error_selector, + InvalidTime_error_startTime_ptr, + MissingOriginalConsiderationItems_error_length, + MissingOriginalConsiderationItems_error_selector, + UnusedItemParameters_error_length, + UnusedItemParameters_error_selector +} from "./ConsiderationErrorConstants.sol"; /** * @title BasicOrderFulfiller @@ -77,6 +166,8 @@ contract BasicOrderFulfiller is OrderValidator { // Declare additional recipient item type to derive from the route type. ItemType additionalRecipientsItemType; + bytes32 orderHash; + // Utilize assembly to extract the order type and the basic order route. assembly { // Read basicOrderType from calldata. @@ -88,7 +179,8 @@ contract BasicOrderFulfiller is OrderValidator { // Divide basicOrderType by four to derive the route. route := shr(2, basicOrderType) - // If route > 1 additionalRecipient items are ERC20 (1) else Eth (0) + // If route > 1 additionalRecipient items are ERC20 (1) else native + // token (0). additionalRecipientsItemType := gt(route, 1) } @@ -108,7 +200,7 @@ contract BasicOrderFulfiller is OrderValidator { // Revert if msg.value has not been supplied as part of payable // routes or has been supplied as part of non-payable routes. if (!correctPayableStatus) { - revert InvalidMsgValue(msg.value); + _revertInvalidMsgValue(msg.value); } } @@ -138,25 +230,17 @@ contract BasicOrderFulfiller is OrderValidator { ) // If route > 2, receivedItemType is route - 2. If route is 2, - // the receivedItemType is ERC20 (1). Otherwise, it is Eth (0). - receivedItemType := add( - mul(sub(route, 2), gt(route, 2)), - eq(route, 2) - ) + // the receivedItemType is ERC20 (1). Otherwise, it is native + // token (0). + receivedItemType := byte(route, BasicOrder_receivedItemByteMap) // If route > 3, offeredItemType is ERC20 (1). Route is 2 or 3, // offeredItemType = route. Route is 0 or 1, it is route + 2. - offeredItemType := sub( - add(route, mul(iszero(additionalRecipientsItemType), 2)), - mul( - offerTypeIsAdditionalRecipientsType, - add(receivedItemType, 1) - ) - ) + offeredItemType := byte(route, BasicOrder_offeredItemByteMap) } // Derive & validate order using parameters and update order status. - _prepareBasicFulfillmentFromCalldata( + orderHash = _prepareBasicFulfillmentFromCalldata( parameters, orderType, receivedItemType, @@ -175,38 +259,38 @@ contract BasicOrderFulfiller is OrderValidator { conduitKey := calldataload( add( BasicOrder_offererConduit_cdPtr, - mul(offerTypeIsAdditionalRecipientsType, OneWord) + shl(OneWordShift, offerTypeIsAdditionalRecipientsType) ) ) } // Transfer tokens based on the route. if (additionalRecipientsItemType == ItemType.NATIVE) { - // Ensure neither the token nor the identifier parameters are set. - if ( - (uint160(parameters.considerationToken) | - parameters.considerationIdentifier) != 0 - ) { - revert UnusedItemParameters(); + // Ensure neither consideration token nor identifier are set. Note + // that dirty upper bits in the consideration token will still cause + // this error to be thrown. + assembly { + if or( + calldataload(BasicOrder_considerationToken_cdPtr), + calldataload(BasicOrder_considerationIdentifier_cdPtr) + ) { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, UnusedItemParameters_error_selector) + + // revert(abi.encodeWithSignature("UnusedItemParameters()")) + revert( + Error_selector_offset, + UnusedItemParameters_error_length + ) + } } // Transfer the ERC721 or ERC1155 item, bypassing the accumulator. - _transferIndividual721Or1155Item( - offeredItemType, - parameters.offerToken, - parameters.offerer, - msg.sender, - parameters.offerIdentifier, - parameters.offerAmount, - conduitKey - ); + _transferIndividual721Or1155Item(offeredItemType, conduitKey); // Transfer native to recipients, return excess to caller & wrap up. - _transferEthAndFinalize( - parameters.considerationAmount, - parameters.offerer, - parameters.additionalRecipients - ); + _transferNativeTokensAndFinalize(); } else { // Initialize an accumulator array. From this point forward, no new // memory regions can be safely allocated until the accumulator is @@ -266,8 +350,6 @@ contract BasicOrderFulfiller is OrderValidator { // Transfer ERC20 tokens to all recipients and wrap up. _transferERC20AndFinalize( - parameters.offerer, - parameters, offerTypeIsAdditionalRecipientsType, accumulator ); @@ -276,6 +358,9 @@ contract BasicOrderFulfiller is OrderValidator { _triggerIfArmed(accumulator); } + // Determine whether order is restricted and, if so, that it is valid. + _assertRestrictedBasicOrderValidity(orderHash, orderType, parameters); + // Clear the reentrancy guard. _clearReentrancyGuard(); @@ -292,11 +377,8 @@ contract BasicOrderFulfiller is OrderValidator { * offsets. Checking that the offsets were produced by default encoding * will ensure that other functions using Solidity's calldata accessors * (which calculate pointers from the stored offsets) are reading the - * same data as the order hash is derived from. Also note that This - * function accesses memory directly. It does not clear the expanded - * memory regions used, nor does it update the free memory pointer, so - * other direct memory access must not assume that unused memory is - * empty. + * same data as the order hash is derived from. Also note that this + * function accesses memory directly. * * @param parameters The parameters of the basic order. * @param orderType The order type. @@ -309,6 +391,7 @@ contract BasicOrderFulfiller is OrderValidator { * consideration item on the order. * @param offeredItemType The item type of the offered item on * the order. + * @return orderHash The calculated order hash. */ function _prepareBasicFulfillmentFromCalldata( BasicOrderParameters calldata parameters, @@ -317,28 +400,65 @@ contract BasicOrderFulfiller is OrderValidator { ItemType additionalRecipientsItemType, address additionalRecipientsToken, ItemType offeredItemType - ) internal { + ) internal returns (bytes32 orderHash) { // Ensure this function cannot be triggered during a reentrant call. - _setReentrancyGuard(); - - // Ensure current timestamp falls between order start time and end time. - _verifyTime(parameters.startTime, parameters.endTime, true); + _setReentrancyGuard(false); // Native tokens rejected during execution. // Verify that calldata offsets for all dynamic types were produced by - // default encoding. This ensures that the constants we use for calldata + // default encoding. This ensures that the constants used for calldata // pointers to dynamic types are the same as those calculated by // Solidity using their offsets. Also verify that the basic order type // is within range. _assertValidBasicOrderParameters(); - // Ensure supplied consideration array length is not less than original. - _assertConsiderationLengthIsNotLessThanOriginalConsiderationLength( - parameters.additionalRecipients.length, - parameters.totalOriginalAdditionalRecipients - ); + // Check for invalid time and missing original consideration items. + // Utilize assembly so that constant calldata pointers can be applied. + assembly { + // Ensure current timestamp is between order start time & end time. + if or( + gt(calldataload(BasicOrder_startTime_cdPtr), timestamp()), + iszero(gt(calldataload(BasicOrder_endTime_cdPtr), timestamp())) + ) { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidTime_error_selector) - // Declare stack element for the order hash. - bytes32 orderHash; + // Store arguments. + mstore( + InvalidTime_error_startTime_ptr, + calldataload(BasicOrder_startTime_cdPtr) + ) + mstore( + InvalidTime_error_endTime_ptr, + calldataload(BasicOrder_endTime_cdPtr) + ) + + // revert(abi.encodeWithSignature( + // "InvalidTime(uint256,uint256)", + // startTime, + // endTime + // )) + revert(Error_selector_offset, InvalidTime_error_length) + } + + // Ensure consideration array length isn't less than total original. + if lt( + calldataload(BasicOrder_additionalRecipients_length_cdPtr), + calldataload(BasicOrder_totalOriginalAdditionalRecipients_cdPtr) + ) { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, MissingOriginalConsiderationItems_error_selector) + + // revert(abi.encodeWithSignature( + // "MissingOriginalConsiderationItems()" + // )) + revert( + Error_selector_offset, + MissingOriginalConsiderationItems_error_length + ) + } + } { /** @@ -422,7 +542,7 @@ contract BasicOrderFulfiller is OrderValidator { // array. let eventConsiderationArrPtr := add( OrderFulfilled_consideration_length_baseOffset, - mul(totalAdditionalRecipients, OneWord) + shl(OneWordShift, totalAdditionalRecipients) ) // Set the length of the consideration array to the number of @@ -430,12 +550,7 @@ contract BasicOrderFulfiller is OrderValidator { // item. mstore( eventConsiderationArrPtr, - add( - calldataload( - BasicOrder_additionalRecipients_length_cdPtr - ), - 1 - ) + add(totalAdditionalRecipients, 1) ) // Overwrite the consideration array pointer so it points to the @@ -460,9 +575,9 @@ contract BasicOrderFulfiller is OrderValidator { * 3. Calculate EIP712 ConsiderationItem hashes for original * additional recipients and add a ReceivedItem for each to the * consideration array in the OrderFulfilled event. The original - * additional recipients are all the considerations signed by - * the offerer aside from the primary consideration of the - * order. Uses memory region from 0x80-0x160 as a buffer for + * additional recipients are all the consideration items signed + * by the offerer aside from the primary consideration items of + * the order. Uses memory region from 0x80-0x160 as a buffer for * calculating EIP712 ConsiderationItem hashes. */ @@ -485,13 +600,16 @@ contract BasicOrderFulfiller is OrderValidator { ) mstore(BasicOrder_considerationItem_identifier_ptr, 0) + // Declare a stack variable where all additional recipients will + // be combined to guard against providing dirty upper bits. + let combinedAdditionalRecipients + // Read length of the additionalRecipients array from calldata // and iterate. totalAdditionalRecipients := calldataload( BasicOrder_totalOriginalAdditionalRecipients_cdPtr ) let i := 0 - // prettier-ignore for {} lt(i, totalAdditionalRecipients) { i := add(i, 1) } { @@ -502,7 +620,7 @@ contract BasicOrderFulfiller is OrderValidator { // Retrieve calldata pointer for additional recipient. let additionalRecipientCdPtr := add( BasicOrder_additionalRecipients_data_cdPtr, - mul(AdditionalRecipients_size, i) + mul(AdditionalRecipient_size, i) ) // Copy startAmount from calldata to the ConsiderationItem @@ -518,7 +636,13 @@ contract BasicOrderFulfiller is OrderValidator { calldatacopy( BasicOrder_considerationItem_endAmount_ptr, additionalRecipientCdPtr, - AdditionalRecipients_size + AdditionalRecipient_size + ) + + // Include the recipient as part of combined recipients. + combinedAdditionalRecipients := or( + combinedAdditionalRecipients, + calldataload(add(additionalRecipientCdPtr, OneWord)) ) // Add 1 word to the pointer as part of each loop to reduce @@ -585,7 +709,7 @@ contract BasicOrderFulfiller is OrderValidator { receivedItemsHash_ptr, keccak256( BasicOrder_considerationHashesArray_ptr, - mul(add(totalAdditionalRecipients, 1), OneWord) + shl(OneWordShift, add(totalAdditionalRecipients, 1)) ) ) @@ -600,14 +724,14 @@ contract BasicOrderFulfiller is OrderValidator { totalAdditionalRecipients := calldataload( BasicOrder_additionalRecipients_length_cdPtr ) - // prettier-ignore + for {} lt(i, totalAdditionalRecipients) { i := add(i, 1) } { // Retrieve calldata pointer for additional recipient. let additionalRecipientCdPtr := add( BasicOrder_additionalRecipients_data_cdPtr, - mul(AdditionalRecipients_size, i) + mul(AdditionalRecipient_size, i) ) // At this point, eventConsiderationArrPtr points to the @@ -640,6 +764,27 @@ contract BasicOrderFulfiller is OrderValidator { additionalRecipientCdPtr, TwoWords ) + + // Include the recipient as part of combined recipients. + combinedAdditionalRecipients := or( + combinedAdditionalRecipients, + calldataload(add(additionalRecipientCdPtr, OneWord)) + ) + } + + // Ensure no dirty upper bits on combined additional recipients. + if gt(combinedAdditionalRecipients, MaskOverLastTwentyBytes) { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidBasicOrderParameterEncoding_error_selector) + + // revert(abi.encodeWithSignature( + // "InvalidBasicOrderParameterEncoding()" + // )) + revert( + Error_selector_offset, + InvalidBasicOrderParameterEncoding_error_length + ) } } } @@ -712,11 +857,11 @@ contract BasicOrderFulfiller is OrderValidator { */ let eventConsiderationArrPtr := add( OrderFulfilled_offer_length_baseOffset, - mul( + shl( + OneWordShift, calldataload( BasicOrder_additionalRecipients_length_cdPtr - ), - OneWord + ) ) ) @@ -730,7 +875,7 @@ contract BasicOrderFulfiller is OrderValidator { // offerAmount) from OrderParameters to (token, identifier, // amount) in SpentItem struct. calldatacopy( - add(eventConsiderationArrPtr, AdditionalRecipients_size), + add(eventConsiderationArrPtr, AdditionalRecipient_size), BasicOrder_offerToken_cdPtr, ThreeWords ) @@ -838,12 +983,12 @@ contract BasicOrderFulfiller is OrderValidator { * - 0x140: recipient 0 */ - // Derive pointer to start of OrderFulfilled event data + // Derive pointer to start of OrderFulfilled event data. let eventDataPtr := add( OrderFulfilled_baseOffset, - mul( - calldataload(BasicOrder_additionalRecipients_length_cdPtr), - OneWord + shl( + OneWordShift, + calldataload(BasicOrder_additionalRecipients_length_cdPtr) ) ) @@ -893,107 +1038,249 @@ contract BasicOrderFulfiller is OrderValidator { // Restore the zero slot. mstore(ZeroSlot, 0) - } - // Determine whether order is restricted and, if so, that it is valid. - _assertRestrictedBasicOrderValidity( - orderHash, - parameters.zoneHash, - orderType, - parameters.offerer, - parameters.zone - ); + // Update the free memory pointer so that event data is persisted. + mstore(FreeMemoryPointerSlot, add(eventDataPtr, dataSize)) + } // Verify and update the status of the derived order. - _validateBasicOrderAndUpdateStatus( - orderHash, - parameters.offerer, - parameters.signature - ); + _validateBasicOrderAndUpdateStatus(orderHash, parameters.signature); + + // Return the derived order hash. + return orderHash; + } + + /** + * @dev Internal function to transfer an individual ERC721 or ERC1155 item + * from a given originator to a given recipient. The accumulator will + * be bypassed, meaning that this function should be utilized in cases + * where multiple item transfers can be accumulated into a single + * conduit call. Sufficient approvals must be set, either on the + * respective conduit or on this contract. Note that this function may + * only be safely called as part of basic orders, as it assumes a + * specific calldata encoding structure that must first be validated. + * + * @param itemType The type of item to transfer, either ERC721 or ERC1155. + * @param conduitKey A bytes32 value indicating what corresponding conduit, + * if any, to source token approvals from. The zero hash + * signifies that no conduit should be used, with direct + * approvals set on this contract. + */ + function _transferIndividual721Or1155Item( + ItemType itemType, + bytes32 conduitKey + ) internal { + // Retrieve token, from, identifier, and amount from calldata using + // fixed calldata offsets based on strict basic parameter encoding. + address token; + address from; + uint256 identifier; + uint256 amount; + assembly { + token := calldataload(BasicOrder_offerToken_cdPtr) + from := calldataload(BasicOrder_offerer_cdPtr) + identifier := calldataload(BasicOrder_offerIdentifier_cdPtr) + amount := calldataload(BasicOrder_offerAmount_cdPtr) + } + + // Determine if the transfer is to be performed via a conduit. + if (conduitKey != bytes32(0)) { + // Use free memory pointer as calldata offset for the conduit call. + uint256 callDataOffset; + + // Utilize assembly to place each argument in free memory. + assembly { + // Retrieve the free memory pointer and use it as the offset. + callDataOffset := mload(FreeMemoryPointerSlot) + + // Write ConduitInterface.execute.selector to memory. + mstore(callDataOffset, Conduit_execute_signature) + + // Write the offset to the ConduitTransfer array in memory. + mstore( + add( + callDataOffset, + Conduit_execute_ConduitTransfer_offset_ptr + ), + Conduit_execute_ConduitTransfer_ptr + ) + + // Write the length of the ConduitTransfer array to memory. + mstore( + add( + callDataOffset, + Conduit_execute_ConduitTransfer_length_ptr + ), + Conduit_execute_ConduitTransfer_length + ) + + // Write the item type to memory. + mstore( + add(callDataOffset, Conduit_execute_transferItemType_ptr), + itemType + ) + + // Write the token to memory. + mstore( + add(callDataOffset, Conduit_execute_transferToken_ptr), + token + ) + + // Write the transfer source to memory. + mstore( + add(callDataOffset, Conduit_execute_transferFrom_ptr), + from + ) + + // Write the transfer recipient (the caller) to memory. + mstore( + add(callDataOffset, Conduit_execute_transferTo_ptr), + caller() + ) + + // Write the token identifier to memory. + mstore( + add(callDataOffset, Conduit_execute_transferIdentifier_ptr), + identifier + ) + + // Write the transfer amount to memory. + mstore( + add(callDataOffset, Conduit_execute_transferAmount_ptr), + amount + ) + } + + // Perform the call to the conduit. + _callConduitUsingOffsets( + conduitKey, + callDataOffset, + OneConduitExecute_size + ); + } else { + // Otherwise, determine whether it is an ERC721 or ERC1155 item. + if (itemType == ItemType.ERC721) { + // Ensure that exactly one 721 item is being transferred. + if (amount != 1) { + _revertInvalidERC721TransferAmount(amount); + } + + // Perform transfer to caller via the token contract directly. + _performERC721Transfer(token, from, msg.sender, identifier); + } else { + // Perform transfer to caller via the token contract directly. + _performERC1155Transfer( + token, + from, + msg.sender, + identifier, + amount + ); + } + } } /** * @dev Internal function to transfer Ether (or other native tokens) to a * given recipient as part of basic order fulfillment. Note that * conduits are not utilized for native tokens as the transferred - * amount must be provided as msg.value. - * - * @param amount The amount to transfer. - * @param to The recipient of the native token transfer. - * @param additionalRecipients The additional recipients of the order. + * amount must be provided as msg.value. Also note that this function + * may only be safely called as part of basic orders, as it assumes a + * specific calldata encoding structure that must first be validated. */ - function _transferEthAndFinalize( - uint256 amount, - address payable to, - AdditionalRecipient[] calldata additionalRecipients - ) internal { - // Put ether value supplied by the caller on the stack. - uint256 etherRemaining = msg.value; + function _transferNativeTokensAndFinalize() internal { + // Put native token value supplied by the caller on the stack. + uint256 nativeTokensRemaining = msg.value; + + // Retrieve consideration amount, offerer, and total size of additional + // recipients data from calldata using fixed offsets and place on stack. + uint256 amount; + address payable to; + uint256 totalAdditionalRecipientsDataSize; + assembly { + amount := calldataload(BasicOrder_considerationAmount_cdPtr) + to := calldataload(BasicOrder_offerer_cdPtr) + totalAdditionalRecipientsDataSize := shl( + AdditionalRecipient_size_shift, + calldataload(BasicOrder_additionalRecipients_length_cdPtr) + ) + } - // Retrieve total number of additional recipients and place on stack. - uint256 totalAdditionalRecipients = additionalRecipients.length; + uint256 additionalRecipientAmount; + address payable recipient; // Skip overflow check as for loop is indexed starting at zero. unchecked { - // Iterate over each additional recipient. - for (uint256 i = 0; i < totalAdditionalRecipients; ++i) { - // Retrieve the additional recipient. - AdditionalRecipient calldata additionalRecipient = ( - additionalRecipients[i] - ); - - // Read ether amount to transfer to recipient & place on stack. - uint256 additionalRecipientAmount = additionalRecipient.amount; + // Iterate over additional recipient data by two-word element. + for ( + uint256 i = 0; + i < totalAdditionalRecipientsDataSize; + i += AdditionalRecipient_size + ) { + assembly { + // Retrieve calldata pointer for additional recipient. + let additionalRecipientCdPtr := add( + BasicOrder_additionalRecipients_data_cdPtr, + i + ) - // Ensure that sufficient Ether is available. - if (additionalRecipientAmount > etherRemaining) { - revert InsufficientEtherSupplied(); + additionalRecipientAmount := calldataload( + additionalRecipientCdPtr + ) + recipient := calldataload( + add(OneWord, additionalRecipientCdPtr) + ) } - // Transfer Ether to the additional recipient. - _transferEth( - additionalRecipient.recipient, - additionalRecipientAmount - ); + // Ensure that sufficient native tokens are available. + if (additionalRecipientAmount > nativeTokensRemaining) { + _revertInsufficientNativeTokensSupplied(); + } - // Reduce ether value available. Skip underflow check as + // Reduce native token value available. Skip underflow check as // subtracted value is confirmed above as less than remaining. - etherRemaining -= additionalRecipientAmount; + nativeTokensRemaining -= additionalRecipientAmount; + + // Transfer native tokens to the additional recipient. + _transferNativeTokens(recipient, additionalRecipientAmount); } } - // Ensure that sufficient Ether is still available. - if (amount > etherRemaining) { - revert InsufficientEtherSupplied(); + // Ensure that sufficient native tokens are still available. + if (amount > nativeTokensRemaining) { + _revertInsufficientNativeTokensSupplied(); } - // Transfer Ether to the offerer. - _transferEth(to, amount); + // Transfer native tokens to the offerer. + _transferNativeTokens(to, amount); - // If any Ether remains after transfers, return it to the caller. - if (etherRemaining > amount) { - // Skip underflow check as etherRemaining > amount. + // If any native tokens remain after transfers, return to the caller. + if (nativeTokensRemaining > amount) { + // Skip underflow check as nativeTokensRemaining > amount. unchecked { - // Transfer remaining Ether to the caller. - _transferEth(payable(msg.sender), etherRemaining - amount); + // Transfer remaining native tokens to the caller. + _transferNativeTokens( + payable(msg.sender), + nativeTokensRemaining - amount + ); } } } /** * @dev Internal function to transfer ERC20 tokens to a given recipient as - * part of basic order fulfillment. + * part of basic order fulfillment. Note that this function may only be + * safely called as part of basic orders, as it assumes a specific + * calldata encoding structure that must first be validated. Also note + * that basic order parameters are retrieved using fixed offsets, this + * requires that strict basic order encoding has already been verified. * - * @param offerer The offerer of the fulfiller order. - * @param parameters The basic order parameters. * @param fromOfferer A boolean indicating whether to decrement amount from * the offered amount. * @param accumulator An open-ended array that collects transfers to execute * against a given conduit in a single call. */ function _transferERC20AndFinalize( - address offerer, - BasicOrderParameters calldata parameters, bool fromOfferer, bytes memory accumulator ) internal { @@ -1012,28 +1299,32 @@ contract BasicOrderFulfiller is OrderValidator { // Set ERC20 token transfer variables based on fromOfferer boolean. if (fromOfferer) { - // Use offerer as from value and msg.sender as to value. - from = offerer; - to = msg.sender; - - // Use offer token and related values if token is from offerer. - token = parameters.offerToken; - identifier = parameters.offerIdentifier; - amount = parameters.offerAmount; + // Use offerer as from value, msg.sender as to value, and offer + // token, identifier, & amount values if token is from offerer. + assembly { + from := calldataload(BasicOrder_offerer_cdPtr) + to := caller() + token := calldataload(BasicOrder_offerToken_cdPtr) + identifier := calldataload(BasicOrder_offerIdentifier_cdPtr) + amount := calldataload(BasicOrder_offerAmount_cdPtr) + } } else { - // Use msg.sender as from value and offerer as to value. - from = msg.sender; - to = offerer; - - // Otherwise, use consideration token and related values. - token = parameters.considerationToken; - identifier = parameters.considerationIdentifier; - amount = parameters.considerationAmount; + // Otherwise, use msg.sender as from value, offerer as to value, + // and consideration token, identifier, and amount values. + assembly { + from := caller() + to := calldataload(BasicOrder_offerer_cdPtr) + token := calldataload(BasicOrder_considerationToken_cdPtr) + identifier := calldataload( + BasicOrder_considerationIdentifier_cdPtr + ) + amount := calldataload(BasicOrder_considerationAmount_cdPtr) + } } // Ensure that no identifier is supplied. if (identifier != 0) { - revert UnusedItemParameters(); + _revertUnusedItemParameters(); } } @@ -1046,24 +1337,39 @@ contract BasicOrderFulfiller is OrderValidator { conduitKey := calldataload( sub( BasicOrder_fulfillerConduit_cdPtr, - mul(fromOfferer, OneWord) + shl(OneWordShift, fromOfferer) ) ) } - // Retrieve total number of additional recipients and place on stack. - uint256 totalAdditionalRecipients = ( - parameters.additionalRecipients.length - ); + // Retrieve total size of additional recipients data and place on stack. + uint256 totalAdditionalRecipientsDataSize; + assembly { + totalAdditionalRecipientsDataSize := shl( + AdditionalRecipient_size_shift, + calldataload(BasicOrder_additionalRecipients_length_cdPtr) + ) + } + + uint256 additionalRecipientAmount; + address recipient; // Iterate over each additional recipient. - for (uint256 i = 0; i < totalAdditionalRecipients; ) { - // Retrieve the additional recipient. - AdditionalRecipient calldata additionalRecipient = ( - parameters.additionalRecipients[i] - ); + for (uint256 i = 0; i < totalAdditionalRecipientsDataSize; ) { + assembly { + // Retrieve calldata pointer for additional recipient. + let additionalRecipientCdPtr := add( + BasicOrder_additionalRecipients_data_cdPtr, + i + ) - uint256 additionalRecipientAmount = additionalRecipient.amount; + additionalRecipientAmount := calldataload( + additionalRecipientCdPtr + ) + recipient := calldataload( + add(OneWord, additionalRecipientCdPtr) + ) + } // Decrement the amount to transfer to fulfiller if indicated. if (fromOfferer) { @@ -1074,7 +1380,7 @@ contract BasicOrderFulfiller is OrderValidator { _transferERC20( token, from, - additionalRecipient.recipient, + recipient, additionalRecipientAmount, conduitKey, accumulator @@ -1082,7 +1388,7 @@ contract BasicOrderFulfiller is OrderValidator { // Skip overflow check as for loop is indexed starting at zero. unchecked { - ++i; + i += AdditionalRecipient_size; } } diff --git a/contracts/lib/Consideration.sol b/contracts/lib/Consideration.sol index 765f6b38b..96d66a639 100644 --- a/contracts/lib/Consideration.sol +++ b/contracts/lib/Consideration.sol @@ -1,40 +1,55 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -// prettier-ignore import { ConsiderationInterface } from "../interfaces/ConsiderationInterface.sol"; -// prettier-ignore import { - OrderComponents, - BasicOrderParameters, - OrderParameters, - Order, AdvancedOrder, - OrderStatus, + BasicOrderParameters, CriteriaResolver, + Execution, Fulfillment, FulfillmentComponent, - Execution + Order, + OrderComponents } from "./ConsiderationStructs.sol"; import { OrderCombiner } from "./OrderCombiner.sol"; +import { + CalldataStart, + CalldataPointer +} from "../helpers/PointerLibraries.sol"; + +import { + Offset_fulfillAdvancedOrder_criteriaResolvers, + Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts, + Offset_fulfillAvailableAdvancedOrders_criteriaResolvers, + Offset_fulfillAvailableAdvancedOrders_offerFulfillments, + Offset_fulfillAvailableOrders_considerationFulfillments, + Offset_fulfillAvailableOrders_offerFulfillments, + Offset_matchAdvancedOrders_criteriaResolvers, + Offset_matchAdvancedOrders_fulfillments, + Offset_matchOrders_fulfillments, + OrderParameters_counter_offset +} from "./ConsiderationConstants.sol"; + /** * @title Consideration - * @author 0age - * @custom:coauthor d1ll0n - * @custom:coauthor transmissions11 - * @custom:version 1.1 - * @notice Consideration is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. - * It minimizes external calls to the greatest extent possible and - * provides lightweight methods for common routes as well as more - * flexible methods for composing advanced orders or groups of orders. - * Each order contains an arbitrary number of items that may be spent - * (the "offer") along with an arbitrary number of items that must be - * received back by the indicated recipients (the "consideration"). + * @author 0age (0age.eth) + * @custom:coauthor d1ll0n (d1ll0n.eth) + * @custom:coauthor transmissions11 (t11s.eth) + * @custom:coauthor James Wenzel (emo.eth) + * @custom:version 1.2 + * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155 + * marketplace that provides lightweight methods for common routes as + * well as more flexible methods for composing advanced orders or groups + * of orders. Each order contains an arbitrary number of items that may + * be spent (the "offer") along with an arbitrary number of items that + * must be received back by the indicated recipients (the + * "consideration"). */ contract Consideration is ConsiderationInterface, OrderCombiner { /** @@ -47,6 +62,17 @@ contract Consideration is ConsiderationInterface, OrderCombiner { */ constructor(address conduitController) OrderCombiner(conduitController) {} + /** + * @notice Accept native token transfers during execution that may then be + * used to facilitate native token transfers, where any tokens that + * remain will be transferred to the caller. Native tokens are only + * acceptable mid-fulfillment (and not during basic fulfillment). + */ + receive() external payable { + // Ensure the reentrancy guard is currently set to accept native tokens. + _assertAcceptingNativeTokens(); + } + /** * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by * supplying Ether (or other native tokens), ERC20 tokens, an ERC721 @@ -67,18 +93,49 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * this contract (or their chosen conduit if indicated) * before any tokens can be transferred. Also note that * contract recipients of ERC1155 consideration items must - * implement `onERC1155Received` in order to receive those - * items. + * implement `onERC1155Received` to receive those items. * * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillBasicOrder(BasicOrderParameters calldata parameters) - external - payable - override - returns (bool fulfilled) - { + function fulfillBasicOrder( + BasicOrderParameters calldata parameters + ) external payable override returns (bool fulfilled) { + // Validate and fulfill the basic order. + fulfilled = _validateAndFulfillBasicOrder(parameters); + } + + /** + * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by + * supplying Ether (or other native tokens), ERC20 tokens, an ERC721 + * item, or an ERC1155 item as consideration. Six permutations are + * supported: Native token to ERC721, Native token to ERC1155, ERC20 + * to ERC721, ERC20 to ERC1155, ERC721 to ERC20, and ERC1155 to + * ERC20 (with native tokens supplied as msg.value). For an order to + * be eligible for fulfillment via this method, it must contain a + * single offer item (though that item may have a greater amount if + * the item is not an ERC721). An arbitrary number of "additional + * recipients" may also be supplied which will each receive native + * tokens or ERC20 items from the fulfiller as consideration. Refer + * to the documentation for a more comprehensive summary of how to + * utilize this method and what orders are compatible with it. Note + * that this function costs less gas than `fulfillBasicOrder` due to + * the zero bytes in the function selector (0x00000000) which also + * results in earlier function dispatch. + * + * @param parameters Additional information on the fulfilled order. Note + * that the offerer and the fulfiller must first approve + * this contract (or their chosen conduit if indicated) + * before any tokens can be transferred. Also note that + * contract recipients of ERC1155 consideration items must + * implement `onERC1155Received` to receive those items. + * + * @return fulfilled A boolean indicating whether the order has been + * successfully fulfilled. + */ + function fulfillBasicOrder_efficient_6GL6yc( + BasicOrderParameters calldata parameters + ) external payable override returns (bool fulfilled) { // Validate and fulfill the basic order. fulfilled = _validateAndFulfillBasicOrder(parameters); } @@ -89,7 +146,7 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * criteria-based orders or partial filling of orders (though * filling the remainder of a partially-filled order is supported). * - * @param order The order to fulfill. Note that both the + * @custom:param order The order to fulfill. Note that both the * offerer and the fulfiller must first approve * this contract (or the corresponding conduit if * indicated) to transfer any relevant tokens on @@ -100,20 +157,23 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * any, to source the fulfiller's token approvals * from. The zero hash signifies that no conduit * should be used (and direct approvals set on - * Consideration). + * this contract). * * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey) - external - payable - override - returns (bool fulfilled) - { + function fulfillOrder( + /** + * @custom:name order + */ + Order calldata, + bytes32 fulfillerConduitKey + ) external payable override returns (bool fulfilled) { // Convert order to "advanced" order, then validate and fulfill it. fulfilled = _validateAndFulfillAdvancedOrder( - _convertOrderToAdvanced(order), + _toAdvancedOrderReturnType(_decodeOrderAsAdvancedOrder)( + CalldataStart.pptr() + ), new CriteriaResolver[](0), // No criteria resolvers supplied. fulfillerConduitKey, msg.sender @@ -125,53 +185,68 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * items for offer and consideration alongside criteria resolvers * containing specific token identifiers and associated proofs. * - * @param advancedOrder The order to fulfill along with the fraction - * of the order to attempt to fill. Note that - * both the offerer and the fulfiller must first - * approve this contract (or their conduit if - * indicated by the order) to transfer any - * relevant tokens on their behalf and that - * contracts must implement `onERC1155Received` - * to receive ERC1155 tokens as consideration. - * Also note that all offer and consideration - * components must have no remainder after - * multiplication of the respective amount with - * the supplied fraction for the partial fill to - * be considered valid. - * @param criteriaResolvers An array where each element contains a - * reference to a specific offer or - * consideration, a token identifier, and a proof - * that the supplied token identifier is - * contained in the merkle root held by the item - * in question's criteria element. Note that an - * empty criteria indicates that any - * (transferable) token identifier on the token - * in question is valid and that no associated - * proof needs to be supplied. - * @param fulfillerConduitKey A bytes32 value indicating what conduit, if - * any, to source the fulfiller's token approvals - * from. The zero hash signifies that no conduit - * should be used (and direct approvals set on - * Consideration). - * @param recipient The intended recipient for all received items, - * with `address(0)` indicating that the caller - * should receive the items. + * @custom:param advancedOrder The order to fulfill along with the + * fraction of the order to attempt to fill. + * Note that both the offerer and the + * fulfiller must first approve this + * contract (or their conduit if indicated + * by the order) to transfer any relevant + * tokens on their behalf and that contracts + * must implement `onERC1155Received` to + * receive ERC1155 tokens as consideration. + * Also note that all offer and + * consideration components must have no + * remainder after multiplication of the + * respective amount with the supplied + * fraction for the partial fill to be + * considered valid. + * @custom:param criteriaResolvers An array where each element contains a + * reference to a specific offer or + * consideration, a token identifier, and a + * proof that the supplied token identifier + * is contained in the merkle root held by + * the item in question's criteria element. + * Note that an empty criteria indicates + * that any (transferable) token identifier + * on the token in question is valid and + * that no associated proof needs to be + * supplied. + * @param fulfillerConduitKey A bytes32 value indicating what conduit, + * if any, to source the fulfiller's token + * approvals from. The zero hash signifies + * that no conduit should be used (and + * direct approvals set on this contract). + * @param recipient The intended recipient for all received + * items, with `address(0)` indicating that + * the caller should receive the items. * * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ function fulfillAdvancedOrder( - AdvancedOrder calldata advancedOrder, - CriteriaResolver[] calldata criteriaResolvers, + /** + * @custom:name advancedOrder + */ + AdvancedOrder calldata, + /** + * @custom:name criteriaResolvers + */ + CriteriaResolver[] calldata, bytes32 fulfillerConduitKey, address recipient ) external payable override returns (bool fulfilled) { // Validate and fulfill the order. fulfilled = _validateAndFulfillAdvancedOrder( - advancedOrder, - criteriaResolvers, + _toAdvancedOrderReturnType(_decodeAdvancedOrder)( + CalldataStart.pptr() + ), + _toCriteriaResolversReturnType(_decodeCriteriaResolvers)( + CalldataStart.pptr( + Offset_fulfillAdvancedOrder_criteriaResolvers + ) + ), fulfillerConduitKey, - recipient == address(0) ? msg.sender : recipient + _substituteCallerForEmptyRecipient(recipient) ); } @@ -189,27 +264,37 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * partial filling of orders (though filling the remainder of a * partially-filled order is supported). * - * @param orders The orders to fulfill. Note that both - * the offerer and the fulfiller must first - * approve this contract (or the - * corresponding conduit if indicated) to - * transfer any relevant tokens on their - * behalf and that contracts must implement - * `onERC1155Received` to receive ERC1155 - * tokens as consideration. - * @param offerFulfillments An array of FulfillmentComponent arrays - * indicating which offer items to attempt - * to aggregate when preparing executions. - * @param considerationFulfillments An array of FulfillmentComponent arrays - * indicating which consideration items to - * attempt to aggregate when preparing - * executions. - * @param fulfillerConduitKey A bytes32 value indicating what conduit, - * if any, to source the fulfiller's token - * approvals from. The zero hash signifies - * that no conduit should be used (and - * direct approvals set on Consideration). - * @param maximumFulfilled The maximum number of orders to fulfill. + * @custom:param orders The orders to fulfill. Note that + * both the offerer and the + * fulfiller must first approve this + * contract (or the corresponding + * conduit if indicated) to transfer + * any relevant tokens on their + * behalf and that contracts must + * implement `onERC1155Received` to + * receive ERC1155 tokens as + * consideration. + * @custom:param offerFulfillments An array of FulfillmentComponent + * arrays indicating which offer + * items to attempt to aggregate + * when preparing executions. Note + * that any offer items not included + * as part of a fulfillment will be + * sent unaggregated to the caller. + * @custom:param considerationFulfillments An array of FulfillmentComponent + * arrays indicating which + * consideration items to attempt to + * aggregate when preparing + * executions. + * @param fulfillerConduitKey A bytes32 value indicating what + * conduit, if any, to source the + * fulfiller's token approvals from. + * The zero hash signifies that no + * conduit should be used (and + * direct approvals set on this + * contract). + * @param maximumFulfilled The maximum number of orders to + * fulfill. * * @return availableOrders An array of booleans indicating if each order * with an index corresponding to the index of the @@ -219,24 +304,50 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * orders. */ function fulfillAvailableOrders( - Order[] calldata orders, - FulfillmentComponent[][] calldata offerFulfillments, - FulfillmentComponent[][] calldata considerationFulfillments, + /** + * @custom:name orders + */ + Order[] calldata, + /** + * @custom:name offerFulfillments + */ + FulfillmentComponent[][] calldata, + /** + * @custom:name considerationFulfillments + */ + FulfillmentComponent[][] calldata, bytes32 fulfillerConduitKey, uint256 maximumFulfilled ) external payable override - returns (bool[] memory availableOrders, Execution[] memory executions) + returns ( + bool[] memory /* availableOrders */, + Execution[] memory /* executions */ + ) { // Convert orders to "advanced" orders and fulfill all available orders. return _fulfillAvailableAdvancedOrders( - _convertOrdersToAdvanced(orders), // Convert to advanced orders. + _toAdvancedOrdersReturnType(_decodeOrdersAsAdvancedOrders)( + CalldataStart.pptr() + ), // Convert to advanced orders. new CriteriaResolver[](0), // No criteria resolvers supplied. - offerFulfillments, - considerationFulfillments, + _toNestedFulfillmentComponentsReturnType( + _decodeNestedFulfillmentComponents + )( + CalldataStart.pptr( + Offset_fulfillAvailableOrders_offerFulfillments + ) + ), + _toNestedFulfillmentComponentsReturnType( + _decodeNestedFulfillmentComponents + )( + CalldataStart.pptr( + Offset_fulfillAvailableOrders_considerationFulfillments + ) + ), fulfillerConduitKey, msg.sender, maximumFulfilled @@ -256,49 +367,63 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * recipient, respectively. Note that a failing item transfer or an * issue with order formatting will cause the entire batch to fail. * - * @param advancedOrders The orders to fulfill along with the - * fraction of those orders to attempt to - * fill. Note that both the offerer and the - * fulfiller must first approve this - * contract (or their conduit if indicated - * by the order) to transfer any relevant - * tokens on their behalf and that - * contracts must implement - * `onERC1155Received` in order to receive - * ERC1155 tokens as consideration. Also - * note that all offer and consideration - * components must have no remainder after - * multiplication of the respective amount - * with the supplied fraction for an - * order's partial fill amount to be - * considered valid. - * @param criteriaResolvers An array where each element contains a - * reference to a specific offer or - * consideration, a token identifier, and a - * proof that the supplied token identifier - * is contained in the merkle root held by - * the item in question's criteria element. - * Note that an empty criteria indicates - * that any (transferable) token - * identifier on the token in question is - * valid and that no associated proof needs - * to be supplied. - * @param offerFulfillments An array of FulfillmentComponent arrays - * indicating which offer items to attempt - * to aggregate when preparing executions. - * @param considerationFulfillments An array of FulfillmentComponent arrays - * indicating which consideration items to - * attempt to aggregate when preparing - * executions. - * @param fulfillerConduitKey A bytes32 value indicating what conduit, - * if any, to source the fulfiller's token - * approvals from. The zero hash signifies - * that no conduit should be used (and - * direct approvals set on Consideration). - * @param recipient The intended recipient for all received - * items, with `address(0)` indicating that - * the caller should receive the items. - * @param maximumFulfilled The maximum number of orders to fulfill. + * @custom:param advancedOrders The orders to fulfill along with + * the fraction of those orders to + * attempt to fill. Note that both + * the offerer and the fulfiller + * must first approve this contract + * (or their conduit if indicated by + * the order) to transfer any + * relevant tokens on their behalf + * and that contracts must implement + * `onERC1155Received` to receive + * ERC1155 tokens as consideration. + * Also note that all offer and + * consideration components must + * have no remainder after + * multiplication of the respective + * amount with the supplied fraction + * for an order's partial fill + * amount to be considered valid. + * @custom:param criteriaResolvers An array where each element + * contains a reference to a + * specific offer or consideration, + * a token identifier, and a proof + * that the supplied token + * identifier is contained in the + * merkle root held by the item in + * question's criteria element. Note + * that an empty criteria indicates + * that any (transferable) token + * identifier on the token in + * question is valid and that no + * associated proof needs to be + * supplied. + * @custom:param offerFulfillments An array of FulfillmentComponent + * arrays indicating which offer + * items to attempt to aggregate + * when preparing executions. Note + * that any offer items not included + * as part of a fulfillment will be + * sent unaggregated to the caller. + * @custom:param considerationFulfillments An array of FulfillmentComponent + * arrays indicating which + * consideration items to attempt to + * aggregate when preparing + * executions. + * @param fulfillerConduitKey A bytes32 value indicating what + * conduit, if any, to source the + * fulfiller's token approvals from. + * The zero hash signifies that no + * conduit should be used (and + * direct approvals set on this + * contract). + * @param recipient The intended recipient for all + * received items, with `address(0)` + * indicating that the caller should + * receive the offer items. + * @param maximumFulfilled The maximum number of orders to + * fulfill. * * @return availableOrders An array of booleans indicating if each order * with an index corresponding to the index of the @@ -308,10 +433,22 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * orders. */ function fulfillAvailableAdvancedOrders( - AdvancedOrder[] memory advancedOrders, - CriteriaResolver[] calldata criteriaResolvers, - FulfillmentComponent[][] calldata offerFulfillments, - FulfillmentComponent[][] calldata considerationFulfillments, + /** + * @custom:name advancedOrders + */ + AdvancedOrder[] calldata, + /** + * @custom:name criteriaResolvers + */ + CriteriaResolver[] calldata, + /** + * @custom:name offerFulfillments + */ + FulfillmentComponent[][] calldata, + /** + * @custom:name considerationFulfillments + */ + FulfillmentComponent[][] calldata, bytes32 fulfillerConduitKey, address recipient, uint256 maximumFulfilled @@ -319,17 +456,38 @@ contract Consideration is ConsiderationInterface, OrderCombiner { external payable override - returns (bool[] memory availableOrders, Execution[] memory executions) + returns ( + bool[] memory /* availableOrders */, + Execution[] memory /* executions */ + ) { // Fulfill all available orders. return _fulfillAvailableAdvancedOrders( - advancedOrders, - criteriaResolvers, - offerFulfillments, - considerationFulfillments, + _toAdvancedOrdersReturnType(_decodeAdvancedOrders)( + CalldataStart.pptr() + ), + _toCriteriaResolversReturnType(_decodeCriteriaResolvers)( + CalldataStart.pptr( + Offset_fulfillAvailableAdvancedOrders_criteriaResolvers + ) + ), + _toNestedFulfillmentComponentsReturnType( + _decodeNestedFulfillmentComponents + )( + CalldataStart.pptr( + Offset_fulfillAvailableAdvancedOrders_offerFulfillments + ) + ), + _toNestedFulfillmentComponentsReturnType( + _decodeNestedFulfillmentComponents + )( + CalldataStart.pptr( + Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts + ) + ), fulfillerConduitKey, - recipient == address(0) ? msg.sender : recipient, + _substituteCallerForEmptyRecipient(recipient), maximumFulfilled ); } @@ -340,84 +498,135 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * fulfillments allocating offer components to consideration * components. Note that this function does not support * criteria-based or partial filling of orders (though filling the - * remainder of a partially-filled order is supported). - * - * @param orders The orders to match. Note that both the offerer - * and fulfiller on each order must first approve - * this contract (or their conduit if indicated by - * the order) to transfer any relevant tokens on - * their behalf and each consideration recipient - * must implement `onERC1155Received` in order to - * receive ERC1155 tokens. - * @param fulfillments An array of elements allocating offer components - * to consideration components. Note that each - * consideration component must be fully met in - * order for the match operation to be valid. + * remainder of a partially-filled order is supported). Any unspent + * offer item amounts or native tokens will be transferred to the + * caller. + * + * @custom:param orders The orders to match. Note that both the + * offerer and fulfiller on each order must first + * approve this contract (or their conduit if + * indicated by the order) to transfer any + * relevant tokens on their behalf and each + * consideration recipient must implement + * `onERC1155Received` to receive ERC1155 tokens. + * @custom:param fulfillments An array of elements allocating offer + * components to consideration components. Note + * that each consideration component must be + * fully met for the match operation to be valid, + * and that any unspent offer items will be sent + * unaggregated to the caller. * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or native + * tokens will not be reflected as part of this array. */ function matchOrders( - Order[] calldata orders, - Fulfillment[] calldata fulfillments - ) external payable override returns (Execution[] memory executions) { + /** + * @custom:name orders + */ + Order[] calldata, + /** + * @custom:name fulfillments + */ + Fulfillment[] calldata + ) external payable override returns (Execution[] memory /* executions */) { // Convert to advanced, validate, and match orders using fulfillments. return _matchAdvancedOrders( - _convertOrdersToAdvanced(orders), + _toAdvancedOrdersReturnType(_decodeOrdersAsAdvancedOrders)( + CalldataStart.pptr() + ), new CriteriaResolver[](0), // No criteria resolvers supplied. - fulfillments + _toFulfillmentsReturnType(_decodeFulfillments)( + CalldataStart.pptr(Offset_matchOrders_fulfillments) + ), + msg.sender ); } /** - * @notice Match an arbitrary number of full or partial orders, each with an - * arbitrary number of items for offer and consideration, supplying - * criteria resolvers containing specific token identifiers and - * associated proofs as well as fulfillments allocating offer - * components to consideration components. - * - * @param advancedOrders The advanced orders to match. Note that both the - * offerer and fulfiller on each order must first - * approve this contract (or their conduit if - * indicated by the order) to transfer any relevant - * tokens on their behalf and each consideration - * recipient must implement `onERC1155Received` in - * order to receive ERC1155 tokens. Also note that - * the offer and consideration components for each - * order must have no remainder after multiplying - * the respective amount with the supplied fraction - * in order for the group of partial fills to be - * considered valid. - * @param criteriaResolvers An array where each element contains a reference - * to a specific order as well as that order's - * offer or consideration, a token identifier, and - * a proof that the supplied token identifier is - * contained in the order's merkle root. Note that - * an empty root indicates that any (transferable) - * token identifier is valid and that no associated - * proof needs to be supplied. - * @param fulfillments An array of elements allocating offer components - * to consideration components. Note that each - * consideration component must be fully met in - * order for the match operation to be valid. + * @notice Match an arbitrary number of full, partial, or contract orders, + * each with an arbitrary number of items for offer and + * consideration, supplying criteria resolvers containing specific + * token identifiers and associated proofs as well as fulfillments + * allocating offer components to consideration components. Any + * unspent offer item amounts will be transferred to the designated + * recipient (with the null address signifying to use the caller) + * and any unspent native tokens will be returned to the caller. + * + * @custom:param advancedOrders The advanced orders to match. Note that + * both the offerer and fulfiller on each + * order must first approve this contract + * (or their conduit if indicated by the + * order) to transfer any relevant tokens on + * their behalf and each consideration + * recipient must implement + * `onERC1155Received` to receive ERC1155 + * tokens. Also note that the offer and + * consideration components for each order + * must have no remainder after multiplying + * the respective amount with the supplied + * fraction for the group of partial fills + * to be considered valid. + * @custom:param criteriaResolvers An array where each element contains a + * reference to a specific offer or + * consideration, a token identifier, and a + * proof that the supplied token identifier + * is contained in the merkle root held by + * the item in question's criteria element. + * Note that an empty criteria indicates + * that any (transferable) token identifier + * on the token in question is valid and + * that no associated proof needs to be + * supplied. + * @custom:param fulfillments An array of elements allocating offer + * components to consideration components. + * Note that each consideration component + * must be fully met for the match operation + * to be valid, and that any unspent offer + * items will be sent unaggregated to the + * designated recipient. + * @param recipient The intended recipient for all unspent + * offer item amounts, or the caller if the + * null address is supplied. * * @return executions An array of elements indicating the sequence of - * transfers performed as part of matching the given - * orders. + * transfers performed as part of matching the given + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of this + * array. */ function matchAdvancedOrders( - AdvancedOrder[] memory advancedOrders, - CriteriaResolver[] calldata criteriaResolvers, - Fulfillment[] calldata fulfillments - ) external payable override returns (Execution[] memory executions) { + /** + * @custom:name advancedOrders + */ + AdvancedOrder[] calldata, + /** + * @custom:name criteriaResolvers + */ + CriteriaResolver[] calldata, + /** + * @custom:name fulfillments + */ + Fulfillment[] calldata, + address recipient + ) external payable override returns (Execution[] memory /* executions */) { // Validate and match the advanced orders using supplied fulfillments. return _matchAdvancedOrders( - advancedOrders, - criteriaResolvers, - fulfillments + _toAdvancedOrdersReturnType(_decodeAdvancedOrders)( + CalldataStart.pptr() + ), + _toCriteriaResolversReturnType(_decodeCriteriaResolvers)( + CalldataStart.pptr( + Offset_matchAdvancedOrders_criteriaResolvers + ) + ), + _toFulfillmentsReturnType(_decodeFulfillments)( + CalldataStart.pptr(Offset_matchAdvancedOrders_fulfillments) + ), + _substituteCallerForEmptyRecipient(recipient) ); } @@ -432,11 +641,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * @return cancelled A boolean indicating whether the supplied orders have * been successfully cancelled. */ - function cancel(OrderComponents[] calldata orders) - external - override - returns (bool cancelled) - { + function cancel( + OrderComponents[] calldata orders + ) external override returns (bool cancelled) { // Cancel the orders. cancelled = _cancel(orders); } @@ -451,18 +658,19 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * that anyone can validate a signed order, but only the offerer can * validate an order without supplying a signature. * - * @param orders The orders to validate. + * @custom:param orders The orders to validate. * * @return validated A boolean indicating whether the supplied orders have * been successfully validated. */ - function validate(Order[] calldata orders) - external - override - returns (bool validated) - { - // Validate the orders. - validated = _validate(orders); + function validate( + /** + * @custom:name orders + */ + Order[] calldata + ) external override returns (bool /* validated */) { + return + _validate(_toOrdersReturnType(_decodeOrders)(CalldataStart.pptr())); } /** @@ -473,46 +681,44 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * @return newCounter The new counter. */ function incrementCounter() external override returns (uint256 newCounter) { - // Increment current counter for the supplied offerer. + // Increment current counter for the supplied offerer. Note that the + // counter is incremented by a large, quasi-random interval. newCounter = _incrementCounter(); } /** * @notice Retrieve the order hash for a given order. * - * @param order The components of the order. + * @custom:param order The components of the order. * * @return orderHash The order hash. */ - function getOrderHash(OrderComponents calldata order) - external - view - override - returns (bytes32 orderHash) - { + function getOrderHash( + /** + * @custom:name order + */ + OrderComponents calldata + ) external view override returns (bytes32 orderHash) { + CalldataPointer orderPointer = CalldataStart.pptr(); + // Derive order hash by supplying order parameters along with counter. orderHash = _deriveOrderHash( - OrderParameters( - order.offerer, - order.zone, - order.offer, - order.consideration, - order.orderType, - order.startTime, - order.endTime, - order.zoneHash, - order.salt, - order.conduitKey, - order.consideration.length - ), - order.counter + _toOrderParametersReturnType( + _decodeOrderComponentsAsOrderParameters + )(orderPointer), + // Read order counter + orderPointer.offset(OrderParameters_counter_offset).readUint256() ); } /** * @notice Retrieve the status of a given order by hash, including whether * the order has been cancelled or validated and the fraction of the - * order that has been filled. + * order that has been filled. Since the _orderStatus[orderHash] + * does not get set for contract orders, getOrderStatus will always + * return (false, false, 0, 0) for those hashes. Note that this + * function is susceptible to view reentrancy and so should be used + * with care when calling from other contracts. * * @param orderHash The order hash in question. * @@ -526,7 +732,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * @return totalSize The total size of the order that is either filled or * unfilled (i.e. the "denominator"). */ - function getOrderStatus(bytes32 orderHash) + function getOrderStatus( + bytes32 orderHash + ) external view override @@ -548,12 +756,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * * @return counter The current counter. */ - function getCounter(address offerer) - external - view - override - returns (uint256 counter) - { + function getCounter( + address offerer + ) external view override returns (uint256 counter) { // Return the counter for the supplied offerer. counter = _getCounter(offerer); } @@ -579,6 +784,21 @@ contract Consideration is ConsiderationInterface, OrderCombiner { return _information(); } + /** + * @dev Gets the contract offerer nonce for the specified contract offerer. + * Note that this function is susceptible to view reentrancy and so + * should be used with care when calling from other contracts. + * + * @param contractOfferer The contract offerer for which to get the nonce. + * + * @return nonce The contract offerer nonce. + */ + function getContractOffererNonce( + address contractOfferer + ) external view override returns (uint256 nonce) { + nonce = _contractNonces[contractOfferer]; + } + /** * @notice Retrieve the name of this contract. * @@ -588,9 +808,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner { external pure override - returns (string memory contractName) + returns (string memory /* contractName */) { // Return the name of the contract. - contractName = _name(); + return _name(); } } diff --git a/contracts/lib/ConsiderationBase.sol b/contracts/lib/ConsiderationBase.sol index f7e46be2a..074fe2cf9 100644 --- a/contracts/lib/ConsiderationBase.sol +++ b/contracts/lib/ConsiderationBase.sol @@ -1,24 +1,45 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -// prettier-ignore import { ConduitControllerInterface } from "../interfaces/ConduitControllerInterface.sol"; -// prettier-ignore import { ConsiderationEventsAndErrors } from "../interfaces/ConsiderationEventsAndErrors.sol"; -import "./ConsiderationConstants.sol"; +import { + EIP712_domainData_chainId_offset, + EIP712_domainData_nameHash_offset, + EIP712_domainData_size, + EIP712_domainData_verifyingContract_offset, + EIP712_domainData_versionHash_offset, + FreeMemoryPointerSlot, + NameLengthPtr, + NameWithLength, + OneWord, + OneWordShift, + Slot0x80, + ThreeWords, + ZeroSlot +} from "./ConsiderationConstants.sol"; + +import { ConsiderationDecoder } from "./ConsiderationDecoder.sol"; +import { ConsiderationEncoder } from "./ConsiderationEncoder.sol"; + +import { TypehashDirectory } from "./TypehashDirectory.sol"; /** * @title ConsiderationBase * @author 0age * @notice ConsiderationBase contains immutable constants and constructor logic. */ -contract ConsiderationBase is ConsiderationEventsAndErrors { +contract ConsiderationBase is + ConsiderationDecoder, + ConsiderationEncoder, + ConsiderationEventsAndErrors +{ // Precompute hashes, original chainId, and domain separator on deployment. bytes32 internal immutable _NAME_HASH; bytes32 internal immutable _VERSION_HASH; @@ -32,6 +53,9 @@ contract ConsiderationBase is ConsiderationEventsAndErrors { // Allow for interaction with the conduit controller. ConduitControllerInterface internal immutable _CONDUIT_CONTROLLER; + // BulkOrder typehash storage + TypehashDirectory internal immutable _BULK_ORDER_TYPEHASH_DIRECTORY; + // Cache the conduit creation code hash used by the conduit controller. bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH; @@ -54,6 +78,8 @@ contract ConsiderationBase is ConsiderationEventsAndErrors { _ORDER_TYPEHASH ) = _deriveTypehashes(); + _BULK_ORDER_TYPEHASH_DIRECTORY = new TypehashDirectory(); + // Store the current chainId and derive the current domain separator. _CHAIN_ID = block.chainid; _DOMAIN_SEPARATOR = _deriveDomainSeparator(); @@ -70,19 +96,48 @@ contract ConsiderationBase is ConsiderationEventsAndErrors { /** * @dev Internal view function to derive the EIP-712 domain separator. * - * @return The derived domain separator. + * @return domainSeparator The derived domain separator. */ - function _deriveDomainSeparator() internal view returns (bytes32) { - // prettier-ignore - return keccak256( - abi.encode( - _EIP_712_DOMAIN_TYPEHASH, - _NAME_HASH, - _VERSION_HASH, - block.chainid, - address(this) - ) - ); + function _deriveDomainSeparator() + internal + view + returns (bytes32 domainSeparator) + { + bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH; + bytes32 nameHash = _NAME_HASH; + bytes32 versionHash = _VERSION_HASH; + + // Leverage scratch space and other memory to perform an efficient hash. + assembly { + // Retrieve the free memory pointer; it will be replaced afterwards. + let freeMemoryPointer := mload(FreeMemoryPointerSlot) + + // Retrieve value at 0x80; it will also be replaced afterwards. + let slot0x80 := mload(Slot0x80) + + // Place typehash, name hash, and version hash at start of memory. + mstore(0, typehash) + mstore(EIP712_domainData_nameHash_offset, nameHash) + mstore(EIP712_domainData_versionHash_offset, versionHash) + + // Place chainId in the next memory location. + mstore(EIP712_domainData_chainId_offset, chainid()) + + // Place the address of this contract in the next memory location. + mstore(EIP712_domainData_verifyingContract_offset, address()) + + // Hash relevant region of memory to derive the domain separator. + domainSeparator := keccak256(0, EIP712_domainData_size) + + // Restore the free memory pointer. + mstore(FreeMemoryPointerSlot, freeMemoryPointer) + + // Restore the zero slot to zero. + mstore(ZeroSlot, 0) + + // Restore the value at 0x80. + mstore(Slot0x80, slot0x80) + } } /** @@ -152,60 +207,56 @@ contract ConsiderationBase is ConsiderationEventsAndErrors { nameHash = keccak256(bytes(_nameString())); // Derive hash of the version string of the contract. - versionHash = keccak256(bytes("1.1")); + versionHash = keccak256(bytes("1.2")); // Construct the OfferItem type string. - // prettier-ignore - bytes memory offerItemTypeString = abi.encodePacked( - "OfferItem(", - "uint8 itemType,", - "address token,", - "uint256 identifierOrCriteria,", - "uint256 startAmount,", - "uint256 endAmount", + bytes memory offerItemTypeString = bytes( + "OfferItem(" + "uint8 itemType," + "address token," + "uint256 identifierOrCriteria," + "uint256 startAmount," + "uint256 endAmount" ")" ); // Construct the ConsiderationItem type string. - // prettier-ignore - bytes memory considerationItemTypeString = abi.encodePacked( - "ConsiderationItem(", - "uint8 itemType,", - "address token,", - "uint256 identifierOrCriteria,", - "uint256 startAmount,", - "uint256 endAmount,", - "address recipient", + bytes memory considerationItemTypeString = bytes( + "ConsiderationItem(" + "uint8 itemType," + "address token," + "uint256 identifierOrCriteria," + "uint256 startAmount," + "uint256 endAmount," + "address recipient" ")" ); // Construct the OrderComponents type string, not including the above. - // prettier-ignore - bytes memory orderComponentsPartialTypeString = abi.encodePacked( - "OrderComponents(", - "address offerer,", - "address zone,", - "OfferItem[] offer,", - "ConsiderationItem[] consideration,", - "uint8 orderType,", - "uint256 startTime,", - "uint256 endTime,", - "bytes32 zoneHash,", - "uint256 salt,", - "bytes32 conduitKey,", - "uint256 counter", + bytes memory orderComponentsPartialTypeString = bytes( + "OrderComponents(" + "address offerer," + "address zone," + "OfferItem[] offer," + "ConsiderationItem[] consideration," + "uint8 orderType," + "uint256 startTime," + "uint256 endTime," + "bytes32 zoneHash," + "uint256 salt," + "bytes32 conduitKey," + "uint256 counter" ")" ); // Construct the primary EIP-712 domain type string. - // prettier-ignore eip712DomainTypehash = keccak256( - abi.encodePacked( - "EIP712Domain(", - "string name,", - "string version,", - "uint256 chainId,", - "address verifyingContract", + bytes( + "EIP712Domain(" + "string name," + "string version," + "uint256 chainId," + "address verifyingContract" ")" ) ); @@ -216,13 +267,24 @@ contract ConsiderationBase is ConsiderationEventsAndErrors { // Derive ConsiderationItem type hash using corresponding type string. considerationItemTypehash = keccak256(considerationItemTypeString); - // Derive OrderItem type hash via combination of relevant type strings. - orderTypehash = keccak256( - abi.encodePacked( - orderComponentsPartialTypeString, - considerationItemTypeString, - offerItemTypeString - ) + bytes memory orderTypeString = bytes.concat( + orderComponentsPartialTypeString, + considerationItemTypeString, + offerItemTypeString ); + + // Derive OrderItem type hash via combination of relevant type strings. + orderTypehash = keccak256(orderTypeString); + } + + function _lookupBulkOrderTypehash( + uint256 treeHeight + ) internal view returns (bytes32 typeHash) { + TypehashDirectory directory = _BULK_ORDER_TYPEHASH_DIRECTORY; + assembly { + let typeHashOffset := add(1, shl(OneWordShift, sub(treeHeight, 1))) + extcodecopy(directory, 0, typeHashOffset, OneWord) + typeHash := mload(0) + } } } diff --git a/contracts/lib/ConsiderationConstants.sol b/contracts/lib/ConsiderationConstants.sol index 12f4b0963..23848c64d 100644 --- a/contracts/lib/ConsiderationConstants.sol +++ b/contracts/lib/ConsiderationConstants.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /* * -------------------------- Disambiguation & Other Notes --------------------- @@ -8,7 +8,7 @@ pragma solidity >=0.8.7; * offset or pointer to the body of a dynamic type. In calldata, the head * is always an offset (relative to the parent object), while in memory, * the head is always the pointer to the body. More information found here: - * https://docs.soliditylang.org/en/v0.8.14/abi-spec.html#argument-encoding + * https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#argument-encoding * - Note that the length of an array is separate from and precedes the * head of the array. * @@ -38,30 +38,62 @@ pragma solidity >=0.8.7; // Name is right padded, so it touches the length which is left padded. This // enables writing both values at once. Length goes at byte 95 in memory, and // name fills bytes 96-109, so both values can be written left-padded to 77. -uint256 constant NameLengthPtr = 77; +uint256 constant NameLengthPtr = 0x4D; uint256 constant NameWithLength = 0x0d436F6E73696465726174696F6E; -uint256 constant Version = 0x312e31; -uint256 constant Version_length = 3; -uint256 constant Version_shift = 0xe8; +uint256 constant information_version_offset = 0; +uint256 constant information_version_cd_offset = 0x60; +uint256 constant information_domainSeparator_offset = 0x20; +uint256 constant information_conduitController_offset = 0x40; +uint256 constant information_versionLengthPtr = 0x63; +uint256 constant information_versionWithLength = 0x03312e32; // 1.2 +uint256 constant information_length = 0xa0; uint256 constant _NOT_ENTERED = 1; uint256 constant _ENTERED = 2; +uint256 constant _ENTERED_AND_ACCEPTING_NATIVE_TOKENS = 3; + +uint256 constant Offset_fulfillAdvancedOrder_criteriaResolvers = 0x20; +uint256 constant Offset_fulfillAvailableOrders_offerFulfillments = 0x20; +uint256 constant Offset_fulfillAvailableOrders_considerationFulfillments = 0x40; +uint256 constant Offset_fulfillAvailableAdvancedOrders_criteriaResolvers = 0x20; +uint256 constant Offset_fulfillAvailableAdvancedOrders_offerFulfillments = 0x40; +uint256 constant Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts = ( + 0x60 +); + +uint256 constant Offset_matchOrders_fulfillments = 0x20; + +uint256 constant Offset_matchAdvancedOrders_criteriaResolvers = 0x20; +uint256 constant Offset_matchAdvancedOrders_fulfillments = 0x40; // Common Offsets // Offsets for identically positioned fields shared by: // OfferItem, ConsiderationItem, SpentItem, ReceivedItem +uint256 constant Selector_length = 0x4; + uint256 constant Common_token_offset = 0x20; uint256 constant Common_identifier_offset = 0x40; uint256 constant Common_amount_offset = 0x60; +uint256 constant Common_endAmount_offset = 0x80; + +uint256 constant SpentItem_size = 0x80; +uint256 constant SpentItem_size_shift = 0x7; + +uint256 constant OfferItem_size = 0xa0; +uint256 constant OfferItem_size_with_length = 0xc0; +uint256 constant ReceivedItem_size_excluding_recipient = 0x80; uint256 constant ReceivedItem_size = 0xa0; uint256 constant ReceivedItem_amount_offset = 0x60; uint256 constant ReceivedItem_recipient_offset = 0x80; uint256 constant ReceivedItem_CommonParams_size = 0x60; +uint256 constant ConsiderationItem_size = 0xc0; +uint256 constant ConsiderationItem_size_with_length = 0xe0; + uint256 constant ConsiderationItem_recipient_offset = 0xa0; // Store the same constant in an abbreviated format for a line length fix. uint256 constant ConsiderItem_recipient_offset = 0xa0; @@ -69,39 +101,43 @@ uint256 constant ConsiderItem_recipient_offset = 0xa0; uint256 constant Execution_offerer_offset = 0x20; uint256 constant Execution_conduit_offset = 0x40; -uint256 constant InvalidFulfillmentComponentData_error_signature = ( - 0x7fda727900000000000000000000000000000000000000000000000000000000 -); -uint256 constant InvalidFulfillmentComponentData_error_len = 0x04; - -uint256 constant Panic_error_signature = ( - 0x4e487b7100000000000000000000000000000000000000000000000000000000 -); -uint256 constant Panic_error_offset = 0x04; -uint256 constant Panic_error_length = 0x24; -uint256 constant Panic_arithmetic = 0x11; - -uint256 constant MissingItemAmount_error_signature = ( - 0x91b3e51400000000000000000000000000000000000000000000000000000000 -); -uint256 constant MissingItemAmount_error_len = 0x04; - +// uint256 constant OrderParameters_offerer_offset = 0x00; +uint256 constant OrderParameters_zone_offset = 0x20; uint256 constant OrderParameters_offer_head_offset = 0x40; uint256 constant OrderParameters_consideration_head_offset = 0x60; +// uint256 constant OrderParameters_orderType_offset = 0x80; +uint256 constant OrderParameters_startTime_offset = 0xa0; +uint256 constant OrderParameters_endTime_offset = 0xc0; +uint256 constant OrderParameters_zoneHash_offset = 0xe0; +// uint256 constant OrderParameters_salt_offset = 0x100; uint256 constant OrderParameters_conduit_offset = 0x120; uint256 constant OrderParameters_counter_offset = 0x140; uint256 constant Fulfillment_itemIndex_offset = 0x20; +uint256 constant AdvancedOrder_head_size = 0xa0; uint256 constant AdvancedOrder_numerator_offset = 0x20; +uint256 constant AdvancedOrder_denominator_offset = 0x40; +uint256 constant AdvancedOrder_signature_offset = 0x60; +uint256 constant AdvancedOrder_extraData_offset = 0x80; + +uint256 constant OrderStatus_ValidatedAndNotCancelled = 1; +uint256 constant OrderStatus_filledNumerator_offset = 0x10; +uint256 constant OrderStatus_filledDenominator_offset = 0x88; -uint256 constant AlmostOneWord = 0x1f; +uint256 constant ThirtyOneBytes = 0x1f; uint256 constant OneWord = 0x20; uint256 constant TwoWords = 0x40; uint256 constant ThreeWords = 0x60; uint256 constant FourWords = 0x80; uint256 constant FiveWords = 0xa0; +uint256 constant OneWordShift = 0x5; +uint256 constant TwoWordsShift = 0x6; + +uint256 constant SixtyThreeBytes = 0x3f; +uint256 constant OnlyFullWordMask = 0xffffffe0; + uint256 constant FreeMemoryPointerSlot = 0x40; uint256 constant ZeroSlot = 0x60; uint256 constant DefaultFreeMemoryPointer = 0x80; @@ -109,19 +145,47 @@ uint256 constant DefaultFreeMemoryPointer = 0x80; uint256 constant Slot0x80 = 0x80; uint256 constant Slot0xA0 = 0xa0; -uint256 constant BasicOrder_endAmount_cdPtr = 0x104; +// uint256 constant BasicOrder_endAmount_cdPtr = 0x104; uint256 constant BasicOrder_common_params_size = 0xa0; uint256 constant BasicOrder_considerationHashesArray_ptr = 0x160; +uint256 constant BasicOrder_receivedItemByteMap = ( + 0x0000010102030000000000000000000000000000000000000000000000000000 +); +uint256 constant BasicOrder_offeredItemByteMap = ( + 0x0203020301010000000000000000000000000000000000000000000000000000 +); + +bytes32 constant OrdersMatchedTopic0 = ( + 0x4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e7 +); uint256 constant EIP712_Order_size = 0x180; uint256 constant EIP712_OfferItem_size = 0xc0; uint256 constant EIP712_ConsiderationItem_size = 0xe0; -uint256 constant AdditionalRecipients_size = 0x40; +uint256 constant AdditionalRecipient_size = 0x40; +uint256 constant AdditionalRecipient_size_shift = 0x6; uint256 constant EIP712_DomainSeparator_offset = 0x02; uint256 constant EIP712_OrderHash_offset = 0x22; uint256 constant EIP712_DigestPayload_size = 0x42; +uint256 constant EIP712_domainData_nameHash_offset = 0x20; +uint256 constant EIP712_domainData_versionHash_offset = 0x40; +uint256 constant EIP712_domainData_chainId_offset = 0x60; +uint256 constant EIP712_domainData_verifyingContract_offset = 0x80; +uint256 constant EIP712_domainData_size = 0xa0; + +// Minimum BulkOrder proof size: 64 bytes for signature + 3 for key + 32 for 1 +// sibling. Maximum BulkOrder proof size: 65 bytes for signature + 3 for key + +// 768 for 24 siblings. + +uint256 constant BulkOrderProof_minSize = 0x63; +uint256 constant BulkOrderProof_rangeSize = 0x2e2; +uint256 constant BulkOrderProof_lengthAdjustmentBeforeMask = 0x1d; +uint256 constant BulkOrderProof_lengthRangeAfterMask = 0x2; +uint256 constant BulkOrderProof_keyShift = 0xe8; +uint256 constant BulkOrderProof_keySize = 0x3; + uint256 constant receivedItemsHash_ptr = 0x60; /* @@ -172,6 +236,11 @@ uint256 constant OrderFulfilled_baseOffset = 0x180; uint256 constant OrderFulfilled_consideration_length_baseOffset = 0x2a0; uint256 constant OrderFulfilled_offer_length_baseOffset = 0x200; +// Related constants used for restricted order checks on basic orders. +uint256 constant OrderFulfilled_baseDataSize = 0x160; +// uint256 constant ValidateOrder_offerDataOffset = 0x184; +// uint256 constant RatifyOrder_offerDataOffset = 0xc4; + // uint256 constant OrderFulfilled_orderHash_offset = 0x00; uint256 constant OrderFulfilled_fulfiller_offset = 0x20; uint256 constant OrderFulfilled_offer_head_offset = 0x40; @@ -182,16 +251,16 @@ uint256 constant OrderFulfilled_consideration_body_offset = 0x120; // BasicOrderParameters uint256 constant BasicOrder_parameters_cdPtr = 0x04; uint256 constant BasicOrder_considerationToken_cdPtr = 0x24; -// uint256 constant BasicOrder_considerationIdentifier_cdPtr = 0x44; +uint256 constant BasicOrder_considerationIdentifier_cdPtr = 0x44; uint256 constant BasicOrder_considerationAmount_cdPtr = 0x64; uint256 constant BasicOrder_offerer_cdPtr = 0x84; uint256 constant BasicOrder_zone_cdPtr = 0xa4; uint256 constant BasicOrder_offerToken_cdPtr = 0xc4; -// uint256 constant BasicOrder_offerIdentifier_cdPtr = 0xe4; +uint256 constant BasicOrder_offerIdentifier_cdPtr = 0xe4; uint256 constant BasicOrder_offerAmount_cdPtr = 0x104; uint256 constant BasicOrder_basicOrderType_cdPtr = 0x124; uint256 constant BasicOrder_startTime_cdPtr = 0x144; -// uint256 constant BasicOrder_endTime_cdPtr = 0x164; +uint256 constant BasicOrder_endTime_cdPtr = 0x164; // uint256 constant BasicOrder_zoneHash_cdPtr = 0x184; // uint256 constant BasicOrder_salt_cdPtr = 0x1a4; uint256 constant BasicOrder_offererConduit_cdPtr = 0x1c4; @@ -201,9 +270,7 @@ uint256 constant BasicOrder_additionalRecipients_head_cdPtr = 0x224; uint256 constant BasicOrder_signature_cdPtr = 0x244; uint256 constant BasicOrder_additionalRecipients_length_cdPtr = 0x264; uint256 constant BasicOrder_additionalRecipients_data_cdPtr = 0x284; - uint256 constant BasicOrder_parameters_ptr = 0x20; - uint256 constant BasicOrder_basicOrderType_range = 0x18; // 24 values /* @@ -235,7 +302,7 @@ uint256 constant BasicOrder_considerationItem_endAmount_ptr = 0x120; * - 0x100: startAmount * - 0x120: endAmount */ -uint256 constant BasicOrder_offerItem_typeHash_ptr = DefaultFreeMemoryPointer; +uint256 constant BasicOrder_offerItem_typeHash_ptr = 0x80; uint256 constant BasicOrder_offerItem_itemType_ptr = 0xa0; uint256 constant BasicOrder_offerItem_token_ptr = 0xc0; // uint256 constant BasicOrder_offerItem_identifier_ptr = 0xe0; @@ -272,6 +339,11 @@ uint256 constant BasicOrder_order_startTime_ptr = 0x140; uint256 constant BasicOrder_order_counter_ptr = 0x1e0; uint256 constant BasicOrder_additionalRecipients_head_ptr = 0x240; uint256 constant BasicOrder_signature_ptr = 0x260; +uint256 constant BasicOrder_startTimeThroughZoneHash_size = 0x60; + +uint256 constant ContractOrder_orderHash_offerer_shift = 0x60; + +uint256 constant Counter_blockhash_shift = 0x80; // Signature-related bytes32 constant EIP2098_allButHighestBitMask = ( @@ -287,28 +359,18 @@ uint256 constant ECDSA_signature_v_offset = 0x60; bytes32 constant EIP1271_isValidSignature_selector = ( 0x1626ba7e00000000000000000000000000000000000000000000000000000000 ); -uint256 constant EIP1271_isValidSignature_signatureHead_negativeOffset = 0x20; uint256 constant EIP1271_isValidSignature_digest_negativeOffset = 0x40; uint256 constant EIP1271_isValidSignature_selector_negativeOffset = 0x44; uint256 constant EIP1271_isValidSignature_calldata_baseLength = 0x64; - uint256 constant EIP1271_isValidSignature_signature_head_offset = 0x40; -// abi.encodeWithSignature("NoContract(address)") -uint256 constant NoContract_error_signature = ( - 0x5f15d67200000000000000000000000000000000000000000000000000000000 -); -uint256 constant NoContract_error_sig_ptr = 0x0; -uint256 constant NoContract_error_token_ptr = 0x4; -uint256 constant NoContract_error_length = 0x24; // 4 + 32 == 36 - uint256 constant EIP_712_PREFIX = ( 0x1901000000000000000000000000000000000000000000000000000000000000 ); uint256 constant ExtraGasBuffer = 0x20; -uint256 constant CostPerWord = 3; -uint256 constant MemoryExpansionCoefficient = 0x200; // 512 +uint256 constant CostPerWord = 0x3; +uint256 constant MemoryExpansionCoefficientShift = 0x9; uint256 constant Create2AddressDerivation_ptr = 0x0b; uint256 constant Create2AddressDerivation_length = 0x55; @@ -316,11 +378,12 @@ uint256 constant Create2AddressDerivation_length = 0x55; uint256 constant MaskOverByteTwelve = ( 0x0000000000000000000000ff0000000000000000000000000000000000000000 ); - uint256 constant MaskOverLastTwentyBytes = ( 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff ); - +uint256 constant AddressDirtyUpperBitThreshold = ( + 0x0000000000000000000000010000000000000000000000000000000000000000 +); uint256 constant MaskOverFirstFourBytes = ( 0xffffffff00000000000000000000000000000000000000000000000000000000 ); @@ -334,7 +397,6 @@ uint256 constant MaxUint120 = 0xffffffffffffffffffffffffffffff; uint256 constant Conduit_execute_ConduitTransfer_ptr = 0x20; uint256 constant Conduit_execute_ConduitTransfer_length = 0x01; - uint256 constant Conduit_execute_ConduitTransfer_offset_ptr = 0x04; uint256 constant Conduit_execute_ConduitTransfer_length_ptr = 0x24; uint256 constant Conduit_execute_transferItemType_ptr = 0x44; @@ -353,10 +415,9 @@ uint256 constant Accumulator_conduitKey_ptr = 0x20; uint256 constant Accumulator_selector_ptr = 0x40; uint256 constant Accumulator_array_offset_ptr = 0x44; uint256 constant Accumulator_array_length_ptr = 0x64; - uint256 constant Accumulator_itemSizeOffsetDifference = 0x3c; - uint256 constant Accumulator_array_offset = 0x20; + uint256 constant Conduit_transferItem_size = 0xc0; uint256 constant Conduit_transferItem_token_ptr = 0x20; uint256 constant Conduit_transferItem_from_ptr = 0x40; @@ -364,49 +425,88 @@ uint256 constant Conduit_transferItem_to_ptr = 0x60; uint256 constant Conduit_transferItem_identifier_ptr = 0x80; uint256 constant Conduit_transferItem_amount_ptr = 0xa0; -// Declare constant for errors related to amount derivation. -// error InexactFraction() @ AmountDerivationErrors.sol -uint256 constant InexactFraction_error_signature = ( - 0xc63cf08900000000000000000000000000000000000000000000000000000000 -); -uint256 constant InexactFraction_error_len = 0x04; - -// Declare constant for errors related to signature verification. -uint256 constant Ecrecover_precompile = 1; +uint256 constant Ecrecover_precompile = 0x1; uint256 constant Ecrecover_args_size = 0x80; uint256 constant Signature_lower_v = 27; -// error BadSignatureV(uint8) @ SignatureVerificationErrors.sol -uint256 constant BadSignatureV_error_signature = ( - 0x1f003d0a00000000000000000000000000000000000000000000000000000000 +// Bitmask that only gives a non-zero value if masked with a non-match selector. +uint256 constant NonMatchSelector_MagicMask = ( + 0x4000000000000000000000000000000000000000000000000000000000 ); -uint256 constant BadSignatureV_error_offset = 0x04; -uint256 constant BadSignatureV_error_length = 0x24; -// error InvalidSigner() @ SignatureVerificationErrors.sol -uint256 constant InvalidSigner_error_signature = ( - 0x815e1d6400000000000000000000000000000000000000000000000000000000 +// First bit indicates that a NATIVE offer items has been used and the 231st bit +// indicates that a non match selector has been called. +uint256 constant NonMatchSelector_InvalidErrorValue = ( + 0x4000000000000000000000000000000000000000000000000000000001 ); -uint256 constant InvalidSigner_error_length = 0x04; -// error InvalidSignature() @ SignatureVerificationErrors.sol -uint256 constant InvalidSignature_error_signature = ( - 0x8baa579f00000000000000000000000000000000000000000000000000000000 +/** + * @dev Selector and offsets for generateOrder + * + * function generateOrder( + * address fulfiller, + * SpentItem[] calldata minimumReceived, + * SpentItem[] calldata maximumSpent, + * bytes calldata context + * ) + */ +uint256 constant generateOrder_selector = 0x98919765; +uint256 constant generateOrder_selector_offset = 0x1c; +uint256 constant generateOrder_head_offset = 0x04; +uint256 constant generateOrder_minimumReceived_head_offset = 0x20; +uint256 constant generateOrder_maximumSpent_head_offset = 0x40; +uint256 constant generateOrder_context_head_offset = 0x60; +uint256 constant generateOrder_base_tail_offset = 0x80; +uint256 constant generateOrder_maximum_returndatasize = 0xffff; + +uint256 constant ratifyOrder_selector = 0xf4dd92ce; +uint256 constant ratifyOrder_selector_offset = 0x1c; +uint256 constant ratifyOrder_head_offset = 0x04; +// uint256 constant ratifyOrder_offer_head_offset = 0x00; +uint256 constant ratifyOrder_consideration_head_offset = 0x20; +uint256 constant ratifyOrder_context_head_offset = 0x40; +uint256 constant ratifyOrder_orderHashes_head_offset = 0x60; +uint256 constant ratifyOrder_contractNonce_offset = 0x80; +uint256 constant ratifyOrder_base_tail_offset = 0xa0; + +uint256 constant validateOrder_selector = 0x17b1f942; +uint256 constant validateOrder_selector_offset = 0x1c; +uint256 constant validateOrder_head_offset = 0x04; +uint256 constant validateOrder_zoneParameters_offset = 0x20; + +// uint256 constant ZoneParameters_orderHash_offset = 0x00; +uint256 constant ZoneParameters_fulfiller_offset = 0x20; +uint256 constant ZoneParameters_offerer_offset = 0x40; +uint256 constant ZoneParameters_offer_head_offset = 0x60; +uint256 constant ZoneParameters_consideration_head_offset = 0x80; +uint256 constant ZoneParameters_extraData_head_offset = 0xa0; +uint256 constant ZoneParameters_orderHashes_head_offset = 0xc0; +uint256 constant ZoneParameters_startTime_offset = 0xe0; +uint256 constant ZoneParameters_endTime_offset = 0x100; +uint256 constant ZoneParameters_zoneHash_offset = 0x120; +uint256 constant ZoneParameters_base_tail_offset = 0x140; +uint256 constant ZoneParameters_selectorAndPointer_length = 0x24; +uint256 constant ZoneParameters_basicOrderFixedElements_length = 0x64; + +// ConsiderationDecoder Constants +uint256 constant OrderParameters_head_size = 0x0160; +uint256 constant OrderParameters_totalOriginalConsiderationItems_offset = ( + 0x0140 ); -uint256 constant InvalidSignature_error_length = 0x04; +uint256 constant AdvancedOrderPlusOrderParameters_head_size = 0x0200; -// error BadContractSignature() @ SignatureVerificationErrors.sol -uint256 constant BadContractSignature_error_signature = ( - 0x4f7fb80d00000000000000000000000000000000000000000000000000000000 -); -uint256 constant BadContractSignature_error_length = 0x04; +uint256 constant Order_signature_offset = 0x20; +uint256 constant Order_head_size = 0x40; + +uint256 constant AdvancedOrder_fixed_segment_0 = 0x40; + +uint256 constant CriteriaResolver_head_size = 0xa0; +uint256 constant CriteriaResolver_fixed_segment_0 = 0x80; +uint256 constant CriteriaResolver_criteriaProof_offset = 0x80; -uint256 constant NumBitsAfterSelector = 0xe0; +uint256 constant FulfillmentComponent_mem_tail_size = 0x40; +uint256 constant FulfillmentComponent_mem_tail_size_shift = 0x6; +uint256 constant Fulfillment_head_size = 0x40; +uint256 constant Fulfillment_considerationComponents_offset = 0x20; -// 69 is the lowest modulus for which the remainder -// of every selector other than the two match functions -// is greater than those of the match functions. -uint256 constant NonMatchSelector_MagicModulus = 69; -// Of the two match function selectors, the highest -// remainder modulo 69 is 29. -uint256 constant NonMatchSelector_MagicRemainder = 0x1d; +uint256 constant OrderComponents_OrderParameters_common_head_size = 0x0140; diff --git a/contracts/lib/ConsiderationDecoder.sol b/contracts/lib/ConsiderationDecoder.sol new file mode 100644 index 000000000..9d1ce5c84 --- /dev/null +++ b/contracts/lib/ConsiderationDecoder.sol @@ -0,0 +1,1351 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { + AdvancedOrder, + ConsiderationItem, + CriteriaResolver, + Fulfillment, + FulfillmentComponent, + OfferItem, + Order, + OrderParameters, + ReceivedItem +} from "./ConsiderationStructs.sol"; + +import { + AdvancedOrder_denominator_offset, + AdvancedOrder_extraData_offset, + AdvancedOrder_fixed_segment_0, + AdvancedOrder_head_size, + AdvancedOrder_numerator_offset, + AdvancedOrder_signature_offset, + AdvancedOrderPlusOrderParameters_head_size, + Common_amount_offset, + Common_endAmount_offset, + ConsiderationItem_size_with_length, + ConsiderationItem_size, + CriteriaResolver_criteriaProof_offset, + CriteriaResolver_fixed_segment_0, + CriteriaResolver_head_size, + FourWords, + FreeMemoryPointerSlot, + Fulfillment_considerationComponents_offset, + Fulfillment_head_size, + FulfillmentComponent_mem_tail_size_shift, + FulfillmentComponent_mem_tail_size, + generateOrder_maximum_returndatasize, + OfferItem_size_with_length, + OfferItem_size, + OneWord, + OneWordShift, + OnlyFullWordMask, + Order_head_size, + Order_signature_offset, + OrderComponents_OrderParameters_common_head_size, + OrderParameters_consideration_head_offset, + OrderParameters_head_size, + OrderParameters_offer_head_offset, + OrderParameters_totalOriginalConsiderationItems_offset, + ReceivedItem_recipient_offset, + ReceivedItem_size, + ReceivedItem_size_excluding_recipient, + SpentItem_size_shift, + SpentItem_size, + ThirtyOneBytes, + TwoWords +} from "./ConsiderationConstants.sol"; + +import { + CalldataPointer, + malloc, + MemoryPointer, + OffsetOrLengthMask +} from "../helpers/PointerLibraries.sol"; + +contract ConsiderationDecoder { + /** + * @dev Takes a bytes array from calldata and copies it into memory. + * + * @param cdPtrLength A calldata pointer to the start of the bytes array in + * calldata which contains the length of the array. + * + * @return mPtrLength A memory pointer to the start of the bytes array in + * memory which contains the length of the array. + */ + function _decodeBytes( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + assembly { + // Get the current free memory pointer. + mPtrLength := mload(FreeMemoryPointerSlot) + + // Derive the size of the bytes array, rounding up to nearest word + // and adding a word for the length field. Note: masking + // `calldataload(cdPtrLength)` is redundant here. + let size := add( + and( + add(calldataload(cdPtrLength), ThirtyOneBytes), + OnlyFullWordMask + ), + OneWord + ) + + // Copy bytes from calldata into memory based on pointers and size. + calldatacopy(mPtrLength, cdPtrLength, size) + + // Store the masked value in memory. Note: the value of `size` is at + // least 32, meaning the calldatacopy above will at least write to + // `[mPtrLength, mPtrLength + 32)`. + mstore( + mPtrLength, + and(calldataload(cdPtrLength), OffsetOrLengthMask) + ) + + // Update free memory pointer based on the size of the bytes array. + mstore(FreeMemoryPointerSlot, add(mPtrLength, size)) + } + } + + /** + * @dev Takes an offer array from calldata and copies it into memory. + * + * @param cdPtrLength A calldata pointer to the start of the offer array + * in calldata which contains the length of the array. + * + * @return mPtrLength A memory pointer to the start of the offer array in + * memory which contains the length of the array. + */ + function _decodeOffer( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + assembly { + // Retrieve length of array, masking to prevent potential overflow. + let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask) + + // Get the current free memory pointer. + mPtrLength := mload(FreeMemoryPointerSlot) + + // Write the array length to memory. + mstore(mPtrLength, arrLength) + + // Derive the head by adding one word to the length pointer. + let mPtrHead := add(mPtrLength, OneWord) + + // Derive the tail by adding one word per element (note that structs + // are written to memory with an offset per struct element). + let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength)) + + // Track the next tail, beginning with the initial tail value. + let mPtrTailNext := mPtrTail + + // Copy all offer array data into memory at the tail pointer. + calldatacopy( + mPtrTail, + add(cdPtrLength, OneWord), + mul(arrLength, OfferItem_size) + ) + + // Track the next head pointer, starting with initial head value. + let mPtrHeadNext := mPtrHead + + // Iterate over each head pointer until it reaches the tail. + for { + + } lt(mPtrHeadNext, mPtrTail) { + + } { + // Write the next tail pointer to next head pointer in memory. + mstore(mPtrHeadNext, mPtrTailNext) + + // Increment the next head pointer by one word. + mPtrHeadNext := add(mPtrHeadNext, OneWord) + + // Increment the next tail pointer by the size of an offer item. + mPtrTailNext := add(mPtrTailNext, OfferItem_size) + } + + // Update free memory pointer to allocate memory up to end of tail. + mstore(FreeMemoryPointerSlot, mPtrTailNext) + } + } + + /** + * @dev Takes a consideration array from calldata and copies it into memory. + * + * @param cdPtrLength A calldata pointer to the start of the consideration + * array in calldata which contains the length of the + * array. + * + * @return mPtrLength A memory pointer to the start of the consideration + * array in memory which contains the length of the + * array. + */ + function _decodeConsideration( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + assembly { + // Retrieve length of array, masking to prevent potential overflow. + let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask) + + // Get the current free memory pointer. + mPtrLength := mload(FreeMemoryPointerSlot) + + // Write the array length to memory. + mstore(mPtrLength, arrLength) + + // Derive the head by adding one word to the length pointer. + let mPtrHead := add(mPtrLength, OneWord) + + // Derive the tail by adding one word per element (note that structs + // are written to memory with an offset per struct element). + let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength)) + + // Track the next tail, beginning with the initial tail value. + let mPtrTailNext := mPtrTail + + // Copy all consideration array data into memory at tail pointer. + calldatacopy( + mPtrTail, + add(cdPtrLength, OneWord), + mul(arrLength, ConsiderationItem_size) + ) + + // Track the next head pointer, starting with initial head value. + let mPtrHeadNext := mPtrHead + + // Iterate over each head pointer until it reaches the tail. + for { + + } lt(mPtrHeadNext, mPtrTail) { + + } { + // Write the next tail pointer to next head pointer in memory. + mstore(mPtrHeadNext, mPtrTailNext) + + // Increment the next head pointer by one word. + mPtrHeadNext := add(mPtrHeadNext, OneWord) + + // Increment next tail pointer by size of a consideration item. + mPtrTailNext := add(mPtrTailNext, ConsiderationItem_size) + } + + // Update free memory pointer to allocate memory up to end of tail. + mstore(FreeMemoryPointerSlot, mPtrTailNext) + } + } + + /** + * @dev Takes a calldata pointer and memory pointer and copies a referenced + * OrderParameters struct and associated offer and consideration data + * to memory. + * + * @param cdPtr A calldata pointer for the OrderParameters struct. + * @param mPtr A memory pointer to the OrderParameters struct head. + */ + function _decodeOrderParametersTo( + CalldataPointer cdPtr, + MemoryPointer mPtr + ) internal pure { + // Copy the full OrderParameters head from calldata to memory. + cdPtr.copy(mPtr, OrderParameters_head_size); + + // Resolve the offer calldata offset, use that to decode and copy offer + // from calldata, and write resultant memory offset to head in memory. + mPtr.offset(OrderParameters_offer_head_offset).write( + _decodeOffer(cdPtr.pptr(OrderParameters_offer_head_offset)) + ); + + // Resolve consideration calldata offset, use that to copy consideration + // from calldata, and write resultant memory offset to head in memory. + mPtr.offset(OrderParameters_consideration_head_offset).write( + _decodeConsideration( + cdPtr.pptr(OrderParameters_consideration_head_offset) + ) + ); + } + + /** + * @dev Takes a calldata pointer to an OrderParameters struct and copies the + * decoded struct to memory. + * + * @param cdPtr A calldata pointer for the OrderParameters struct. + * + * @return mPtr A memory pointer to the OrderParameters struct head. + */ + function _decodeOrderParameters( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate required memory for the OrderParameters head (offer and + // consideration are allocated independently). + mPtr = malloc(OrderParameters_head_size); + + // Decode and copy the order parameters to the newly allocated memory. + _decodeOrderParametersTo(cdPtr, mPtr); + } + + /** + * @dev Takes a calldata pointer to an Order struct and copies the decoded + * struct to memory. + * + * @param cdPtr A calldata pointer for the Order struct. + * + * @return mPtr A memory pointer to the Order struct head. + */ + function _decodeOrder( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate required memory for the Order head (OrderParameters and + // signature are allocated independently). + mPtr = malloc(Order_head_size); + + // Resolve OrderParameters calldata offset, use it to decode and copy + // from calldata, and write resultant memory offset to head in memory. + mPtr.write(_decodeOrderParameters(cdPtr.pptr())); + + // Resolve signature calldata offset, use that to decode and copy from + // calldata, and write resultant memory offset to head in memory. + mPtr.offset(Order_signature_offset).write( + _decodeBytes(cdPtr.pptr(Order_signature_offset)) + ); + } + + /** + * @dev Takes a calldata pointer to an AdvancedOrder struct and copies the + * decoded struct to memory. + * + * @param cdPtr A calldata pointer for the AdvancedOrder struct. + * + * @return mPtr A memory pointer to the AdvancedOrder struct head. + */ + function _decodeAdvancedOrder( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate memory for AdvancedOrder head and OrderParameters head. + mPtr = malloc(AdvancedOrderPlusOrderParameters_head_size); + + // Use numerator + denominator calldata offset to decode and copy + // from calldata and write resultant memory offset to head in memory. + cdPtr.offset(AdvancedOrder_numerator_offset).copy( + mPtr.offset(AdvancedOrder_numerator_offset), + AdvancedOrder_fixed_segment_0 + ); + + // Get pointer to memory immediately after advanced order. + MemoryPointer mPtrParameters = mPtr.offset(AdvancedOrder_head_size); + + // Write pptr for advanced order parameters to memory. + mPtr.write(mPtrParameters); + + // Resolve OrderParameters calldata pointer & write to allocated region. + _decodeOrderParametersTo(cdPtr.pptr(), mPtrParameters); + + // Resolve signature calldata offset, use that to decode and copy from + // calldata, and write resultant memory offset to head in memory. + mPtr.offset(AdvancedOrder_signature_offset).write( + _decodeBytes(cdPtr.pptr(AdvancedOrder_signature_offset)) + ); + + // Resolve extraData calldata offset, use that to decode and copy from + // calldata, and write resultant memory offset to head in memory. + mPtr.offset(AdvancedOrder_extraData_offset).write( + _decodeBytes(cdPtr.pptr(AdvancedOrder_extraData_offset)) + ); + } + + /** + * @dev Allocates a single word of empty bytes in memory and returns the + * pointer to that memory region. + * + * @return mPtr The memory pointer to the new empty word in memory. + */ + function _getEmptyBytesOrArray() + internal + pure + returns (MemoryPointer mPtr) + { + mPtr = malloc(OneWord); + mPtr.write(0); + } + + /** + * @dev Takes a calldata pointer to an Order struct and copies the decoded + * struct to memory as an AdvancedOrder. + * + * @param cdPtr A calldata pointer for the Order struct. + * + * @return mPtr A memory pointer to the AdvancedOrder struct head. + */ + function _decodeOrderAsAdvancedOrder( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate memory for AdvancedOrder head and OrderParameters head. + mPtr = malloc(AdvancedOrderPlusOrderParameters_head_size); + + // Get pointer to memory immediately after advanced order. + MemoryPointer mPtrParameters = mPtr.offset(AdvancedOrder_head_size); + + // Write pptr for advanced order parameters. + mPtr.write(mPtrParameters); + + // Resolve OrderParameters calldata pointer & write to allocated region. + _decodeOrderParametersTo(cdPtr.pptr(), mPtrParameters); + + // Write default Order numerator and denominator values (i.e. 1/1). + mPtr.offset(AdvancedOrder_numerator_offset).write(1); + mPtr.offset(AdvancedOrder_denominator_offset).write(1); + + // Resolve signature calldata offset, use that to decode and copy from + // calldata, and write resultant memory offset to head in memory. + mPtr.offset(AdvancedOrder_signature_offset).write( + _decodeBytes(cdPtr.pptr(Order_signature_offset)) + ); + + // Resolve extraData calldata offset, use that to decode and copy from + // calldata, and write resultant memory offset to head in memory. + mPtr.offset(AdvancedOrder_extraData_offset).write( + _getEmptyBytesOrArray() + ); + } + + /** + * @dev Takes a calldata pointer to an array of Order structs and copies the + * decoded array to memory as an array of AdvancedOrder structs. + * + * @param cdPtrLength A calldata pointer to the start of the orders array in + * calldata which contains the length of the array. + * + * @return mPtrLength A memory pointer to the start of the array of advanced + * orders in memory which contains length of the array. + */ + function _decodeOrdersAsAdvancedOrders( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive offset to the tail based on one word per array element. + uint256 tailOffset = arrLength << OneWordShift; + + // Add one additional word for the length and allocate memory. + mPtrLength = malloc(tailOffset + OneWord); + + // Write the length of the array to memory. + mPtrLength.write(arrLength); + + // Advance to first memory & calldata pointers (e.g. after length). + MemoryPointer mPtrHead = mPtrLength.next(); + CalldataPointer cdPtrHead = cdPtrLength.next(); + + // Iterate over each pointer, word by word, until tail is reached. + for (uint256 offset = 0; offset < tailOffset; offset += OneWord) { + // Resolve Order calldata offset, use it to decode and copy from + // calldata, and write resultant AdvancedOrder offset to memory. + mPtrHead.offset(offset).write( + _decodeOrderAsAdvancedOrder(cdPtrHead.pptr(offset)) + ); + } + } + } + + /** + * @dev Takes a calldata pointer to a criteria proof, or an array bytes32 + * types, and copies the decoded proof to memory. + * + * @param cdPtrLength A calldata pointer to the start of the criteria proof + * in calldata which contains the length of the array. + * + * @return mPtrLength A memory pointer to the start of the criteria proof + * in memory which contains length of the array. + */ + function _decodeCriteriaProof( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive array size based on one word per array element and length. + uint256 arrSize = (arrLength + 1) << OneWordShift; + + // Allocate memory equal to the array size. + mPtrLength = malloc(arrSize); + + // Copy the array from calldata into memory. + cdPtrLength.copy(mPtrLength, arrSize); + } + } + + /** + * @dev Takes a calldata pointer to a CriteriaResolver struct and copies the + * decoded struct to memory. + * + * @param cdPtr A calldata pointer for the CriteriaResolver struct. + * + * @return mPtr A memory pointer to the CriteriaResolver struct head. + */ + function _decodeCriteriaResolver( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate required memory for the CriteriaResolver head (the criteria + // proof bytes32 array is allocated independently). + mPtr = malloc(CriteriaResolver_head_size); + + // Decode and copy order index, side, index, and identifier from + // calldata and write resultant memory offset to head in memory. + cdPtr.copy(mPtr, CriteriaResolver_fixed_segment_0); + + // Resolve criteria proof calldata offset, use it to decode and copy + // from calldata, and write resultant memory offset to head in memory. + mPtr.offset(CriteriaResolver_criteriaProof_offset).write( + _decodeCriteriaProof( + cdPtr.pptr(CriteriaResolver_criteriaProof_offset) + ) + ); + } + + /** + * @dev Takes an array of criteria resolvers from calldata and copies it + * into memory. + * + * @param cdPtrLength A calldata pointer to the start of the criteria + * resolver array in calldata which contains the length + * of the array. + * + * @return mPtrLength A memory pointer to the start of the criteria resolver + * array in memory which contains the length of the + * array. + */ + function _decodeCriteriaResolvers( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive offset to the tail based on one word per array element. + uint256 tailOffset = arrLength << OneWordShift; + + // Add one additional word for the length and allocate memory. + mPtrLength = malloc(tailOffset + OneWord); + + // Write the length of the array to memory. + mPtrLength.write(arrLength); + + // Advance to first memory & calldata pointers (e.g. after length). + MemoryPointer mPtrHead = mPtrLength.next(); + CalldataPointer cdPtrHead = cdPtrLength.next(); + + // Iterate over each pointer, word by word, until tail is reached. + for (uint256 offset = 0; offset < tailOffset; offset += OneWord) { + // Resolve CriteriaResolver calldata offset, use it to decode + // and copy from calldata, and write resultant memory offset. + mPtrHead.offset(offset).write( + _decodeCriteriaResolver(cdPtrHead.pptr(offset)) + ); + } + } + } + + /** + * @dev Takes an array of orders from calldata and copies it into memory. + * + * @param cdPtrLength A calldata pointer to the start of the orders array in + * calldata which contains the length of the array. + * + * @return mPtrLength A memory pointer to the start of the orders array + * in memory which contains the length of the array. + */ + function _decodeOrders( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive offset to the tail based on one word per array element. + uint256 tailOffset = arrLength << OneWordShift; + + // Add one additional word for the length and allocate memory. + mPtrLength = malloc(tailOffset + OneWord); + + // Write the length of the array to memory. + mPtrLength.write(arrLength); + + // Advance to first memory & calldata pointers (e.g. after length). + MemoryPointer mPtrHead = mPtrLength.next(); + CalldataPointer cdPtrHead = cdPtrLength.next(); + + // Iterate over each pointer, word by word, until tail is reached. + for (uint256 offset = 0; offset < tailOffset; offset += OneWord) { + // Resolve Order calldata offset, use it to decode and copy + // from calldata, and write resultant memory offset. + mPtrHead.offset(offset).write( + _decodeOrder(cdPtrHead.pptr(offset)) + ); + } + } + } + + /** + * @dev Takes an array of fulfillment components from calldata and copies it + * into memory. + * + * @param cdPtrLength A calldata pointer to the start of the fulfillment + * components array in calldata which contains the length + * of the array. + * + * @return mPtrLength A memory pointer to the start of the fulfillment + * components array in memory which contains the length + * of the array. + */ + function _decodeFulfillmentComponents( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + assembly { + let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask) + + // Get the current free memory pointer. + mPtrLength := mload(FreeMemoryPointerSlot) + + mstore(mPtrLength, arrLength) + let mPtrHead := add(mPtrLength, OneWord) + let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength)) + let mPtrTailNext := mPtrTail + calldatacopy( + mPtrTail, + add(cdPtrLength, OneWord), + shl(FulfillmentComponent_mem_tail_size_shift, arrLength) + ) + let mPtrHeadNext := mPtrHead + for { + + } lt(mPtrHeadNext, mPtrTail) { + + } { + mstore(mPtrHeadNext, mPtrTailNext) + mPtrHeadNext := add(mPtrHeadNext, OneWord) + mPtrTailNext := add( + mPtrTailNext, + FulfillmentComponent_mem_tail_size + ) + } + + // Update the free memory pointer. + mstore(FreeMemoryPointerSlot, mPtrTailNext) + } + } + + /** + * @dev Takes a nested array of fulfillment components from calldata and + * copies it into memory. + * + * @param cdPtrLength A calldata pointer to the start of the nested + * fulfillment components array in calldata which + * contains the length of the array. + * + * @return mPtrLength A memory pointer to the start of the nested + * fulfillment components array in memory which + * contains the length of the array. + */ + function _decodeNestedFulfillmentComponents( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive offset to the tail based on one word per array element. + uint256 tailOffset = arrLength << OneWordShift; + + // Add one additional word for the length and allocate memory. + mPtrLength = malloc(tailOffset + OneWord); + + // Write the length of the array to memory. + mPtrLength.write(arrLength); + + // Advance to first memory & calldata pointers (e.g. after length). + MemoryPointer mPtrHead = mPtrLength.next(); + CalldataPointer cdPtrHead = cdPtrLength.next(); + + // Iterate over each pointer, word by word, until tail is reached. + for (uint256 offset = 0; offset < tailOffset; offset += OneWord) { + // Resolve FulfillmentComponents array calldata offset, use it + // to decode and copy from calldata, and write memory offset. + mPtrHead.offset(offset).write( + _decodeFulfillmentComponents(cdPtrHead.pptr(offset)) + ); + } + } + } + + /** + * @dev Takes an array of advanced orders from calldata and copies it into + * memory. + * + * @param cdPtrLength A calldata pointer to the start of the advanced orders + * array in calldata which contains the length of the + * array. + * + * @return mPtrLength A memory pointer to the start of the advanced orders + * array in memory which contains the length of the + * array. + */ + function _decodeAdvancedOrders( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive offset to the tail based on one word per array element. + uint256 tailOffset = arrLength << OneWordShift; + + // Add one additional word for the length and allocate memory. + mPtrLength = malloc(tailOffset + OneWord); + + // Write the length of the array to memory. + mPtrLength.write(arrLength); + + // Advance to first memory & calldata pointers (e.g. after length). + MemoryPointer mPtrHead = mPtrLength.next(); + CalldataPointer cdPtrHead = cdPtrLength.next(); + + // Iterate over each pointer, word by word, until tail is reached. + for (uint256 offset = 0; offset < tailOffset; offset += OneWord) { + // Resolve AdvancedOrder calldata offset, use it to decode and + // copy from calldata, and write resultant memory offset. + mPtrHead.offset(offset).write( + _decodeAdvancedOrder(cdPtrHead.pptr(offset)) + ); + } + } + } + + /** + * @dev Takes a calldata pointer to a Fulfillment struct and copies the + * decoded struct to memory. + * + * @param cdPtr A calldata pointer for the Fulfillment struct. + * + * @return mPtr A memory pointer to the Fulfillment struct head. + */ + function _decodeFulfillment( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate required memory for the Fulfillment head (the fulfillment + // components arrays are allocated independently). + mPtr = malloc(Fulfillment_head_size); + + // Resolve offerComponents calldata offset, use it to decode and copy + // from calldata, and write resultant memory offset to head in memory. + mPtr.write(_decodeFulfillmentComponents(cdPtr.pptr())); + + // Resolve considerationComponents calldata offset, use it to decode and + // copy from calldata, and write resultant memory offset to memory head. + mPtr.offset(Fulfillment_considerationComponents_offset).write( + _decodeFulfillmentComponents( + cdPtr.pptr(Fulfillment_considerationComponents_offset) + ) + ); + } + + /** + * @dev Takes an array of fulfillments from calldata and copies it into + * memory. + * + * @param cdPtrLength A calldata pointer to the start of the fulfillments + * array in calldata which contains the length of the + * array. + * + * @return mPtrLength A memory pointer to the start of the fulfillments + * array in memory which contains the length of the + * array. + */ + function _decodeFulfillments( + CalldataPointer cdPtrLength + ) internal pure returns (MemoryPointer mPtrLength) { + // Retrieve length of array, masking to prevent potential overflow. + uint256 arrLength = cdPtrLength.readMaskedUint256(); + + unchecked { + // Derive offset to the tail based on one word per array element. + uint256 tailOffset = arrLength << OneWordShift; + + // Add one additional word for the length and allocate memory. + mPtrLength = malloc(tailOffset + OneWord); + + // Write the length of the array to memory. + mPtrLength.write(arrLength); + + // Advance to first memory & calldata pointers (e.g. after length). + MemoryPointer mPtrHead = mPtrLength.next(); + CalldataPointer cdPtrHead = cdPtrLength.next(); + + // Iterate over each pointer, word by word, until tail is reached. + for (uint256 offset = 0; offset < tailOffset; offset += OneWord) { + // Resolve Fulfillment calldata offset, use it to decode and + // copy from calldata, and write resultant memory offset. + mPtrHead.offset(offset).write( + _decodeFulfillment(cdPtrHead.pptr(offset)) + ); + } + } + } + + /** + * @dev Takes a calldata pointer to an OrderComponents struct and copies the + * decoded struct to memory as an OrderParameters struct (with the + * totalOriginalConsiderationItems value set equal to the length of the + * supplied consideration array). + * + * @param cdPtr A calldata pointer for the OrderComponents struct. + * + * @return mPtr A memory pointer to the OrderParameters struct head. + */ + function _decodeOrderComponentsAsOrderParameters( + CalldataPointer cdPtr + ) internal pure returns (MemoryPointer mPtr) { + // Allocate memory for the OrderParameters head. + mPtr = malloc(OrderParameters_head_size); + + // Copy the full OrderComponents head from calldata to memory. + cdPtr.copy(mPtr, OrderComponents_OrderParameters_common_head_size); + + // Resolve the offer calldata offset, use that to decode and copy offer + // from calldata, and write resultant memory offset to head in memory. + mPtr.offset(OrderParameters_offer_head_offset).write( + _decodeOffer(cdPtr.pptr(OrderParameters_offer_head_offset)) + ); + + // Resolve consideration calldata offset, use that to copy consideration + // from calldata, and write resultant memory offset to head in memory. + MemoryPointer consideration = _decodeConsideration( + cdPtr.pptr(OrderParameters_consideration_head_offset) + ); + mPtr.offset(OrderParameters_consideration_head_offset).write( + consideration + ); + + // Write masked consideration length to totalOriginalConsiderationItems. + mPtr + .offset(OrderParameters_totalOriginalConsiderationItems_offset) + .write(consideration.readUint256()); + } + + /** + * @dev Decodes the returndata from a call to generateOrder, or returns + * empty arrays and a boolean signifying that the returndata does not + * adhere to a valid encoding scheme if it cannot be decoded. + * + * @return invalidEncoding A boolean signifying whether the returndata has + * an invalid encoding. + * @return offer The decoded offer array. + * @return consideration The decoded consideration array. + */ + function _decodeGenerateOrderReturndata() + internal + pure + returns ( + uint256 invalidEncoding, + MemoryPointer offer, + MemoryPointer consideration + ) + { + assembly { + // Check that returndatasize is at least four words: offerOffset, + // considerationOffset, offerLength, & considerationLength + invalidEncoding := lt(returndatasize(), FourWords) + + let offsetOffer + let offsetConsideration + let offerLength + let considerationLength + + // Proceed if enough returndata is present to continue evaluation. + if iszero(invalidEncoding) { + // Copy first two words of returndata (the offsets to offer and + // consideration array lengths) to scratch space. + returndatacopy(0, 0, TwoWords) + offsetOffer := mload(0) + offsetConsideration := mload(OneWord) + + // If valid length, check that offsets are within returndata. + let invalidOfferOffset := gt(offsetOffer, returndatasize()) + let invalidConsiderationOffset := gt( + offsetConsideration, + returndatasize() + ) + + // Only proceed if length (and thus encoding) is valid so far. + invalidEncoding := or( + invalidOfferOffset, + invalidConsiderationOffset + ) + if iszero(invalidEncoding) { + // Copy length of offer array to scratch space. + returndatacopy(0, offsetOffer, OneWord) + offerLength := mload(0) + + // Copy length of consideration array to scratch space. + returndatacopy(OneWord, offsetConsideration, OneWord) + considerationLength := mload(OneWord) + + { + // Calculate total size of offer & consideration arrays. + let totalOfferSize := shl( + SpentItem_size_shift, + offerLength + ) + let totalConsiderationSize := mul( + ReceivedItem_size, + considerationLength + ) + + // Add 4 words to total size to cover the offset and + // length fields of the two arrays. + let totalSize := add( + FourWords, + add(totalOfferSize, totalConsiderationSize) + ) + // Don't continue if returndatasize exceeds 65535 bytes + // or is greater than the calculated size. + invalidEncoding := or( + gt( + or(offerLength, considerationLength), + generateOrder_maximum_returndatasize + ), + gt(totalSize, returndatasize()) + ) + + // Set first word of scratch space to 0 so length of + // offer/consideration are set to 0 on invalid encoding. + mstore(0, 0) + } + } + } + + if iszero(invalidEncoding) { + offer := copySpentItemsAsOfferItems( + add(offsetOffer, OneWord), + offerLength + ) + + consideration := copyReceivedItemsAsConsiderationItems( + add(offsetConsideration, OneWord), + considerationLength + ) + } + + function copySpentItemsAsOfferItems(rdPtrHead, length) + -> mPtrLength + { + // Retrieve the current free memory pointer. + mPtrLength := mload(FreeMemoryPointerSlot) + + // Allocate memory for the array. + mstore( + FreeMemoryPointerSlot, + add( + mPtrLength, + add(OneWord, mul(length, OfferItem_size_with_length)) + ) + ) + + // Write the length of the array to the start of free memory. + mstore(mPtrLength, length) + + // Use offset from length to minimize stack depth. + let headOffsetFromLength := OneWord + let headSizeWithLength := shl(OneWordShift, add(1, length)) + let mPtrTailNext := add(mPtrLength, headSizeWithLength) + + // Iterate over each element. + for { + + } lt(headOffsetFromLength, headSizeWithLength) { + + } { + // Write the memory pointer to the accompanying head offset. + mstore(add(mPtrLength, headOffsetFromLength), mPtrTailNext) + + // Copy itemType, token, identifier and amount. + returndatacopy(mPtrTailNext, rdPtrHead, SpentItem_size) + + // Copy amount to endAmount. + mstore( + add(mPtrTailNext, Common_endAmount_offset), + mload(add(mPtrTailNext, Common_amount_offset)) + ) + + // Update read pointer, next tail pointer, and head offset. + rdPtrHead := add(rdPtrHead, SpentItem_size) + mPtrTailNext := add(mPtrTailNext, OfferItem_size) + headOffsetFromLength := add(headOffsetFromLength, OneWord) + } + } + + function copyReceivedItemsAsConsiderationItems(rdPtrHead, length) + -> mPtrLength + { + // Retrieve the current free memory pointer. + mPtrLength := mload(FreeMemoryPointerSlot) + + // Allocate memory for the array. + mstore( + FreeMemoryPointerSlot, + add( + mPtrLength, + add( + OneWord, + mul(length, ConsiderationItem_size_with_length) + ) + ) + ) + + // Write the length of the array to the start of free memory. + mstore(mPtrLength, length) + + // Use offset from length to minimize stack depth. + let headOffsetFromLength := OneWord + let headSizeWithLength := shl(OneWordShift, add(1, length)) + let mPtrTailNext := add(mPtrLength, headSizeWithLength) + + // Iterate over each element. + for { + + } lt(headOffsetFromLength, headSizeWithLength) { + + } { + // Write the memory pointer to the accompanying head offset. + mstore(add(mPtrLength, headOffsetFromLength), mPtrTailNext) + + // Copy itemType, token, identifier and amount. + returndatacopy( + mPtrTailNext, + rdPtrHead, + ReceivedItem_size_excluding_recipient + ) + + // Copy amount and recipient. + returndatacopy( + add(mPtrTailNext, Common_endAmount_offset), + add(rdPtrHead, Common_amount_offset), + TwoWords + ) + + // Update read pointer, next tail pointer, and head offset. + rdPtrHead := add(rdPtrHead, ReceivedItem_size) + mPtrTailNext := add(mPtrTailNext, ConsiderationItem_size) + headOffsetFromLength := add(headOffsetFromLength, OneWord) + } + } + } + } + + /** + * @dev Converts a function returning _decodeGenerateOrderReturndata types + * into a function returning offer and consideration types. + * + * @param inFn The input function, taking no arguments and returning an + * error buffer, spent item array, and received item array. + * + * @return outFn The output function, taking no arguments and returning an + * error buffer, offer array, and consideration array. + */ + function _convertGetGeneratedOrderResult( + function() + internal + pure + returns (uint256, MemoryPointer, MemoryPointer) inFn + ) + internal + pure + returns ( + function() + internal + pure + returns ( + uint256, + OfferItem[] memory, + ConsiderationItem[] memory + ) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking ReceivedItem, address, bytes32, and bytes + * types (e.g. the _transfer function) into a function taking + * OfferItem, address, bytes32, and bytes types. + * + * @param inFn The input function, taking ReceivedItem, address, bytes32, + * and bytes types (e.g. the _transfer function). + * + * @return outFn The output function, taking OfferItem, address, bytes32, + * and bytes types. + */ + function _toOfferItemInput( + function(ReceivedItem memory, address, bytes32, bytes memory) + internal inFn + ) + internal + pure + returns ( + function(OfferItem memory, address, bytes32, bytes memory) + internal outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking ReceivedItem, address, bytes32, and bytes + * types (e.g. the _transfer function) into a function taking + * ConsiderationItem, address, bytes32, and bytes types. + * + * @param inFn The input function, taking ReceivedItem, address, bytes32, + * and bytes types (e.g. the _transfer function). + * + * @return outFn The output function, taking ConsiderationItem, address, + * bytes32, and bytes types. + */ + function _toConsiderationItemInput( + function(ReceivedItem memory, address, bytes32, bytes memory) + internal inFn + ) + internal + pure + returns ( + function(ConsiderationItem memory, address, bytes32, bytes memory) + internal outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * an OrderParameters type. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning an OrderParameters type. + */ + function _toOrderParametersReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (OrderParameters memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * an AdvancedOrder type. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning an AdvancedOrder type. + */ + function _toAdvancedOrderReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (AdvancedOrder memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * a dynamic array of CriteriaResolver types. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning a dynamic array of CriteriaResolver types. + */ + function _toCriteriaResolversReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (CriteriaResolver[] memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * a dynamic array of Order types. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning a dynamic array of Order types. + */ + function _toOrdersReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (Order[] memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * a nested dynamic array of dynamic arrays of FulfillmentComponent + * types. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning a nested dynamic array of dynamic arrays of + * FulfillmentComponent types. + */ + function _toNestedFulfillmentComponentsReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (FulfillmentComponent[][] memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * a dynamic array of AdvancedOrder types. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning a dynamic array of AdvancedOrder types. + */ + function _toAdvancedOrdersReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (AdvancedOrder[] memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * a dynamic array of Fulfillment types. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning a dynamic array of Fulfillment types. + */ + function _toFulfillmentsReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) + internal + pure + returns (Fulfillment[] memory) outFn + ) + { + assembly { + outFn := inFn + } + } + + /** + * @dev Converts an offer item into a received item, applying a given + * recipient. + * + * @param offerItem The offer item. + * @param recipient The recipient. + * + * @return receivedItem The received item. + */ + function _convertOfferItemToReceivedItemWithRecipient( + OfferItem memory offerItem, + address recipient + ) internal pure returns (ReceivedItem memory receivedItem) { + assembly { + receivedItem := offerItem + mstore(add(receivedItem, ReceivedItem_recipient_offset), recipient) + } + } +} diff --git a/contracts/lib/ConsiderationEncoder.sol b/contracts/lib/ConsiderationEncoder.sol new file mode 100644 index 000000000..cf74ed796 --- /dev/null +++ b/contracts/lib/ConsiderationEncoder.sol @@ -0,0 +1,746 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { + BasicOrder_additionalRecipients_length_cdPtr, + BasicOrder_common_params_size, + BasicOrder_startTime_cdPtr, + BasicOrder_startTimeThroughZoneHash_size, + Common_amount_offset, + Common_identifier_offset, + Common_token_offset, + generateOrder_base_tail_offset, + generateOrder_context_head_offset, + generateOrder_head_offset, + generateOrder_maximumSpent_head_offset, + generateOrder_minimumReceived_head_offset, + generateOrder_selector_offset, + generateOrder_selector, + OneWord, + OneWordShift, + OnlyFullWordMask, + OrderFulfilled_baseDataSize, + OrderFulfilled_offer_length_baseOffset, + OrderParameters_consideration_head_offset, + OrderParameters_endTime_offset, + OrderParameters_offer_head_offset, + OrderParameters_startTime_offset, + OrderParameters_zoneHash_offset, + ratifyOrder_base_tail_offset, + ratifyOrder_consideration_head_offset, + ratifyOrder_context_head_offset, + ratifyOrder_contractNonce_offset, + ratifyOrder_head_offset, + ratifyOrder_orderHashes_head_offset, + ratifyOrder_selector_offset, + ratifyOrder_selector, + ReceivedItem_size, + Selector_length, + SixtyThreeBytes, + SpentItem_size_shift, + SpentItem_size, + validateOrder_head_offset, + validateOrder_selector_offset, + validateOrder_selector, + validateOrder_zoneParameters_offset, + ZoneParameters_base_tail_offset, + ZoneParameters_basicOrderFixedElements_length, + ZoneParameters_consideration_head_offset, + ZoneParameters_endTime_offset, + ZoneParameters_extraData_head_offset, + ZoneParameters_fulfiller_offset, + ZoneParameters_offer_head_offset, + ZoneParameters_offerer_offset, + ZoneParameters_orderHashes_head_offset, + ZoneParameters_selectorAndPointer_length, + ZoneParameters_startTime_offset, + ZoneParameters_zoneHash_offset +} from "./ConsiderationConstants.sol"; + +import { + BasicOrderParameters, + OrderParameters +} from "./ConsiderationStructs.sol"; + +import { + CalldataPointer, + getFreeMemoryPointer, + MemoryPointer +} from "../helpers/PointerLibraries.sol"; + +contract ConsiderationEncoder { + /** + * @dev Takes a bytes array and casts it to a memory pointer. + * + * @param obj A bytes array in memory. + * + * @return ptr A memory pointer to the start of the bytes array in memory. + */ + function toMemoryPointer( + bytes memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Takes an array of bytes32 types and casts it to a memory pointer. + * + * @param obj An array of bytes32 types in memory. + * + * @return ptr A memory pointer to the start of the array of bytes32 types + * in memory. + */ + function toMemoryPointer( + bytes32[] memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Takes a bytes array in memory and copies it to a new location in + * memory. + * + * @param src A memory pointer referencing the bytes array to be copied (and + * pointing to the length of the bytes array). + * @param src A memory pointer referencing the location in memory to copy + * the bytes array to (and pointing to the length of the copied + * bytes array). + * + * @return size The size of the bytes array. + */ + function _encodeBytes( + MemoryPointer src, + MemoryPointer dst + ) internal view returns (uint256 size) { + unchecked { + // Mask the length of the bytes array to protect against overflow + // and round up to the nearest word. + // Note: `size` also includes the 1 word that stores the length. + size = (src.readUint256() + SixtyThreeBytes) & OnlyFullWordMask; + + // Copy the bytes array to the new memory location. + src.copy(dst, size); + } + } + + /** + * @dev Takes an OrderParameters struct and a context bytes array in memory + * and encodes it as `generateOrder` calldata. + * + * @param orderParameters The OrderParameters struct used to construct the + * encoded `generateOrder` calldata. + * @param context The context bytes array used to construct the + * encoded `generateOrder` calldata. + * + * @return dst A memory pointer referencing the encoded `generateOrder` + * calldata. + * @return size The size of the bytes array. + */ + function _encodeGenerateOrder( + OrderParameters memory orderParameters, + bytes memory context + ) internal view returns (MemoryPointer dst, uint256 size) { + // Get the memory pointer for the OrderParameters struct. + MemoryPointer src = orderParameters.toMemoryPointer(); + + // Get free memory pointer to write calldata to. + dst = getFreeMemoryPointer(); + + // Write generateOrder selector and get pointer to start of calldata. + dst.write(generateOrder_selector); + dst = dst.offset(generateOrder_selector_offset); + + // Get pointer to the beginning of the encoded data. + MemoryPointer dstHead = dst.offset(generateOrder_head_offset); + + // Write `fulfiller` to calldata. + dstHead.write(msg.sender); + + // Initialize tail offset, used to populate the minimumReceived array. + uint256 tailOffset = generateOrder_base_tail_offset; + + // Write offset to minimumReceived. + dstHead.offset(generateOrder_minimumReceived_head_offset).write( + tailOffset + ); + + // Get memory pointer to `orderParameters.offer.length`. + MemoryPointer srcOfferPointer = src + .offset(OrderParameters_offer_head_offset) + .readMemoryPointer(); + + // Encode the offer array as a `SpentItem[]`. + uint256 minimumReceivedSize = _encodeSpentItems( + srcOfferPointer, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate maximumSpent array. + tailOffset += minimumReceivedSize; + } + + // Write offset to maximumSpent. + dstHead.offset(generateOrder_maximumSpent_head_offset).write( + tailOffset + ); + + // Get memory pointer to `orderParameters.consideration.length`. + MemoryPointer srcConsiderationPointer = src + .offset(OrderParameters_consideration_head_offset) + .readMemoryPointer(); + + // Encode the consideration array as a `SpentItem[]`. + uint256 maximumSpentSize = _encodeSpentItems( + srcConsiderationPointer, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate context array. + tailOffset += maximumSpentSize; + } + + // Write offset to context. + dstHead.offset(generateOrder_context_head_offset).write(tailOffset); + + // Get memory pointer to context. + MemoryPointer srcContext = toMemoryPointer(context); + + // Encode context as a bytes array. + uint256 contextSize = _encodeBytes( + srcContext, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment the tail offset, now used to determine final size. + tailOffset += contextSize; + + // Derive the final size by including the selector. + size = Selector_length + tailOffset; + } + } + + /** + * @dev Takes an order hash (e.g. offerer shifted 96 bits to the left XOR'd + * with the contract nonce in the case of contract orders), an + * OrderParameters struct, context bytes array, and an array of order + * hashes for each order included as part of the current fulfillment + * and encodes it as `ratifyOrder` calldata. + * + * @param orderHash The order hash (e.g. shl(0x60, offerer) ^ nonce). + * @param orderParameters The OrderParameters struct used to construct the + * encoded `ratifyOrder` calldata. + * @param context The context bytes array used to construct the + * encoded `ratifyOrder` calldata. + * @param orderHashes An array of bytes32 values representing the order + * hashes of all orders included as part of the + * current fulfillment. + * @param shiftedOfferer The offerer for the order, shifted 96 bits to the + * left. + * + * @return dst A memory pointer referencing the encoded `ratifyOrder` + * calldata. + * @return size The size of the bytes array. + */ + function _encodeRatifyOrder( + bytes32 orderHash, // e.g. shl(0x60, offerer) ^ contract nonce + OrderParameters memory orderParameters, + bytes memory context, // encoded based on the schemaID + bytes32[] memory orderHashes, + uint256 shiftedOfferer + ) internal view returns (MemoryPointer dst, uint256 size) { + // Get free memory pointer to write calldata to. This isn't allocated as + // it is only used for a single function call. + dst = getFreeMemoryPointer(); + + // Write ratifyOrder selector and get pointer to start of calldata. + dst.write(ratifyOrder_selector); + dst = dst.offset(ratifyOrder_selector_offset); + + // Get pointer to the beginning of the encoded data. + MemoryPointer dstHead = dst.offset(ratifyOrder_head_offset); + + // Write contractNonce to calldata via xor(orderHash, shiftedOfferer). + dstHead.offset(ratifyOrder_contractNonce_offset).write( + uint256(orderHash) ^ shiftedOfferer + ); + + // Initialize tail offset, used to populate the offer array. + uint256 tailOffset = ratifyOrder_base_tail_offset; + MemoryPointer src = orderParameters.toMemoryPointer(); + + // Write offset to `offer`. + dstHead.write(tailOffset); + + // Get memory pointer to `orderParameters.offer.length`. + MemoryPointer srcOfferPointer = src + .offset(OrderParameters_offer_head_offset) + .readMemoryPointer(); + + // Encode the offer array as a `SpentItem[]`. + uint256 offerSize = _encodeSpentItems( + srcOfferPointer, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate consideration array. + tailOffset += offerSize; + } + + // Write offset to consideration. + dstHead.offset(ratifyOrder_consideration_head_offset).write(tailOffset); + + // Get pointer to `orderParameters.consideration.length`. + MemoryPointer srcConsiderationPointer = src + .offset(OrderParameters_consideration_head_offset) + .readMemoryPointer(); + + // Encode the consideration array as a `ReceivedItem[]`. + uint256 considerationSize = _encodeConsiderationAsReceivedItems( + srcConsiderationPointer, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate context array. + tailOffset += considerationSize; + } + + // Write offset to context. + dstHead.offset(ratifyOrder_context_head_offset).write(tailOffset); + + // Encode context. + uint256 contextSize = _encodeBytes( + toMemoryPointer(context), + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate orderHashes array. + tailOffset += contextSize; + } + + // Write offset to orderHashes. + dstHead.offset(ratifyOrder_orderHashes_head_offset).write(tailOffset); + + // Encode orderHashes. + uint256 orderHashesSize = _encodeOrderHashes( + toMemoryPointer(orderHashes), + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment the tail offset, now used to determine final size. + tailOffset += orderHashesSize; + + // Derive the final size by including the selector. + size = Selector_length + tailOffset; + } + } + + /** + * @dev Takes an order hash, OrderParameters struct, extraData bytes array, + * and array of order hashes for each order included as part of the + * current fulfillment and encodes it as `validateOrder` calldata. + * Note that future, new versions of this contract may end up writing + * to a memory region that might have been potentially dirtied by the + * accumulator. Since the book-keeping for the accumulator does not + * update the free memory pointer, it will be necessary to ensure that + * all bytes in the memory in the range [dst, dst+size) are fully + * updated/written to in this function. + * + * @param orderHash The order hash. + * @param orderParameters The OrderParameters struct used to construct the + * encoded `validateOrder` calldata. + * @param extraData The extraData bytes array used to construct the + * encoded `validateOrder` calldata. + * @param orderHashes An array of bytes32 values representing the order + * hashes of all orders included as part of the + * current fulfillment. + * + * @return dst A memory pointer referencing the encoded `validateOrder` + * calldata. + * @return size The size of the bytes array. + */ + function _encodeValidateOrder( + bytes32 orderHash, + OrderParameters memory orderParameters, + bytes memory extraData, + bytes32[] memory orderHashes + ) internal view returns (MemoryPointer dst, uint256 size) { + // Get free memory pointer to write calldata to. This isn't allocated as + // it is only used for a single function call. + dst = getFreeMemoryPointer(); + + // Write validateOrder selector and get pointer to start of calldata. + dst.write(validateOrder_selector); + dst = dst.offset(validateOrder_selector_offset); + + // Get pointer to the beginning of the encoded data. + MemoryPointer dstHead = dst.offset(validateOrder_head_offset); + + // Write offset to zoneParameters to start of calldata. + dstHead.write(validateOrder_zoneParameters_offset); + + // Reuse `dstHead` as pointer to zoneParameters. + dstHead = dstHead.offset(validateOrder_zoneParameters_offset); + + // Write orderHash and fulfiller to zoneParameters. + dstHead.writeBytes32(orderHash); + dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender); + + // Get the memory pointer to the order parameters struct. + MemoryPointer src = orderParameters.toMemoryPointer(); + + // Copy offerer, startTime, endTime and zoneHash to zoneParameters. + dstHead.offset(ZoneParameters_offerer_offset).write(src.readUint256()); + dstHead.offset(ZoneParameters_startTime_offset).write( + src.offset(OrderParameters_startTime_offset).readUint256() + ); + dstHead.offset(ZoneParameters_endTime_offset).write( + src.offset(OrderParameters_endTime_offset).readUint256() + ); + dstHead.offset(ZoneParameters_zoneHash_offset).write( + src.offset(OrderParameters_zoneHash_offset).readUint256() + ); + + // Initialize tail offset, used to populate the offer array. + uint256 tailOffset = ZoneParameters_base_tail_offset; + + // Write offset to `offer`. + dstHead.offset(ZoneParameters_offer_head_offset).write(tailOffset); + + // Get pointer to `orderParameters.offer.length`. + MemoryPointer srcOfferPointer = src + .offset(OrderParameters_offer_head_offset) + .readMemoryPointer(); + + // Encode the offer array as a `SpentItem[]`. + uint256 offerSize = _encodeSpentItems( + srcOfferPointer, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate consideration array. + tailOffset += offerSize; + } + + // Write offset to consideration. + dstHead.offset(ZoneParameters_consideration_head_offset).write( + tailOffset + ); + + // Get pointer to `orderParameters.consideration.length`. + MemoryPointer srcConsiderationPointer = src + .offset(OrderParameters_consideration_head_offset) + .readMemoryPointer(); + + // Encode the consideration array as a `ReceivedItem[]`. + uint256 considerationSize = _encodeConsiderationAsReceivedItems( + srcConsiderationPointer, + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate extraData array. + tailOffset += considerationSize; + } + + // Write offset to extraData. + dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset); + // Copy extraData. + uint256 extraDataSize = _encodeBytes( + toMemoryPointer(extraData), + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment tail offset, now used to populate orderHashes array. + tailOffset += extraDataSize; + } + + // Write offset to orderHashes. + dstHead.offset(ZoneParameters_orderHashes_head_offset).write( + tailOffset + ); + + // Encode the order hashes array. + uint256 orderHashesSize = _encodeOrderHashes( + toMemoryPointer(orderHashes), + dstHead.offset(tailOffset) + ); + + unchecked { + // Increment the tail offset, now used to determine final size. + tailOffset += orderHashesSize; + + // Derive final size including selector and ZoneParameters pointer. + size = ZoneParameters_selectorAndPointer_length + tailOffset; + } + } + + /** + * @dev Takes an order hash and BasicOrderParameters struct (from calldata) + * and encodes it as `validateOrder` calldata. + * + * @param orderHash The order hash. + * @param parameters The BasicOrderParameters struct used to construct the + * encoded `validateOrder` calldata. + * + * @return dst A memory pointer referencing the encoded `validateOrder` + * calldata. + * @return size The size of the bytes array. + */ + function _encodeValidateBasicOrder( + bytes32 orderHash, + BasicOrderParameters calldata parameters + ) internal view returns (MemoryPointer dst, uint256 size) { + // Get free memory pointer to write calldata to. This isn't allocated as + // it is only used for a single function call. + dst = getFreeMemoryPointer(); + + // Write validateOrder selector and get pointer to start of calldata. + dst.write(validateOrder_selector); + dst = dst.offset(validateOrder_selector_offset); + + // Get pointer to the beginning of the encoded data. + MemoryPointer dstHead = dst.offset(validateOrder_head_offset); + + // Write offset to zoneParameters to start of calldata. + dstHead.write(validateOrder_zoneParameters_offset); + + // Reuse `dstHead` as pointer to zoneParameters. + dstHead = dstHead.offset(validateOrder_zoneParameters_offset); + + // Write offerer, orderHash and fulfiller to zoneParameters. + dstHead.writeBytes32(orderHash); + dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender); + dstHead.offset(ZoneParameters_offerer_offset).write(parameters.offerer); + + // Copy startTime, endTime and zoneHash to zoneParameters. + CalldataPointer.wrap(BasicOrder_startTime_cdPtr).copy( + dstHead.offset(ZoneParameters_startTime_offset), + BasicOrder_startTimeThroughZoneHash_size + ); + + // Initialize tail offset, used for the offer + consideration arrays. + uint256 tailOffset = ZoneParameters_base_tail_offset; + + // Write offset to offer from event data into target calldata. + dstHead.offset(ZoneParameters_offer_head_offset).write(tailOffset); + + unchecked { + // Write consideration offset next (located 5 words after offer). + dstHead.offset(ZoneParameters_consideration_head_offset).write( + tailOffset + BasicOrder_common_params_size + ); + + // Retrieve the offset to the length of additional recipients. + uint256 additionalRecipientsLength = CalldataPointer + .wrap(BasicOrder_additionalRecipients_length_cdPtr) + .readUint256(); + + // Derive offset to event data using base offset & total recipients. + uint256 offerDataOffset = OrderFulfilled_offer_length_baseOffset + + additionalRecipientsLength * + OneWord; + + // Derive size of offer and consideration data. + // 2 words (lengths) + 4 (offer data) + 5 (consideration 1) + 5 * ar + uint256 offerAndConsiderationSize = OrderFulfilled_baseDataSize + + (additionalRecipientsLength * ReceivedItem_size); + + // Copy offer and consideration data from event data to calldata. + MemoryPointer.wrap(offerDataOffset).copy( + dstHead.offset(tailOffset), + offerAndConsiderationSize + ); + + // Increment tail offset, now used to populate extraData array. + tailOffset += offerAndConsiderationSize; + } + + // Write empty bytes for extraData. + dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset); + dstHead.offset(tailOffset).write(0); + + unchecked { + // Increment tail offset, now used to populate orderHashes array. + tailOffset += OneWord; + } + + // Write offset to orderHashes. + dstHead.offset(ZoneParameters_orderHashes_head_offset).write( + tailOffset + ); + + // Write length = 1 to the orderHashes array. + dstHead.offset(tailOffset).write(1); + + unchecked { + // Write the single order hash to the orderHashes array. + dstHead.offset(tailOffset + OneWord).writeBytes32(orderHash); + + // Final size: selector, ZoneParameters pointer, orderHashes & tail. + size = ZoneParameters_basicOrderFixedElements_length + tailOffset; + } + } + + /** + * @dev Takes a memory pointer to an array of bytes32 values representing + * the order hashes included as part of the fulfillment and a memory + * pointer to a location to copy it to, and copies the source data to + * the destination in memory. + * + * @param srcLength A memory pointer referencing the order hashes array to + * be copied (and pointing to the length of the array). + * @param dstLength A memory pointer referencing the location in memory to + * copy the orderHashes array to (and pointing to the + * length of the copied array). + * + * @return size The size of the order hashes array (including the length). + */ + function _encodeOrderHashes( + MemoryPointer srcLength, + MemoryPointer dstLength + ) internal view returns (uint256 size) { + // Read length of the array from source and write to destination. + uint256 length = srcLength.readUint256(); + dstLength.write(length); + + unchecked { + // Determine head & tail size as one word per element in the array. + uint256 headAndTailSize = length << OneWordShift; + + // Copy the tail starting from the next element of the source to the + // next element of the destination. + srcLength.next().copy(dstLength.next(), headAndTailSize); + + // Set size to the length of the tail plus one word for length. + size = headAndTailSize + OneWord; + } + } + + /** + * @dev Takes a memory pointer to an offer or consideration array and a + * memory pointer to a location to copy it to, and copies the source + * data to the destination in memory as a SpentItem array. + * + * @param srcLength A memory pointer referencing the offer or consideration + * array to be copied as a SpentItem array (and pointing to + * the length of the original array). + * @param dstLength A memory pointer referencing the location in memory to + * copy the offer array to (and pointing to the length of + * the copied array). + * + * @return size The size of the SpentItem array (including the length). + */ + function _encodeSpentItems( + MemoryPointer srcLength, + MemoryPointer dstLength + ) internal pure returns (uint256 size) { + assembly { + // Read length of the array from source and write to destination. + let length := mload(srcLength) + mstore(dstLength, length) + + // Get pointer to first item's head position in the array, + // containing the item's pointer in memory. The head pointer will be + // incremented until it reaches the tail position (start of the + // array data). + let mPtrHead := add(srcLength, OneWord) + + // Position in memory to write next item for calldata. Since + // SpentItem has a fixed length, the array elements do not contain + // head elements in calldata, they are concatenated together after + // the array length. + let cdPtrData := add(dstLength, OneWord) + + // Pointer to end of array head in memory. + let mPtrHeadEnd := add(mPtrHead, shl(OneWordShift, length)) + + for { + + } lt(mPtrHead, mPtrHeadEnd) { + + } { + // Read pointer to data for array element from head position. + let mPtrTail := mload(mPtrHead) + + // Copy itemType, token, identifier, amount to calldata. + mstore(cdPtrData, mload(mPtrTail)) + mstore( + add(cdPtrData, Common_token_offset), + mload(add(mPtrTail, Common_token_offset)) + ) + mstore( + add(cdPtrData, Common_identifier_offset), + mload(add(mPtrTail, Common_identifier_offset)) + ) + mstore( + add(cdPtrData, Common_amount_offset), + mload(add(mPtrTail, Common_amount_offset)) + ) + + mPtrHead := add(mPtrHead, OneWord) + cdPtrData := add(cdPtrData, SpentItem_size) + } + + size := add(OneWord, shl(SpentItem_size_shift, length)) + } + } + + /** + * @dev Takes a memory pointer to an consideration array and a memory + * pointer to a location to copy it to, and copies the source data to + * the destination in memory as a ReceivedItem array. + * + * @param srcLength A memory pointer referencing the consideration array to + * be copied as a ReceivedItem array (and pointing to the + * length of the original array). + * @param dstLength A memory pointer referencing the location in memory to + * copy the consideration array to as a ReceivedItem array + * (and pointing to the length of the new array). + * + * @return size The size of the ReceivedItem array (including the length). + */ + function _encodeConsiderationAsReceivedItems( + MemoryPointer srcLength, + MemoryPointer dstLength + ) internal view returns (uint256 size) { + unchecked { + // Read length of the array from source and write to destination. + uint256 length = srcLength.readUint256(); + dstLength.write(length); + + // Get pointer to first item's head position in the array, + // containing the item's pointer in memory. The head pointer will be + // incremented until it reaches the tail position (start of the + // array data). + MemoryPointer srcHead = srcLength.next(); + MemoryPointer srcHeadEnd = srcHead.offset(length << OneWordShift); + + // Position in memory to write next item for calldata. Since + // ReceivedItem has a fixed length, the array elements do not + // contain offsets in calldata, they are concatenated together after + // the array length. + MemoryPointer dstHead = dstLength.next(); + while (srcHead.lt(srcHeadEnd)) { + MemoryPointer srcTail = srcHead.pptr(); + srcTail.copy(dstHead, ReceivedItem_size); + srcHead = srcHead.next(); + dstHead = dstHead.offset(ReceivedItem_size); + } + + size = OneWord + (length * ReceivedItem_size); + } + } +} diff --git a/contracts/lib/ConsiderationEnums.sol b/contracts/lib/ConsiderationEnums.sol index c8797f204..261ae6b3c 100644 --- a/contracts/lib/ConsiderationEnums.sol +++ b/contracts/lib/ConsiderationEnums.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore enum OrderType { // 0: no partial fills, anyone can execute FULL_OPEN, @@ -13,10 +12,12 @@ enum OrderType { FULL_RESTRICTED, // 3: partial fills supported, only offerer or zone can execute - PARTIAL_RESTRICTED + PARTIAL_RESTRICTED, + + // 4: contract order type + CONTRACT } -// prettier-ignore enum BasicOrderType { // 0: no partial fills, anyone can execute ETH_TO_ERC721_FULL_OPEN, @@ -91,7 +92,6 @@ enum BasicOrderType { ERC1155_TO_ERC20_PARTIAL_RESTRICTED } -// prettier-ignore enum BasicOrderRouteType { // 0: provide Ether (or other native token) to receive offered ERC721 item. ETH_TO_ERC721, @@ -112,7 +112,6 @@ enum BasicOrderRouteType { ERC1155_TO_ERC20 } -// prettier-ignore enum ItemType { // 0: ETH on mainnet, MATIC on polygon, etc. NATIVE, @@ -133,7 +132,6 @@ enum ItemType { ERC1155_WITH_CRITERIA } -// prettier-ignore enum Side { // 0: Items that can be spent OFFER, diff --git a/contracts/lib/ConsiderationErrorConstants.sol b/contracts/lib/ConsiderationErrorConstants.sol new file mode 100644 index 000000000..62615a635 --- /dev/null +++ b/contracts/lib/ConsiderationErrorConstants.sol @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +uint256 constant Error_selector_offset = 0x1c; + +/* + * error MissingFulfillmentComponentOnAggregation(uint8 side) + * - Defined in FulfillmentApplicationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: side + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant MissingFulfillmentComponentOnAggregation_error_selector = ( + 0x375c24c1 +); +uint256 constant MissingFulfillmentComponentOnAggregation_error_side_ptr = 0x20; +uint256 constant MissingFulfillmentComponentOnAggregation_error_length = 0x24; + +/* + * error OfferAndConsiderationRequiredOnFulfillment() + * - Defined in FulfillmentApplicationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant OfferAndConsiderationRequiredOnFulfillment_error_selector = ( + 0x98e9db6e +); +uint256 constant OfferAndConsiderationRequiredOnFulfillment_error_length = 0x04; + +/* + * error MismatchedFulfillmentOfferAndConsiderationComponents( + * uint256 fulfillmentIndex + * ) + * - Defined in FulfillmentApplicationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: fulfillmentIndex + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant MismatchedOfferAndConsiderationComponents_error_selector = ( + 0xbced929d +); +uint256 constant MismatchedOfferAndConsiderationComponents_error_idx_ptr = 0x20; +uint256 constant MismatchedOfferAndConsiderationComponents_error_length = 0x24; + +/* + * error InvalidFulfillmentComponentData() + * - Defined in FulfillmentApplicationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InvalidFulfillmentComponentData_error_selector = 0x7fda7279; +uint256 constant InvalidFulfillmentComponentData_error_length = 0x04; + +/* + * error InexactFraction() + * - Defined in AmountDerivationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InexactFraction_error_selector = 0xc63cf089; +uint256 constant InexactFraction_error_length = 0x04; + +/* + * error OrderCriteriaResolverOutOfRange(uint8 side) + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: side + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant OrderCriteriaResolverOutOfRange_error_selector = 0x133c37c6; +uint256 constant OrderCriteriaResolverOutOfRange_error_side_ptr = 0x20; +uint256 constant OrderCriteriaResolverOutOfRange_error_length = 0x24; + +/* + * error UnresolvedOfferCriteria(uint256 orderIndex, uint256 offerIndex) + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderIndex + * - 0x40: offerIndex + * Revert buffer is memory[0x1c:0x60] + */ +uint256 constant UnresolvedOfferCriteria_error_selector = 0xd6929332; +uint256 constant UnresolvedOfferCriteria_error_orderIndex_ptr = 0x20; +uint256 constant UnresolvedOfferCriteria_error_offerIndex_ptr = 0x40; +uint256 constant UnresolvedOfferCriteria_error_length = 0x44; + +/* + * error UnresolvedConsiderationCriteria( + * uint256 orderIndex, + * uint256 considerationIndex + * ) + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderIndex + * - 0x40: considerationIndex + * Revert buffer is memory[0x1c:0x60] + */ +uint256 constant UnresolvedConsiderationCriteria_error_selector = 0xa8930e9a; +uint256 constant UnresolvedConsiderationCriteria_error_orderIndex_ptr = 0x20; +uint256 constant UnresolvedConsiderationCriteria_error_considerationIdx_ptr = ( + 0x40 +); +uint256 constant UnresolvedConsiderationCriteria_error_length = 0x44; + +/* + * error OfferCriteriaResolverOutOfRange() + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant OfferCriteriaResolverOutOfRange_error_selector = 0xbfb3f8ce; +// uint256 constant OfferCriteriaResolverOutOfRange_error_length = 0x04; + +/* + * error ConsiderationCriteriaResolverOutOfRange() + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant ConsiderationCriteriaResolverOutOfRange_error_selector = ( + 0x6088d7de +); +uint256 constant ConsiderationCriteriaResolverOutOfRange_err_selector = ( + 0x6088d7de +); +// uint256 constant ConsiderationCriteriaResolverOutOfRange_error_length = 0x04; + +/* + * error CriteriaNotEnabledForItem() + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant CriteriaNotEnabledForItem_error_selector = 0x94eb6af6; +uint256 constant CriteriaNotEnabledForItem_error_length = 0x04; + +/* + * error InvalidProof() + * - Defined in CriteriaResolutionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InvalidProof_error_selector = 0x09bde339; +uint256 constant InvalidProof_error_length = 0x04; + +/* + * error InvalidRestrictedOrder(bytes32 orderHash) + * - Defined in ZoneInteractionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderHash + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant InvalidRestrictedOrder_error_selector = 0xfb5014fc; +uint256 constant InvalidRestrictedOrder_error_orderHash_ptr = 0x20; +uint256 constant InvalidRestrictedOrder_error_length = 0x24; + +/* + * error InvalidContractOrder(bytes32 orderHash) + * - Defined in ZoneInteractionErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderHash + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant InvalidContractOrder_error_selector = 0x93979285; +uint256 constant InvalidContractOrder_error_orderHash_ptr = 0x20; +uint256 constant InvalidContractOrder_error_length = 0x24; + +/* + * error BadSignatureV(uint8 v) + * - Defined in SignatureVerificationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: v + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant BadSignatureV_error_selector = 0x1f003d0a; +uint256 constant BadSignatureV_error_v_ptr = 0x20; +uint256 constant BadSignatureV_error_length = 0x24; + +/* + * error InvalidSigner() + * - Defined in SignatureVerificationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InvalidSigner_error_selector = 0x815e1d64; +uint256 constant InvalidSigner_error_length = 0x04; + +/* + * error InvalidSignature() + * - Defined in SignatureVerificationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InvalidSignature_error_selector = 0x8baa579f; +uint256 constant InvalidSignature_error_length = 0x04; + +/* + * error BadContractSignature() + * - Defined in SignatureVerificationErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant BadContractSignature_error_selector = 0x4f7fb80d; +uint256 constant BadContractSignature_error_length = 0x04; + +/* + * error InvalidERC721TransferAmount(uint256 amount) + * - Defined in TokenTransferrerErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: amount + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant InvalidERC721TransferAmount_error_selector = 0x69f95827; +uint256 constant InvalidERC721TransferAmount_error_amount_ptr = 0x20; +uint256 constant InvalidERC721TransferAmount_error_length = 0x24; + +/* + * error MissingItemAmount() + * - Defined in TokenTransferrerErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant MissingItemAmount_error_selector = 0x91b3e514; +uint256 constant MissingItemAmount_error_length = 0x04; + +/* + * error UnusedItemParameters() + * - Defined in TokenTransferrerErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant UnusedItemParameters_error_selector = 0x6ab37ce7; +uint256 constant UnusedItemParameters_error_length = 0x04; + +/* + * error NoReentrantCalls() + * - Defined in ReentrancyErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant NoReentrantCalls_error_selector = 0x7fa8a987; +uint256 constant NoReentrantCalls_error_length = 0x04; + +/* + * error OrderAlreadyFilled(bytes32 orderHash) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderHash + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant OrderAlreadyFilled_error_selector = 0x10fda3e1; +uint256 constant OrderAlreadyFilled_error_orderHash_ptr = 0x20; +uint256 constant OrderAlreadyFilled_error_length = 0x24; + +/* + * error InvalidTime(uint256 startTime, uint256 endTime) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: startTime + * - 0x40: endTime + * Revert buffer is memory[0x1c:0x60] + */ +uint256 constant InvalidTime_error_selector = 0x21ccfeb7; +uint256 constant InvalidTime_error_startTime_ptr = 0x20; +uint256 constant InvalidTime_error_endTime_ptr = 0x40; +uint256 constant InvalidTime_error_length = 0x44; + +/* + * error InvalidConduit(bytes32 conduitKey, address conduit) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: conduitKey + * - 0x40: conduit + * Revert buffer is memory[0x1c:0x60] + */ +uint256 constant InvalidConduit_error_selector = 0x1cf99b26; +uint256 constant InvalidConduit_error_conduitKey_ptr = 0x20; +uint256 constant InvalidConduit_error_conduit_ptr = 0x40; +uint256 constant InvalidConduit_error_length = 0x44; + +/* + * error MissingOriginalConsiderationItems() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant MissingOriginalConsiderationItems_error_selector = 0x466aa616; +uint256 constant MissingOriginalConsiderationItems_error_length = 0x04; + +/* + * error InvalidCallToConduit(address conduit) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: conduit + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant InvalidCallToConduit_error_selector = 0xd13d53d4; +uint256 constant InvalidCallToConduit_error_conduit_ptr = 0x20; +uint256 constant InvalidCallToConduit_error_length = 0x24; + +/* + * error ConsiderationNotMet( + * uint256 orderIndex, + * uint256 considerationIndex, + * uint256 shortfallAmount + * ) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderIndex + * - 0x40: considerationIndex + * - 0x60: shortfallAmount + * Revert buffer is memory[0x1c:0x80] + */ +uint256 constant ConsiderationNotMet_error_selector = 0xa5f54208; +uint256 constant ConsiderationNotMet_error_orderIndex_ptr = 0x20; +uint256 constant ConsiderationNotMet_error_considerationIndex_ptr = 0x40; +uint256 constant ConsiderationNotMet_error_shortfallAmount_ptr = 0x60; +uint256 constant ConsiderationNotMet_error_length = 0x64; + +/* + * error InsufficientNativeTokensSupplied() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InsufficientNativeTokensSupplied_error_selector = 0x8ffff980; +uint256 constant InsufficientNativeTokensSupplied_error_length = 0x04; + +/* + * error NativeTokenTransferGenericFailure(address account, uint256 amount) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: account + * - 0x40: amount + * Revert buffer is memory[0x1c:0x60] + */ +uint256 constant NativeTokenTransferGenericFailure_error_selector = 0xbc806b96; +uint256 constant NativeTokenTransferGenericFailure_error_account_ptr = 0x20; +uint256 constant NativeTokenTransferGenericFailure_error_amount_ptr = 0x40; +uint256 constant NativeTokenTransferGenericFailure_error_length = 0x44; + +/* + * error PartialFillsNotEnabledForOrder() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant PartialFillsNotEnabledForOrder_error_selector = 0xa11b63ff; +uint256 constant PartialFillsNotEnabledForOrder_error_length = 0x04; + +/* + * error OrderIsCancelled(bytes32 orderHash) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderHash + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant OrderIsCancelled_error_selector = 0x1a515574; +uint256 constant OrderIsCancelled_error_orderHash_ptr = 0x20; +uint256 constant OrderIsCancelled_error_length = 0x24; + +/* + * error OrderPartiallyFilled(bytes32 orderHash) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: orderHash + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant OrderPartiallyFilled_error_selector = 0xee9e0e63; +uint256 constant OrderPartiallyFilled_error_orderHash_ptr = 0x20; +uint256 constant OrderPartiallyFilled_error_length = 0x24; + +/* + * error CannotCancelOrder() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant CannotCancelOrder_error_selector = 0xfed398fc; +uint256 constant CannotCancelOrder_error_length = 0x04; + +/* + * error BadFraction() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant BadFraction_error_selector = 0x5a052b32; +uint256 constant BadFraction_error_length = 0x04; + +/* + * error InvalidMsgValue(uint256 value) + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: value + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant InvalidMsgValue_error_selector = 0xa61be9f0; +uint256 constant InvalidMsgValue_error_value_ptr = 0x20; +uint256 constant InvalidMsgValue_error_length = 0x24; + +/* + * error InvalidBasicOrderParameterEncoding() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InvalidBasicOrderParameterEncoding_error_selector = 0x39f3e3fd; +uint256 constant InvalidBasicOrderParameterEncoding_error_length = 0x04; + +/* + * error NoSpecifiedOrdersAvailable() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant NoSpecifiedOrdersAvailable_error_selector = 0xd5da9a1b; +uint256 constant NoSpecifiedOrdersAvailable_error_length = 0x04; + +/* + * error InvalidNativeOfferItem() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant InvalidNativeOfferItem_error_selector = 0x12d3f5a3; +uint256 constant InvalidNativeOfferItem_error_length = 0x04; + +/* + * error ConsiderationLengthNotEqualToTotalOriginal() + * - Defined in ConsiderationEventsAndErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * Revert buffer is memory[0x1c:0x20] + */ +uint256 constant ConsiderationLengthNotEqualToTotalOriginal_error_selector = ( + 0x2165628a +); +uint256 constant ConsiderationLengthNotEqualToTotalOriginal_error_length = 0x04; + +/* + * error Panic(uint256 code) + * - Built-in Solidity error + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: code + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant Panic_error_selector = 0x4e487b71; +uint256 constant Panic_error_code_ptr = 0x20; +uint256 constant Panic_error_length = 0x24; + +uint256 constant Panic_arithmetic = 0x11; +// uint256 constant Panic_resource = 0x41; diff --git a/contracts/lib/ConsiderationErrors.sol b/contracts/lib/ConsiderationErrors.sol new file mode 100644 index 000000000..f5f7f1d42 --- /dev/null +++ b/contracts/lib/ConsiderationErrors.sol @@ -0,0 +1,712 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { Side } from "./ConsiderationEnums.sol"; + +import { + BadFraction_error_length, + BadFraction_error_selector, + CannotCancelOrder_error_length, + CannotCancelOrder_error_selector, + ConsiderationLengthNotEqualToTotalOriginal_error_length, + ConsiderationLengthNotEqualToTotalOriginal_error_selector, + ConsiderationNotMet_error_considerationIndex_ptr, + ConsiderationNotMet_error_length, + ConsiderationNotMet_error_orderIndex_ptr, + ConsiderationNotMet_error_selector, + ConsiderationNotMet_error_shortfallAmount_ptr, + CriteriaNotEnabledForItem_error_length, + CriteriaNotEnabledForItem_error_selector, + Error_selector_offset, + InsufficientNativeTokensSupplied_error_length, + InsufficientNativeTokensSupplied_error_selector, + InvalidBasicOrderParameterEncoding_error_length, + InvalidBasicOrderParameterEncoding_error_selector, + InvalidCallToConduit_error_conduit_ptr, + InvalidCallToConduit_error_length, + InvalidCallToConduit_error_selector, + InvalidConduit_error_conduit_ptr, + InvalidConduit_error_conduitKey_ptr, + InvalidConduit_error_length, + InvalidConduit_error_selector, + InvalidContractOrder_error_length, + InvalidContractOrder_error_orderHash_ptr, + InvalidContractOrder_error_selector, + InvalidERC721TransferAmount_error_amount_ptr, + InvalidERC721TransferAmount_error_length, + InvalidERC721TransferAmount_error_selector, + InvalidMsgValue_error_length, + InvalidMsgValue_error_selector, + InvalidMsgValue_error_value_ptr, + InvalidNativeOfferItem_error_length, + InvalidNativeOfferItem_error_selector, + InvalidProof_error_length, + InvalidProof_error_selector, + InvalidTime_error_endTime_ptr, + InvalidTime_error_length, + InvalidTime_error_selector, + InvalidTime_error_startTime_ptr, + MismatchedOfferAndConsiderationComponents_error_idx_ptr, + MismatchedOfferAndConsiderationComponents_error_length, + MismatchedOfferAndConsiderationComponents_error_selector, + MissingFulfillmentComponentOnAggregation_error_length, + MissingFulfillmentComponentOnAggregation_error_selector, + MissingFulfillmentComponentOnAggregation_error_side_ptr, + MissingOriginalConsiderationItems_error_length, + MissingOriginalConsiderationItems_error_selector, + NoReentrantCalls_error_length, + NoReentrantCalls_error_selector, + NoSpecifiedOrdersAvailable_error_length, + NoSpecifiedOrdersAvailable_error_selector, + OfferAndConsiderationRequiredOnFulfillment_error_length, + OfferAndConsiderationRequiredOnFulfillment_error_selector, + OrderAlreadyFilled_error_length, + OrderAlreadyFilled_error_orderHash_ptr, + OrderAlreadyFilled_error_selector, + OrderCriteriaResolverOutOfRange_error_length, + OrderCriteriaResolverOutOfRange_error_selector, + OrderCriteriaResolverOutOfRange_error_side_ptr, + OrderIsCancelled_error_length, + OrderIsCancelled_error_orderHash_ptr, + OrderIsCancelled_error_selector, + OrderPartiallyFilled_error_length, + OrderPartiallyFilled_error_orderHash_ptr, + OrderPartiallyFilled_error_selector, + PartialFillsNotEnabledForOrder_error_length, + PartialFillsNotEnabledForOrder_error_selector, + UnresolvedConsiderationCriteria_error_considerationIdx_ptr, + UnresolvedConsiderationCriteria_error_length, + UnresolvedConsiderationCriteria_error_orderIndex_ptr, + UnresolvedConsiderationCriteria_error_selector, + UnresolvedOfferCriteria_error_length, + UnresolvedOfferCriteria_error_offerIndex_ptr, + UnresolvedOfferCriteria_error_orderIndex_ptr, + UnresolvedOfferCriteria_error_selector, + UnusedItemParameters_error_length, + UnusedItemParameters_error_selector +} from "./ConsiderationErrorConstants.sol"; + +/** + * @dev Reverts the current transaction with a "BadFraction" error message. + */ +function _revertBadFraction() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, BadFraction_error_selector) + + // revert(abi.encodeWithSignature("BadFraction()")) + revert(Error_selector_offset, BadFraction_error_length) + } +} + +/** + * @dev Reverts the current transaction with a "ConsiderationNotMet" error + * message, including the provided order index, consideration index, and + * shortfall amount. + * + * @param orderIndex The index of the order that did not meet the + * consideration criteria. + * @param considerationIndex The index of the consideration item that did not + * meet its criteria. + * @param shortfallAmount The amount by which the consideration criteria were + * not met. + */ +function _revertConsiderationNotMet( + uint256 orderIndex, + uint256 considerationIndex, + uint256 shortfallAmount +) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, ConsiderationNotMet_error_selector) + + // Store arguments. + mstore(ConsiderationNotMet_error_orderIndex_ptr, orderIndex) + mstore( + ConsiderationNotMet_error_considerationIndex_ptr, + considerationIndex + ) + mstore(ConsiderationNotMet_error_shortfallAmount_ptr, shortfallAmount) + + // revert(abi.encodeWithSignature( + // "ConsiderationNotMet(uint256,uint256,uint256)", + // orderIndex, + // considerationIndex, + // shortfallAmount + // )) + revert(Error_selector_offset, ConsiderationNotMet_error_length) + } +} + +/** + * @dev Reverts the current transaction with a "CriteriaNotEnabledForItem" error + * message. + */ +function _revertCriteriaNotEnabledForItem() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, CriteriaNotEnabledForItem_error_selector) + + // revert(abi.encodeWithSignature("CriteriaNotEnabledForItem()")) + revert(Error_selector_offset, CriteriaNotEnabledForItem_error_length) + } +} + +/** + * @dev Reverts the current transaction with an + * "InsufficientNativeTokensSupplied" error message. + */ +function _revertInsufficientNativeTokensSupplied() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InsufficientNativeTokensSupplied_error_selector) + + // revert(abi.encodeWithSignature("InsufficientNativeTokensSupplied()")) + revert( + Error_selector_offset, + InsufficientNativeTokensSupplied_error_length + ) + } +} + +/** + * @dev Reverts the current transaction with an + * "InvalidBasicOrderParameterEncoding" error message. + */ +function _revertInvalidBasicOrderParameterEncoding() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidBasicOrderParameterEncoding_error_selector) + + // revert(abi.encodeWithSignature( + // "InvalidBasicOrderParameterEncoding()" + // )) + revert( + Error_selector_offset, + InvalidBasicOrderParameterEncoding_error_length + ) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidCallToConduit" error + * message, including the provided address of the conduit that was called + * improperly. + * + * @param conduit The address of the conduit that was called improperly. + */ +function _revertInvalidCallToConduit(address conduit) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidCallToConduit_error_selector) + + // Store argument. + mstore(InvalidCallToConduit_error_conduit_ptr, conduit) + + // revert(abi.encodeWithSignature( + // "InvalidCallToConduit(address)", + // conduit + // )) + revert(Error_selector_offset, InvalidCallToConduit_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "CannotCancelOrder" error + * message. + */ +function _revertCannotCancelOrder() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, CannotCancelOrder_error_selector) + + // revert(abi.encodeWithSignature("CannotCancelOrder()")) + revert(Error_selector_offset, CannotCancelOrder_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidConduit" error message, + * including the provided key and address of the invalid conduit. + * + * @param conduitKey The key of the invalid conduit. + * @param conduit The address of the invalid conduit. + */ +function _revertInvalidConduit(bytes32 conduitKey, address conduit) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidConduit_error_selector) + + // Store arguments. + mstore(InvalidConduit_error_conduitKey_ptr, conduitKey) + mstore(InvalidConduit_error_conduit_ptr, conduit) + + // revert(abi.encodeWithSignature( + // "InvalidConduit(bytes32,address)", + // conduitKey, + // conduit + // )) + revert(Error_selector_offset, InvalidConduit_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidERC721TransferAmount" + * error message. + * + * @param amount The invalid amount. + */ +function _revertInvalidERC721TransferAmount(uint256 amount) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidERC721TransferAmount_error_selector) + + // Store argument. + mstore(InvalidERC721TransferAmount_error_amount_ptr, amount) + + // revert(abi.encodeWithSignature( + // "InvalidERC721TransferAmount(uint256)", + // amount + // )) + revert(Error_selector_offset, InvalidERC721TransferAmount_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidMsgValue" error message, + * including the invalid value that was sent in the transaction's + * `msg.value` field. + * + * @param value The invalid value that was sent in the transaction's `msg.value` + * field. + */ +function _revertInvalidMsgValue(uint256 value) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidMsgValue_error_selector) + + // Store argument. + mstore(InvalidMsgValue_error_value_ptr, value) + + // revert(abi.encodeWithSignature("InvalidMsgValue(uint256)", value)) + revert(Error_selector_offset, InvalidMsgValue_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidNativeOfferItem" error + * message. + */ +function _revertInvalidNativeOfferItem() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidNativeOfferItem_error_selector) + + // revert(abi.encodeWithSignature("InvalidNativeOfferItem()")) + revert(Error_selector_offset, InvalidNativeOfferItem_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidProof" error message. + */ +function _revertInvalidProof() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidProof_error_selector) + + // revert(abi.encodeWithSignature("InvalidProof()")) + revert(Error_selector_offset, InvalidProof_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidContractOrder" error + * message. + * + * @param orderHash The hash of the contract order that caused the error. + */ +function _revertInvalidContractOrder(bytes32 orderHash) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidContractOrder_error_selector) + + // Store arguments. + mstore(InvalidContractOrder_error_orderHash_ptr, orderHash) + + // revert(abi.encodeWithSignature( + // "InvalidContractOrder(bytes32)", + // orderHash + // )) + revert(Error_selector_offset, InvalidContractOrder_error_length) + } +} + +/** + * @dev Reverts the current transaction with an "InvalidTime" error message. + * + * @param startTime The time at which the order becomes active. + * @param endTime The time at which the order becomes inactive. + */ +function _revertInvalidTime(uint256 startTime, uint256 endTime) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, InvalidTime_error_selector) + + // Store arguments. + mstore(InvalidTime_error_startTime_ptr, startTime) + mstore(InvalidTime_error_endTime_ptr, endTime) + + // revert(abi.encodeWithSignature( + // "InvalidTime(uint256,uint256)", + // startTime, + // endTime + // )) + revert(Error_selector_offset, InvalidTime_error_length) + } +} + +/** + * @dev Reverts execution with a + * "MismatchedFulfillmentOfferAndConsiderationComponents" error message. + * + * @param fulfillmentIndex The index of the fulfillment that caused the + * error. + */ +function _revertMismatchedFulfillmentOfferAndConsiderationComponents( + uint256 fulfillmentIndex +) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, MismatchedOfferAndConsiderationComponents_error_selector) + + // Store fulfillment index argument. + mstore( + MismatchedOfferAndConsiderationComponents_error_idx_ptr, + fulfillmentIndex + ) + + // revert(abi.encodeWithSignature( + // "MismatchedFulfillmentOfferAndConsiderationComponents(uint256)", + // fulfillmentIndex + // )) + revert( + Error_selector_offset, + MismatchedOfferAndConsiderationComponents_error_length + ) + } +} + +/** + * @dev Reverts execution with a "MissingFulfillmentComponentOnAggregation" + * error message. + * + * @param side The side of the fulfillment component that is missing (0 for + * offer, 1 for consideration). + * + */ +function _revertMissingFulfillmentComponentOnAggregation(Side side) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, MissingFulfillmentComponentOnAggregation_error_selector) + + // Store argument. + mstore(MissingFulfillmentComponentOnAggregation_error_side_ptr, side) + + // revert(abi.encodeWithSignature( + // "MissingFulfillmentComponentOnAggregation(uint8)", + // side + // )) + revert( + Error_selector_offset, + MissingFulfillmentComponentOnAggregation_error_length + ) + } +} + +/** + * @dev Reverts execution with a "MissingOriginalConsiderationItems" error + * message. + */ +function _revertMissingOriginalConsiderationItems() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, MissingOriginalConsiderationItems_error_selector) + + // revert(abi.encodeWithSignature( + // "MissingOriginalConsiderationItems()" + // )) + revert( + Error_selector_offset, + MissingOriginalConsiderationItems_error_length + ) + } +} + +/** + * @dev Reverts execution with a "NoReentrantCalls" error message. + */ +function _revertNoReentrantCalls() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, NoReentrantCalls_error_selector) + + // revert(abi.encodeWithSignature("NoReentrantCalls()")) + revert(Error_selector_offset, NoReentrantCalls_error_length) + } +} + +/** + * @dev Reverts execution with a "NoSpecifiedOrdersAvailable" error message. + */ +function _revertNoSpecifiedOrdersAvailable() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, NoSpecifiedOrdersAvailable_error_selector) + + // revert(abi.encodeWithSignature("NoSpecifiedOrdersAvailable()")) + revert(Error_selector_offset, NoSpecifiedOrdersAvailable_error_length) + } +} + +/** + * @dev Reverts execution with a "OfferAndConsiderationRequiredOnFulfillment" + * error message. + */ +function _revertOfferAndConsiderationRequiredOnFulfillment() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, OfferAndConsiderationRequiredOnFulfillment_error_selector) + + // revert(abi.encodeWithSignature( + // "OfferAndConsiderationRequiredOnFulfillment()" + // )) + revert( + Error_selector_offset, + OfferAndConsiderationRequiredOnFulfillment_error_length + ) + } +} + +/** + * @dev Reverts execution with an "OrderAlreadyFilled" error message. + * + * @param orderHash The hash of the order that has already been filled. + */ +function _revertOrderAlreadyFilled(bytes32 orderHash) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, OrderAlreadyFilled_error_selector) + + // Store argument. + mstore(OrderAlreadyFilled_error_orderHash_ptr, orderHash) + + // revert(abi.encodeWithSignature( + // "OrderAlreadyFilled(bytes32)", + // orderHash + // )) + revert(Error_selector_offset, OrderAlreadyFilled_error_length) + } +} + +/** + * @dev Reverts execution with an "OrderCriteriaResolverOutOfRange" error + * message. + * + * @param side The side of the criteria that is missing (0 for offer, 1 for + * consideration). + * + */ +function _revertOrderCriteriaResolverOutOfRange(Side side) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, OrderCriteriaResolverOutOfRange_error_selector) + + // Store argument. + mstore(OrderCriteriaResolverOutOfRange_error_side_ptr, side) + + // revert(abi.encodeWithSignature( + // "OrderCriteriaResolverOutOfRange(uint8)", + // side + // )) + revert( + Error_selector_offset, + OrderCriteriaResolverOutOfRange_error_length + ) + } +} + +/** + * @dev Reverts execution with an "OrderIsCancelled" error message. + * + * @param orderHash The hash of the order that has already been cancelled. + */ +function _revertOrderIsCancelled(bytes32 orderHash) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, OrderIsCancelled_error_selector) + + // Store argument. + mstore(OrderIsCancelled_error_orderHash_ptr, orderHash) + + // revert(abi.encodeWithSignature( + // "OrderIsCancelled(bytes32)", + // orderHash + // )) + revert(Error_selector_offset, OrderIsCancelled_error_length) + } +} + +/** + * @dev Reverts execution with an "OrderPartiallyFilled" error message. + * + * @param orderHash The hash of the order that has already been partially + * filled. + */ +function _revertOrderPartiallyFilled(bytes32 orderHash) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, OrderPartiallyFilled_error_selector) + + // Store argument. + mstore(OrderPartiallyFilled_error_orderHash_ptr, orderHash) + + // revert(abi.encodeWithSignature( + // "OrderPartiallyFilled(bytes32)", + // orderHash + // )) + revert(Error_selector_offset, OrderPartiallyFilled_error_length) + } +} + +/** + * @dev Reverts execution with a "PartialFillsNotEnabledForOrder" error message. + */ +function _revertPartialFillsNotEnabledForOrder() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, PartialFillsNotEnabledForOrder_error_selector) + + // revert(abi.encodeWithSignature("PartialFillsNotEnabledForOrder()")) + revert( + Error_selector_offset, + PartialFillsNotEnabledForOrder_error_length + ) + } +} + +/** + * @dev Reverts execution with an "UnresolvedConsiderationCriteria" error + * message. + */ +function _revertUnresolvedConsiderationCriteria( + uint256 orderIndex, + uint256 considerationIndex +) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, UnresolvedConsiderationCriteria_error_selector) + + // Store orderIndex and considerationIndex arguments. + mstore(UnresolvedConsiderationCriteria_error_orderIndex_ptr, orderIndex) + mstore( + UnresolvedConsiderationCriteria_error_considerationIdx_ptr, + considerationIndex + ) + + // revert(abi.encodeWithSignature( + // "UnresolvedConsiderationCriteria(uint256, uint256)", + // orderIndex, + // considerationIndex + // )) + revert( + Error_selector_offset, + UnresolvedConsiderationCriteria_error_length + ) + } +} + +/** + * @dev Reverts execution with an "UnresolvedOfferCriteria" error message. + */ +function _revertUnresolvedOfferCriteria( + uint256 orderIndex, + uint256 offerIndex +) pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, UnresolvedOfferCriteria_error_selector) + + // Store arguments. + mstore(UnresolvedOfferCriteria_error_orderIndex_ptr, orderIndex) + mstore(UnresolvedOfferCriteria_error_offerIndex_ptr, offerIndex) + + // revert(abi.encodeWithSignature( + // "UnresolvedOfferCriteria(uint256, uint256)", + // orderIndex, + // offerIndex + // )) + revert(Error_selector_offset, UnresolvedOfferCriteria_error_length) + } +} + +/** + * @dev Reverts execution with an "UnusedItemParameters" error message. + */ +function _revertUnusedItemParameters() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, UnusedItemParameters_error_selector) + + // revert(abi.encodeWithSignature("UnusedItemParameters()")) + revert(Error_selector_offset, UnusedItemParameters_error_length) + } +} + +/** + * @dev Reverts execution with a "ConsiderationLengthNotEqualToTotalOriginal" + * error message. + */ +function _revertConsiderationLengthNotEqualToTotalOriginal() pure { + assembly { + // Store left-padded selector with push4 (reduces bytecode), + // mem[28:32] = selector + mstore(0, ConsiderationLengthNotEqualToTotalOriginal_error_selector) + + // revert(abi.encodeWithSignature( + // "ConsiderationLengthNotEqualToTotalOriginal()" + // )) + revert( + Error_selector_offset, + ConsiderationLengthNotEqualToTotalOriginal_error_length + ) + } +} diff --git a/contracts/lib/ConsiderationStructs.sol b/contracts/lib/ConsiderationStructs.sol index 064d01d9a..04cd4aaf0 100644 --- a/contracts/lib/ConsiderationStructs.sol +++ b/contracts/lib/ConsiderationStructs.sol @@ -1,14 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { - OrderType, BasicOrderType, ItemType, + OrderType, Side } from "./ConsiderationEnums.sol"; +import { + CalldataPointer, + MemoryPointer +} from "../helpers/PointerLibraries.sol"; + /** * @dev An order contains eleven components: an offerer, a zone (or account that * can cancel the order or restrict who can fulfill the order depending on @@ -163,9 +167,9 @@ struct Order { * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill) * and a denominator (the total size of the order) in addition to the * signature and other order parameters. It also supports an optional field - * for supplying extra data; this data will be included in a staticcall to - * `isValidOrderIncludingExtraData` on the zone for the order if the order - * type is restricted and the offerer or zone are not the caller. + * for supplying extra data; this data will be provided to the zone if the + * order type is restricted and the zone is not the caller, or will be + * provided to the offerer as context for contract order types. */ struct AdvancedOrder { OrderParameters parameters; @@ -242,3 +246,534 @@ struct Execution { address offerer; bytes32 conduitKey; } + +/** + * @dev Restricted orders are validated post-execution by calling validateOrder + * on the zone. This struct provides context about the order fulfillment + * and any supplied extraData, as well as all order hashes fulfilled in a + * call to a match or fulfillAvailable method. + */ +struct ZoneParameters { + bytes32 orderHash; + address fulfiller; + address offerer; + SpentItem[] offer; + ReceivedItem[] consideration; + bytes extraData; + bytes32[] orderHashes; + uint256 startTime; + uint256 endTime; + bytes32 zoneHash; +} + +/** + * @dev Zones and contract offerers can communicate which schemas they implement + * along with any associated metadata related to each schema. + */ +struct Schema { + uint256 id; + bytes metadata; +} + +using StructPointers for OrderComponents global; +using StructPointers for OfferItem global; +using StructPointers for ConsiderationItem global; +using StructPointers for SpentItem global; +using StructPointers for ReceivedItem global; +using StructPointers for BasicOrderParameters global; +using StructPointers for AdditionalRecipient global; +using StructPointers for OrderParameters global; +using StructPointers for Order global; +using StructPointers for AdvancedOrder global; +using StructPointers for OrderStatus global; +using StructPointers for CriteriaResolver global; +using StructPointers for Fulfillment global; +using StructPointers for FulfillmentComponent global; +using StructPointers for Execution global; +using StructPointers for ZoneParameters global; + +/** + * @dev This library provides a set of functions for converting structs to + * pointers. + */ +library StructPointers { + /** + * @dev Get a MemoryPointer from OrderComponents. + * + * @param obj The OrderComponents object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + OrderComponents memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from OrderComponents. + * + * @param obj The OrderComponents object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + OrderComponents calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from OfferItem. + * + * @param obj The OfferItem object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + OfferItem memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from OfferItem. + * + * @param obj The OfferItem object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + OfferItem calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from ConsiderationItem. + * + * @param obj The ConsiderationItem object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + ConsiderationItem memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from ConsiderationItem. + * + * @param obj The ConsiderationItem object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + ConsiderationItem calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from SpentItem. + * + * @param obj The SpentItem object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + SpentItem memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from SpentItem. + * + * @param obj The SpentItem object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + SpentItem calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from ReceivedItem. + * + * @param obj The ReceivedItem object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + ReceivedItem memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from ReceivedItem. + * + * @param obj The ReceivedItem object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + ReceivedItem calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from BasicOrderParameters. + * + * @param obj The BasicOrderParameters object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + BasicOrderParameters memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from BasicOrderParameters. + * + * @param obj The BasicOrderParameters object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + BasicOrderParameters calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from AdditionalRecipient. + * + * @param obj The AdditionalRecipient object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + AdditionalRecipient memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from AdditionalRecipient. + * + * @param obj The AdditionalRecipient object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + AdditionalRecipient calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from OrderParameters. + * + * @param obj The OrderParameters object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + OrderParameters memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from OrderParameters. + * + * @param obj The OrderParameters object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + OrderParameters calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from Order. + * + * @param obj The Order object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + Order memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from Order. + * + * @param obj The Order object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + Order calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from AdvancedOrder. + * + * @param obj The AdvancedOrder object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + AdvancedOrder memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from AdvancedOrder. + * + * @param obj The AdvancedOrder object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + AdvancedOrder calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from OrderStatus. + * + * @param obj The OrderStatus object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + OrderStatus memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from OrderStatus. + * + * @param obj The OrderStatus object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + OrderStatus calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from CriteriaResolver. + * + * @param obj The CriteriaResolver object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + CriteriaResolver memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from CriteriaResolver. + * + * @param obj The CriteriaResolver object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + CriteriaResolver calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from Fulfillment. + * + * @param obj The Fulfillment object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + Fulfillment memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from Fulfillment. + * + * @param obj The Fulfillment object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + Fulfillment calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from FulfillmentComponent. + * + * @param obj The FulfillmentComponent object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + FulfillmentComponent memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from FulfillmentComponent. + * + * @param obj The FulfillmentComponent object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + FulfillmentComponent calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from Execution. + * + * @param obj The Execution object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + Execution memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from Execution. + * + * @param obj The Execution object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + Execution calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a MemoryPointer from ZoneParameters. + * + * @param obj The ZoneParameters object. + * + * @return ptr The MemoryPointer. + */ + function toMemoryPointer( + ZoneParameters memory obj + ) internal pure returns (MemoryPointer ptr) { + assembly { + ptr := obj + } + } + + /** + * @dev Get a CalldataPointer from ZoneParameters. + * + * @param obj The ZoneParameters object. + * + * @return ptr The CalldataPointer. + */ + function toCalldataPointer( + ZoneParameters calldata obj + ) internal pure returns (CalldataPointer ptr) { + assembly { + ptr := obj + } + } +} diff --git a/contracts/lib/CounterManager.sol b/contracts/lib/CounterManager.sol index 69532b391..710411a17 100644 --- a/contracts/lib/CounterManager.sol +++ b/contracts/lib/CounterManager.sol @@ -1,13 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -// prettier-ignore import { ConsiderationEventsAndErrors } from "../interfaces/ConsiderationEventsAndErrors.sol"; import { ReentrancyGuard } from "./ReentrancyGuard.sol"; +import { + Counter_blockhash_shift, + OneWord, + TwoWords +} from "./ConsiderationConstants.sol"; + /** * @title CounterManager * @author 0age @@ -19,9 +24,13 @@ contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard { mapping(address => uint256) private _counters; /** - * @dev Internal function to cancel all orders from a given offerer with a - * given zone in bulk by incrementing a counter. Note that only the - * offerer may increment the counter. + * @dev Internal function to cancel all orders from a given offerer in bulk + * by incrementing a counter by a large, quasi-random interval. Note + * that only the offerer may increment the counter. Note that the + * counter is incremented by a large, quasi-random interval, which + * makes it infeasible to "activate" signed orders by incrementing the + * counter. This activation functionality can be achieved instead with + * restricted orders or contract orders. * * @return newCounter The new counter. */ @@ -29,10 +38,29 @@ contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard { // Ensure that the reentrancy guard is not currently set. _assertNonReentrant(); - // Skip overflow check as counter cannot be incremented that far. - unchecked { - // Increment current counter for the supplied offerer. - newCounter = ++_counters[msg.sender]; + // Utilize assembly to access counters storage mapping directly. Skip + // overflow check as counter cannot be incremented that far. + assembly { + // Use second half of previous block hash as a quasi-random number. + let quasiRandomNumber := shr( + Counter_blockhash_shift, + blockhash(sub(number(), 1)) + ) + + // Write the caller to scratch space. + mstore(0, caller()) + + // Write the storage slot for _counters to scratch space. + mstore(OneWord, _counters.slot) + + // Derive the storage pointer for the counter value. + let storagePointer := keccak256(0, TwoWords) + + // Derive new counter value using random number and original value. + newCounter := add(quasiRandomNumber, sload(storagePointer)) + + // Store the updated counter value. + sstore(storagePointer, newCounter) } // Emit an event containing the new counter. @@ -47,11 +75,9 @@ contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard { * * @return currentCounter The current counter. */ - function _getCounter(address offerer) - internal - view - returns (uint256 currentCounter) - { + function _getCounter( + address offerer + ) internal view returns (uint256 currentCounter) { // Return the counter for the supplied offerer. currentCounter = _counters[offerer]; } diff --git a/contracts/lib/CriteriaResolution.sol b/contracts/lib/CriteriaResolution.sol index 6c814958a..076c3852d 100644 --- a/contracts/lib/CriteriaResolution.sol +++ b/contracts/lib/CriteriaResolution.sol @@ -1,24 +1,42 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { ItemType, Side } from "./ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, - ConsiderationItem, - OrderParameters, AdvancedOrder, - CriteriaResolver + CriteriaResolver, + MemoryPointer, + OfferItem, + OrderParameters } from "./ConsiderationStructs.sol"; -import "./ConsiderationConstants.sol"; +import { + _revertCriteriaNotEnabledForItem, + _revertInvalidProof, + _revertOrderCriteriaResolverOutOfRange, + _revertUnresolvedConsiderationCriteria, + _revertUnresolvedOfferCriteria +} from "./ConsiderationErrors.sol"; -// prettier-ignore import { CriteriaResolutionErrors } from "../interfaces/CriteriaResolutionErrors.sol"; +import { + OneWord, + OneWordShift, + OrderParameters_consideration_head_offset, + Selector_length, + TwoWords +} from "./ConsiderationConstants.sol"; + +import { + ConsiderationCriteriaResolverOutOfRange_err_selector, + Error_selector_offset, + OfferCriteriaResolverOutOfRange_error_selector +} from "./ConsiderationErrorConstants.sol"; + /** * @title CriteriaResolution * @author 0age @@ -64,104 +82,86 @@ contract CriteriaResolution is CriteriaResolutionErrors { // Ensure that the order index is in range. if (orderIndex >= totalAdvancedOrders) { - revert OrderCriteriaResolverOutOfRange(); + _revertOrderCriteriaResolverOutOfRange( + criteriaResolver.side + ); } + // Retrieve the referenced advanced order. + AdvancedOrder memory advancedOrder = advancedOrders[orderIndex]; + // Skip criteria resolution for order if not fulfilled. - if (advancedOrders[orderIndex].numerator == 0) { + if (advancedOrder.numerator == 0) { continue; } // Retrieve the parameters for the order. OrderParameters memory orderParameters = ( - advancedOrders[orderIndex].parameters + advancedOrder.parameters ); - // Read component index from memory and place it on the stack. - uint256 componentIndex = criteriaResolver.index; + { + // Get a pointer to the list of items to give to + // _updateCriteriaItem. If the resolver refers to a + // consideration item, this array pointer will be replaced + // with the consideration array. + OfferItem[] memory items = orderParameters.offer; - // Declare values for item's type and criteria. - ItemType itemType; - uint256 identifierOrCriteria; + // Read component index from memory and place it on stack. + uint256 componentIndex = criteriaResolver.index; - // If the criteria resolver refers to an offer item... - if (criteriaResolver.side == Side.OFFER) { - // Retrieve the offer. - OfferItem[] memory offer = orderParameters.offer; - - // Ensure that the component index is in range. - if (componentIndex >= offer.length) { - revert OfferCriteriaResolverOutOfRange(); - } - - // Retrieve relevant item using the component index. - OfferItem memory offerItem = offer[componentIndex]; - - // Read item type and criteria from memory & place on stack. - itemType = offerItem.itemType; - identifierOrCriteria = offerItem.identifierOrCriteria; - - // Optimistically update item type to remove criteria usage. - // Use assembly to operate on ItemType enum as a number. - ItemType newItemType; - assembly { - // Item type 4 becomes 2 and item type 5 becomes 3. - newItemType := sub(3, eq(itemType, 4)) - } - offerItem.itemType = newItemType; - - // Optimistically update identifier w/ supplied identifier. - offerItem.identifierOrCriteria = criteriaResolver - .identifier; - } else { - // Otherwise, the resolver refers to a consideration item. - ConsiderationItem[] memory consideration = ( - orderParameters.consideration + // Get error selector for `OfferCriteriaResolverOutOfRange`. + uint256 errorSelector = ( + OfferCriteriaResolverOutOfRange_error_selector ); - // Ensure that the component index is in range. - if (componentIndex >= consideration.length) { - revert ConsiderationCriteriaResolverOutOfRange(); + // If the resolver refers to a consideration item... + if (criteriaResolver.side != Side.OFFER) { + // Get the pointer to `orderParameters.consideration` + // Using the array directly has a significant impact on + // the optimized compiler output. + MemoryPointer considerationPtr = orderParameters + .toMemoryPointer() + .pptr(OrderParameters_consideration_head_offset); + + // Replace the items pointer with a pointer to the + // consideration array. + assembly { + items := considerationPtr + } + + // Replace the error selector with the selector for + // `ConsiderationCriteriaResolverOutOfRange`. + errorSelector = ( + ConsiderationCriteriaResolverOutOfRange_err_selector + ); } - // Retrieve relevant item using order and component index. - ConsiderationItem memory considerationItem = ( - consideration[componentIndex] - ); - - // Read item type and criteria from memory & place on stack. - itemType = considerationItem.itemType; - identifierOrCriteria = ( - considerationItem.identifierOrCriteria - ); - - // Optimistically update item type to remove criteria usage. - // Use assembly to operate on ItemType enum as a number. - ItemType newItemType; - assembly { - // Item type 4 becomes 2 and item type 5 becomes 3. - newItemType := sub(3, eq(itemType, 4)) + // Ensure that the component index is in range. + if (componentIndex >= items.length) { + assembly { + // Revert with either + // `OfferCriteriaResolverOutOfRange()` or + // `ConsiderationCriteriaResolverOutOfRange()`, + // depending on whether the resolver refers to a + // consideration item. + mstore(0, errorSelector) + // revert(abi.encodeWithSignature( + // "OfferCriteriaResolverOutOfRange()" + // )) + // or + // revert(abi.encodeWithSignature( + // "ConsiderationCriteriaResolverOutOfRange()" + // )) + revert(Error_selector_offset, Selector_length) + } } - considerationItem.itemType = newItemType; - // Optimistically update identifier w/ supplied identifier. - considerationItem.identifierOrCriteria = ( - criteriaResolver.identifier - ); - } - - // Ensure the specified item type indicates criteria usage. - if (!_isItemWithCriteria(itemType)) { - revert CriteriaNotEnabledForItem(); - } - - // If criteria is not 0 (i.e. a collection-wide offer)... - if (identifierOrCriteria != uint256(0)) { - // Verify identifier inclusion in criteria root using proof. - _verifyProof( - criteriaResolver.identifier, - identifierOrCriteria, - criteriaResolver.criteriaProof + // Apply the criteria resolver to the item in question. + _updateCriteriaItem( + items, + componentIndex, + criteriaResolver ); } } @@ -192,7 +192,7 @@ contract CriteriaResolution is CriteriaResolutionErrors { orderParameters.consideration[j].itemType ) ) { - revert UnresolvedConsiderationCriteria(); + _revertUnresolvedConsiderationCriteria(i, j); } } @@ -205,13 +205,64 @@ contract CriteriaResolution is CriteriaResolutionErrors { if ( _isItemWithCriteria(orderParameters.offer[j].itemType) ) { - revert UnresolvedOfferCriteria(); + _revertUnresolvedOfferCriteria(i, j); } } } } } + /** + * @dev Internal pure function to update a criteria item. + * + * @param offer The offer containing the item to update. + * @param componentIndex The index of the item to update. + * @param criteriaResolver The criteria resolver to use to update the item. + */ + function _updateCriteriaItem( + OfferItem[] memory offer, + uint256 componentIndex, + CriteriaResolver memory criteriaResolver + ) internal pure { + // Retrieve relevant item using the component index. + OfferItem memory offerItem = offer[componentIndex]; + + // Read item type and criteria from memory & place on stack. + ItemType itemType = offerItem.itemType; + + // Ensure the specified item type indicates criteria usage. + if (!_isItemWithCriteria(itemType)) { + _revertCriteriaNotEnabledForItem(); + } + + uint256 identifierOrCriteria = offerItem.identifierOrCriteria; + + // If criteria is not 0 (i.e. a collection-wide criteria-based item)... + if (identifierOrCriteria != uint256(0)) { + // Verify identifier inclusion in criteria root using proof. + _verifyProof( + criteriaResolver.identifier, + identifierOrCriteria, + criteriaResolver.criteriaProof + ); + } else if (criteriaResolver.criteriaProof.length != 0) { + // Revert if non-empty proof is supplied for a collection-wide item. + _revertInvalidProof(); + } + + // Update item type to remove criteria usage. + // Use assembly to operate on ItemType enum as a number. + ItemType newItemType; + assembly { + // Item type 4 becomes 2 and item type 5 becomes 3. + newItemType := sub(3, eq(itemType, 4)) + } + offerItem.itemType = newItemType; + + // Update identifier w/ supplied identifier. + offerItem.identifierOrCriteria = criteriaResolver.identifier; + } + /** * @dev Internal pure function to check whether a given item type represents * a criteria-based ERC721 or ERC1155 item (e.g. an item that can be @@ -223,11 +274,9 @@ contract CriteriaResolution is CriteriaResolutionErrors { * @return withCriteria A boolean indicating that the item type in question * represents a criteria-based item. */ - function _isItemWithCriteria(ItemType itemType) - internal - pure - returns (bool withCriteria) - { + function _isItemWithCriteria( + ItemType itemType + ) internal pure returns (bool withCriteria) { // ERC721WithCriteria is ItemType 4. ERC1155WithCriteria is ItemType 5. assembly { withCriteria := gt(itemType, 3) @@ -258,14 +307,13 @@ contract CriteriaResolution is CriteriaResolutionErrors { // Derive the hash of the leaf to use as the initial proof element. let computedHash := keccak256(0, OneWord) - // Based on: https://github.com/Rari-Capital/solmate/blob/v7/src/utils/MerkleProof.sol // Get memory start location of the first element in proof array. let data := add(proof, OneWord) // Iterate over each proof element to compute the root hash. for { // Left shift by 5 is equivalent to multiplying by 0x20. - let end := add(data, shl(5, mload(proof))) + let end := add(data, shl(OneWordShift, mload(proof))) } lt(data, end) { // Increment by one word at a time. data := add(data, OneWord) @@ -276,7 +324,7 @@ contract CriteriaResolution is CriteriaResolutionErrors { // Sort proof elements and place them in scratch space. // Slot of `computedHash` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl(5, gt(computedHash, loadedData)) + let scratch := shl(OneWordShift, gt(computedHash, loadedData)) // Store elements to hash contiguously in scratch space. Scratch // space is 64 bytes (0x00 - 0x3f) & both elements are 32 bytes. @@ -293,7 +341,7 @@ contract CriteriaResolution is CriteriaResolutionErrors { // Revert if computed hash does not equal supplied root. if (!isValid) { - revert InvalidProof(); + _revertInvalidProof(); } } } diff --git a/contracts/lib/Executor.sol b/contracts/lib/Executor.sol index 6e030b29b..68b207e56 100644 --- a/contracts/lib/Executor.sol +++ b/contracts/lib/Executor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; @@ -13,7 +13,40 @@ import { Verifiers } from "./Verifiers.sol"; import { TokenTransferrer } from "./TokenTransferrer.sol"; -import "./ConsiderationConstants.sol"; +import { + Accumulator_array_length_ptr, + Accumulator_array_offset_ptr, + Accumulator_array_offset, + Accumulator_conduitKey_ptr, + Accumulator_itemSizeOffsetDifference, + Accumulator_selector_ptr, + AccumulatorArmed, + AccumulatorDisarmed, + Conduit_transferItem_amount_ptr, + Conduit_transferItem_from_ptr, + Conduit_transferItem_identifier_ptr, + Conduit_transferItem_size, + Conduit_transferItem_to_ptr, + Conduit_transferItem_token_ptr, + FreeMemoryPointerSlot, + OneWord, + TwoWords +} from "./ConsiderationConstants.sol"; + +import { + Error_selector_offset, + NativeTokenTransferGenericFailure_error_account_ptr, + NativeTokenTransferGenericFailure_error_amount_ptr, + NativeTokenTransferGenericFailure_error_length, + NativeTokenTransferGenericFailure_error_selector +} from "./ConsiderationErrorConstants.sol"; + +import { + _revertInvalidCallToConduit, + _revertInvalidConduit, + _revertInvalidERC721TransferAmount, + _revertUnusedItemParameters +} from "./ConsiderationErrors.sol"; /** * @title Executor @@ -56,15 +89,15 @@ contract Executor is Verifiers, TokenTransferrer { if (item.itemType == ItemType.NATIVE) { // Ensure neither the token nor the identifier parameters are set. if ((uint160(item.token) | item.identifier) != 0) { - revert UnusedItemParameters(); + _revertUnusedItemParameters(); } // transfer the native tokens to the recipient. - _transferEth(item.recipient, item.amount); + _transferNativeTokens(item.recipient, item.amount); } else if (item.itemType == ItemType.ERC20) { // Ensure that no identifier is supplied. if (item.identifier != 0) { - revert UnusedItemParameters(); + _revertUnusedItemParameters(); } // Transfer ERC20 tokens from the source to the recipient. @@ -101,122 +134,6 @@ contract Executor is Verifiers, TokenTransferrer { } } - /** - * @dev Internal function to transfer an individual ERC721 or ERC1155 item - * from a given originator to a given recipient. The accumulator will - * be bypassed, meaning that this function should be utilized in cases - * where multiple item transfers can be accumulated into a single - * conduit call. Sufficient approvals must be set, either on the - * respective conduit or on this contract itself. - * - * @param itemType The type of item to transfer, either ERC721 or ERC1155. - * @param token The token to transfer. - * @param from The originator of the transfer. - * @param to The recipient of the transfer. - * @param identifier The tokenId to transfer. - * @param amount The amount to transfer. - * @param conduitKey A bytes32 value indicating what corresponding conduit, - * if any, to source token approvals from. The zero hash - * signifies that no conduit should be used, with direct - * approvals set on this contract. - */ - function _transferIndividual721Or1155Item( - ItemType itemType, - address token, - address from, - address to, - uint256 identifier, - uint256 amount, - bytes32 conduitKey - ) internal { - // Determine if the transfer is to be performed via a conduit. - if (conduitKey != bytes32(0)) { - // Use free memory pointer as calldata offset for the conduit call. - uint256 callDataOffset; - - // Utilize assembly to place each argument in free memory. - assembly { - // Retrieve the free memory pointer and use it as the offset. - callDataOffset := mload(FreeMemoryPointerSlot) - - // Write ConduitInterface.execute.selector to memory. - mstore(callDataOffset, Conduit_execute_signature) - - // Write the offset to the ConduitTransfer array in memory. - mstore( - add( - callDataOffset, - Conduit_execute_ConduitTransfer_offset_ptr - ), - Conduit_execute_ConduitTransfer_ptr - ) - - // Write the length of the ConduitTransfer array to memory. - mstore( - add( - callDataOffset, - Conduit_execute_ConduitTransfer_length_ptr - ), - Conduit_execute_ConduitTransfer_length - ) - - // Write the item type to memory. - mstore( - add(callDataOffset, Conduit_execute_transferItemType_ptr), - itemType - ) - - // Write the token to memory. - mstore( - add(callDataOffset, Conduit_execute_transferToken_ptr), - token - ) - - // Write the transfer source to memory. - mstore( - add(callDataOffset, Conduit_execute_transferFrom_ptr), - from - ) - - // Write the transfer recipient to memory. - mstore(add(callDataOffset, Conduit_execute_transferTo_ptr), to) - - // Write the token identifier to memory. - mstore( - add(callDataOffset, Conduit_execute_transferIdentifier_ptr), - identifier - ) - - // Write the transfer amount to memory. - mstore( - add(callDataOffset, Conduit_execute_transferAmount_ptr), - amount - ) - } - - // Perform the call to the conduit. - _callConduitUsingOffsets( - conduitKey, - callDataOffset, - OneConduitExecute_size - ); - } else { - // Otherwise, determine whether it is an ERC721 or ERC1155 item. - if (itemType == ItemType.ERC721) { - // Ensure that exactly one 721 item is being transferred. - if (amount != 1) { - revert InvalidERC721TransferAmount(); - } - - // Perform transfer via the token contract directly. - _performERC721Transfer(token, from, to, identifier); - } else { - // Perform transfer via the token contract directly. - _performERC1155Transfer(token, from, to, identifier, amount); - } - } - } - /** * @dev Internal function to transfer Ether or other native tokens to a * given recipient. @@ -224,7 +141,10 @@ contract Executor is Verifiers, TokenTransferrer { * @param to The recipient of the transfer. * @param amount The amount to transfer. */ - function _transferEth(address payable to, uint256 amount) internal { + function _transferNativeTokens( + address payable to, + uint256 amount + ) internal { // Ensure that the supplied amount is non-zero. _assertNonZeroAmount(amount); @@ -232,7 +152,7 @@ contract Executor is Verifiers, TokenTransferrer { bool success; assembly { - // Transfer the ETH and store if it succeeded or not. + // Transfer the native token and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } @@ -242,7 +162,27 @@ contract Executor is Verifiers, TokenTransferrer { _revertWithReasonIfOneIsReturned(); // Otherwise, revert with a generic error message. - revert EtherTransferGenericFailure(to, amount); + assembly { + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, NativeTokenTransferGenericFailure_error_selector) + + // Write `to` and `amount` arguments. + mstore(NativeTokenTransferGenericFailure_error_account_ptr, to) + mstore( + NativeTokenTransferGenericFailure_error_amount_ptr, + amount + ) + + // revert(abi.encodeWithSignature( + // "NativeTokenTransferGenericFailure(address,uint256)", + // to, + // amount + // )) + revert( + Error_selector_offset, + NativeTokenTransferGenericFailure_error_length + ) + } } } @@ -328,7 +268,7 @@ contract Executor is Verifiers, TokenTransferrer { if (conduitKey == bytes32(0)) { // Ensure that exactly one 721 item is being transferred. if (amount != 1) { - revert InvalidERC721TransferAmount(); + _revertInvalidERC721TransferAmount(amount); } // Perform transfer via the token contract directly. @@ -537,12 +477,12 @@ contract Executor is Verifiers, TokenTransferrer { _revertWithReasonIfOneIsReturned(); // Otherwise, revert with a generic error. - revert InvalidCallToConduit(conduit); + _revertInvalidCallToConduit(conduit); } // Ensure result was extracted and matches EIP-1271 magic value. if (result != ConduitInterface.execute.selector) { - revert InvalidConduit(conduitKey, conduit); + _revertInvalidConduit(conduitKey, conduit); } } @@ -556,11 +496,9 @@ contract Executor is Verifiers, TokenTransferrer { * @return accumulatorConduitKey The conduit key currently set for the * accumulator. */ - function _getAccumulatorConduitKey(bytes memory accumulator) - internal - pure - returns (bytes32 accumulatorConduitKey) - { + function _getAccumulatorConduitKey( + bytes memory accumulator + ) internal pure returns (bytes32 accumulatorConduitKey) { // Retrieve the current conduit key from the accumulator. assembly { accumulatorConduitKey := mload( diff --git a/contracts/lib/FulfillmentApplier.sol b/contracts/lib/FulfillmentApplier.sol index 02c0957f5..86ee261e6 100644 --- a/contracts/lib/FulfillmentApplier.sol +++ b/contracts/lib/FulfillmentApplier.sol @@ -1,26 +1,55 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -import { ItemType, Side } from "./ConsiderationEnums.sol"; +import { Side } from "./ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, - ConsiderationItem, - ReceivedItem, - OrderParameters, AdvancedOrder, Execution, - FulfillmentComponent + FulfillmentComponent, + ReceivedItem } from "./ConsiderationStructs.sol"; -import "./ConsiderationConstants.sol"; +import { + _revertMismatchedFulfillmentOfferAndConsiderationComponents, + _revertMissingFulfillmentComponentOnAggregation, + _revertOfferAndConsiderationRequiredOnFulfillment +} from "./ConsiderationErrors.sol"; -// prettier-ignore import { FulfillmentApplicationErrors } from "../interfaces/FulfillmentApplicationErrors.sol"; +import { + AdvancedOrder_numerator_offset, + Common_amount_offset, + Common_identifier_offset, + Common_token_offset, + Execution_conduit_offset, + Execution_offerer_offset, + Fulfillment_itemIndex_offset, + OneWord, + OneWordShift, + OrderParameters_conduit_offset, + OrderParameters_consideration_head_offset, + OrderParameters_offer_head_offset, + ReceivedItem_CommonParams_size, + ReceivedItem_recipient_offset, + ReceivedItem_size +} from "./ConsiderationConstants.sol"; + +import { + Error_selector_offset, + InvalidFulfillmentComponentData_error_length, + InvalidFulfillmentComponentData_error_selector, + MissingItemAmount_error_length, + MissingItemAmount_error_selector, + Panic_arithmetic, + Panic_error_code_ptr, + Panic_error_length, + Panic_error_selector +} from "./ConsiderationErrorConstants.sol"; + /** * @title FulfillmentApplier * @author 0age @@ -42,19 +71,22 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { * Note that each consideration amount must * be zero in order for the match operation * to be valid. + * @param fulfillmentIndex The index of the fulfillment being + * applied. * * @return execution The transfer performed as a result of the fulfillment. */ function _applyFulfillment( AdvancedOrder[] memory advancedOrders, - FulfillmentComponent[] calldata offerComponents, - FulfillmentComponent[] calldata considerationComponents + FulfillmentComponent[] memory offerComponents, + FulfillmentComponent[] memory considerationComponents, + uint256 fulfillmentIndex ) internal pure returns (Execution memory execution) { // Ensure 1+ of both offer and consideration components are supplied. if ( offerComponents.length == 0 || considerationComponents.length == 0 ) { - revert OfferAndConsiderationRequiredOnFulfillment(); + _revertOfferAndConsiderationRequiredOnFulfillment(); } // Declare a new Execution struct. @@ -70,6 +102,15 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { // Retrieve the consideration item from the execution struct. ReceivedItem memory considerationItem = considerationExecution.item; + // Skip aggregating offer items if no consideration items are available. + if (considerationItem.amount == 0) { + // Set the offerer and recipient to null address if execution + // amount is zero. This will cause the execution item to be skipped. + considerationExecution.offerer = address(0); + considerationExecution.item.recipient = payable(0); + return considerationExecution; + } + // Recipient does not need to be specified because it will always be set // to that of the consideration. // Validate & aggregate offer items to Execution object. @@ -79,13 +120,19 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { execution ); - // Ensure offer and consideration share types, tokens and identifiers. + // Ensure offer & consideration item types, tokens, & identifiers match. + // (a != b || c != d || e != f) == (((a ^ b) | (c ^ d) | (e ^ f)) != 0), + // but the second expression requires less gas to evaluate. if ( - execution.item.itemType != considerationItem.itemType || - execution.item.token != considerationItem.token || - execution.item.identifier != considerationItem.identifier + ((uint8(execution.item.itemType) ^ + uint8(considerationItem.itemType)) | + (uint160(execution.item.token) ^ + uint160(considerationItem.token)) | + (execution.item.identifier ^ considerationItem.identifier)) != 0 ) { - revert MismatchedFulfillmentOfferAndConsiderationComponents(); + _revertMismatchedFulfillmentOfferAndConsiderationComponents( + fulfillmentIndex + ); } // If total consideration amount exceeds the offer amount... @@ -105,9 +152,6 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { .startAmount = (considerationItem.amount - execution.item.amount); } - - // Reduce total consideration amount to equal the offer amount. - considerationItem.amount = execution.item.amount; } else { // Retrieve the first offer component from the fulfillment. FulfillmentComponent memory targetComponent = offerComponents[0]; @@ -166,7 +210,7 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { // Retrieve fulfillment components array length and place on stack. // Ensure at least one fulfillment component has been supplied. if (fulfillmentComponents.length == 0) { - revert MissingFulfillmentComponentOnAggregation(side); + _revertMissingFulfillmentComponentOnAggregation(side); } // If the fulfillment components are offer components... @@ -223,257 +267,198 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { Execution memory execution ) internal pure { assembly { - // Declare function for reverts on invalid fulfillment data. - function throwInvalidFulfillmentComponentData() { - // Store the InvalidFulfillmentComponentData error signature. - mstore(0, InvalidFulfillmentComponentData_error_signature) - - // Return, supplying InvalidFulfillmentComponentData signature. - revert(0, InvalidFulfillmentComponentData_error_len) - } - - // Declare function for reverts due to arithmetic overflows. - function throwOverflow() { - // Store the Panic error signature. - mstore(0, Panic_error_signature) - - // Store the arithmetic (0x11) panic code as initial argument. - mstore(Panic_error_offset, Panic_arithmetic) - - // Return, supplying Panic signature and arithmetic code. - revert(0, Panic_error_length) - } - - // Get position in offerComponents head. - let fulfillmentHeadPtr := add(offerComponents, OneWord) - - // Retrieve the order index using the fulfillment pointer. - let orderIndex := mload(mload(fulfillmentHeadPtr)) - - // Ensure that the order index is not out of range. - if iszero(lt(orderIndex, mload(advancedOrders))) { - throwInvalidFulfillmentComponentData() - } - - // Read advancedOrders[orderIndex] pointer from its array head. - let orderPtr := mload( - // Calculate head position of advancedOrders[orderIndex]. - add(add(advancedOrders, OneWord), mul(orderIndex, OneWord)) - ) - - // Read the pointer to OrderParameters from the AdvancedOrder. - let paramsPtr := mload(orderPtr) - - // Load the offer array pointer. - let offerArrPtr := mload( - add(paramsPtr, OrderParameters_offer_head_offset) - ) - - // Retrieve item index using an offset of the fulfillment pointer. - let itemIndex := mload( - add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset) - ) - - // Only continue if the fulfillment is not invalid. - if iszero(lt(itemIndex, mload(offerArrPtr))) { - throwInvalidFulfillmentComponentData() - } - - // Retrieve consideration item pointer using the item index. - let offerItemPtr := mload( - add( - // Get pointer to beginning of receivedItem. - add(offerArrPtr, OneWord), - // Calculate offset to pointer for desired order. - mul(itemIndex, OneWord) - ) - ) - // Declare a variable for the final aggregated item amount. - let amount := 0 - - // Create variable to track errors encountered with amount. - let errorBuffer := 0 + let amount - // Only add offer amount to execution amount on a nonzero numerator. - if mload(add(orderPtr, AdvancedOrder_numerator_offset)) { - // Retrieve amount pointer using consideration item pointer. - let amountPtr := add(offerItemPtr, Common_amount_offset) + // Declare a variable to track errors encountered with amount. + let errorBuffer - // Set the amount. - amount := mload(amountPtr) + // Declare a variable for the hash of itemType, token, identifier + let dataHash - // Zero out amount on item to indicate it is credited. - mstore(amountPtr, 0) + for { + // Create variable to track position in offerComponents head. + let fulfillmentHeadPtr := offerComponents - // Buffer indicating whether issues were found. - errorBuffer := iszero(amount) - } + // Get position one word past last element in head of array. + let endPtr := add( + offerComponents, + shl(OneWordShift, mload(offerComponents)) + ) + } lt(fulfillmentHeadPtr, endPtr) { - // Retrieve the received item pointer. - let receivedItemPtr := mload(execution) - - // Set the item type on the received item. - mstore(receivedItemPtr, mload(offerItemPtr)) - - // Set the token on the received item. - mstore( - add(receivedItemPtr, Common_token_offset), - mload(add(offerItemPtr, Common_token_offset)) - ) - - // Set the identifier on the received item. - mstore( - add(receivedItemPtr, Common_identifier_offset), - mload(add(offerItemPtr, Common_identifier_offset)) - ) - - // Set the offerer on returned execution using order pointer. - mstore(add(execution, Execution_offerer_offset), mload(paramsPtr)) - - // Set conduitKey on returned execution via offset of order pointer. - mstore( - add(execution, Execution_conduit_offset), - mload(add(paramsPtr, OrderParameters_conduit_offset)) - ) - - // Calculate the hash of (itemType, token, identifier). - let dataHash := keccak256( - receivedItemPtr, - ReceivedItem_CommonParams_size - ) - - // Get position one word past last element in head of array. - let endPtr := add( - offerComponents, - mul(mload(offerComponents), OneWord) - ) - - // Iterate over remaining offer components. - // prettier-ignore - for {} lt(fulfillmentHeadPtr, endPtr) {} { - // Increment the pointer to the fulfillment head by one word. + } { + // Increment position in considerationComponents head. fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord) - // Get the order index using the fulfillment pointer. - orderIndex := mload(mload(fulfillmentHeadPtr)) + // Retrieve the order index using the fulfillment pointer. + let orderIndex := mload(mload(fulfillmentHeadPtr)) - // Ensure the order index is in range. + // Ensure that the order index is not out of range. if iszero(lt(orderIndex, mload(advancedOrders))) { - throwInvalidFulfillmentComponentData() + throwInvalidFulfillmentComponentData() } - // Get pointer to AdvancedOrder element. - orderPtr := mload( + // Read advancedOrders[orderIndex] pointer from its array head. + let orderPtr := mload( + // Calculate head position of advancedOrders[orderIndex]. add( add(advancedOrders, OneWord), - mul(orderIndex, OneWord) + shl(OneWordShift, orderIndex) ) ) - // Only continue if numerator is not zero. - if iszero(mload( - add(orderPtr, AdvancedOrder_numerator_offset) - )) { - continue - } - // Read the pointer to OrderParameters from the AdvancedOrder. - paramsPtr := mload(orderPtr) + let paramsPtr := mload(orderPtr) - // Load offer array pointer. - offerArrPtr := mload( - add( - paramsPtr, - OrderParameters_offer_head_offset - ) + // Retrieve item index using an offset of fulfillment pointer. + let itemIndex := mload( + add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset) ) - // Get the item index using the fulfillment pointer. - itemIndex := mload(add(mload(fulfillmentHeadPtr), OneWord)) + let offerItemPtr + { + // Load the offer array pointer. + let offerArrPtr := mload( + add(paramsPtr, OrderParameters_offer_head_offset) + ) - // Throw if itemIndex is out of the range of array. - if iszero( - lt(itemIndex, mload(offerArrPtr)) - ) { - throwInvalidFulfillmentComponentData() + // If the offer item index is out of range or the numerator + // is zero, skip this item. + if or( + iszero(lt(itemIndex, mload(offerArrPtr))), + iszero( + mload(add(orderPtr, AdvancedOrder_numerator_offset)) + ) + ) { + continue + } + + // Retrieve offer item pointer using the item index. + offerItemPtr := mload( + add( + // Get pointer to beginning of receivedItem. + add(offerArrPtr, OneWord), + // Calculate offset to pointer for desired order. + shl(OneWordShift, itemIndex) + ) + ) } - // Retrieve offer item pointer using index. - offerItemPtr := mload( - add( - // Get pointer to beginning of receivedItem. - add(offerArrPtr, OneWord), - // Use offset to pointer for desired order. - mul(itemIndex, OneWord) + // Declare a separate scope for the amount update. + { + // Retrieve amount pointer using consideration item pointer. + let amountPtr := add(offerItemPtr, Common_amount_offset) + + // Add offer item amount to execution amount. + let newAmount := add(amount, mload(amountPtr)) + + // Update error buffer: + // 1 = zero amount, 2 = overflow, 3 = both. + errorBuffer := or( + errorBuffer, + or( + shl(1, lt(newAmount, amount)), + iszero(mload(amountPtr)) + ) ) - ) - // Retrieve amount pointer using offer item pointer. - let amountPtr := add( - offerItemPtr, - Common_amount_offset - ) + // Update the amount to the new, summed amount. + amount := newAmount - // Add offer amount to execution amount. - let newAmount := add(amount, mload(amountPtr)) + // Zero out amount on original item to indicate it is spent. + mstore(amountPtr, 0) + } - // Update error buffer: 1 = zero amount, 2 = overflow, 3 = both. - errorBuffer := or( - errorBuffer, - or( - shl(1, lt(newAmount, amount)), - iszero(mload(amountPtr)) - ) - ) + // Retrieve ReceivedItem pointer from Execution. + let receivedItem := mload(execution) + + // Check if this is the first valid fulfillment item + switch iszero(dataHash) + case 1 { + // On first valid item, populate the received item in + // memory for later comparison. + + // Set the item type on the received item. + mstore(receivedItem, mload(offerItemPtr)) + + // Set the token on the received item. + mstore( + add(receivedItem, Common_token_offset), + mload(add(offerItemPtr, Common_token_offset)) + ) + + // Set the identifier on the received item. + mstore( + add(receivedItem, Common_identifier_offset), + mload(add(offerItemPtr, Common_identifier_offset)) + ) + + // Set offerer on returned execution using order pointer. + mstore( + add(execution, Execution_offerer_offset), + mload(paramsPtr) + ) - // Update the amount to the new, summed amount. - amount := newAmount - - // Zero out amount on original item to indicate it is credited. - mstore(amountPtr, 0) - - // Ensure the indicated item matches original item. - if iszero( - and( - and( - // The offerer must match on both items. - eq( - mload(paramsPtr), - mload( - add(execution, Execution_offerer_offset) - ) - ), - // The conduit key must match on both items. - eq( - mload( - add( - paramsPtr, - OrderParameters_conduit_offset - ) - ), - mload( - add( - execution, - Execution_conduit_offset - ) - ) - ) + // Set execution conduitKey via order pointer offset. + mstore( + add(execution, Execution_conduit_offset), + mload(add(paramsPtr, OrderParameters_conduit_offset)) + ) + + // Calculate the hash of (itemType, token, identifier). + dataHash := keccak256( + receivedItem, + ReceivedItem_CommonParams_size + ) + + // If component index > 0, swap component pointer with + // pointer to first component so that any remainder after + // fulfillment can be added back to the first item. + let firstFulfillmentHeadPtr := add(offerComponents, OneWord) + if xor(firstFulfillmentHeadPtr, fulfillmentHeadPtr) { + let firstFulfillmentPtr := mload( + firstFulfillmentHeadPtr + ) + let fulfillmentPtr := mload(fulfillmentHeadPtr) + mstore(firstFulfillmentHeadPtr, fulfillmentPtr) + } + } + default { + // Compare every subsequent item to the first + if or( + or( + // The offerer must match on both items. + xor( + mload(paramsPtr), + mload(add(execution, Execution_offerer_offset)) + ), + // The conduit key must match on both items. + xor( + mload( + add( + paramsPtr, + OrderParameters_conduit_offset + ) + ), + mload(add(execution, Execution_conduit_offset)) + ) ), // The itemType, token, and identifier must match. - eq( + xor( dataHash, keccak256( offerItemPtr, ReceivedItem_CommonParams_size ) ) - ) - ) { - // Throw if any of the requirements are not met. - throwInvalidFulfillmentComponentData() + ) { + // Throw if any of the requirements are not met. + throwInvalidFulfillmentComponentData() + } } } + // Write final amount to execution. mstore(add(mload(execution), Common_amount_offset), amount) @@ -481,17 +466,45 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { if errorBuffer { // If errorBuffer is 1, an item had an amount of zero. if eq(errorBuffer, 1) { - // Store the MissingItemAmount error signature. - mstore(0, MissingItemAmount_error_signature) - - // Return, supplying MissingItemAmount signature. - revert(0, MissingItemAmount_error_len) + // Store left-padded selector with push4 (reduces bytecode) + // mem[28:32] = selector + mstore(0, MissingItemAmount_error_selector) + + // revert(abi.encodeWithSignature("MissingItemAmount()")) + revert( + Error_selector_offset, + MissingItemAmount_error_length + ) } // If errorBuffer is not 1 or 0, the sum overflowed. // Panic! throwOverflow() } + + // Declare function for reverts on invalid fulfillment data. + function throwInvalidFulfillmentComponentData() { + // Store left-padded selector (uses push4 and reduces code size) + mstore(0, InvalidFulfillmentComponentData_error_selector) + + // revert(abi.encodeWithSignature( + // "InvalidFulfillmentComponentData()" + // )) + revert( + Error_selector_offset, + InvalidFulfillmentComponentData_error_length + ) + } + + // Declare function for reverts due to arithmetic overflows. + function throwOverflow() { + // Store the Panic error signature. + mstore(0, Panic_error_selector) + // Store the arithmetic (0x11) panic code. + mstore(Panic_error_code_ptr, Panic_arithmetic) + // revert(abi.encodeWithSignature("Panic(uint256)", 0x11)) + revert(Error_selector_offset, Panic_error_length) + } } } @@ -499,6 +512,12 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { * @dev Internal pure function to aggregate a group of consideration items * using supplied directives on which component items are candidates * for aggregation, skipping items on orders that are not available. + * Note that this function depends on memory layout affected by an + * earlier call to _validateOrdersAndPrepareToFulfill. The memory for + * the consideration arrays needs to be updated before calling + * _aggregateValidFulfillmentConsiderationItems. + * _validateOrdersAndPrepareToFulfill is called in _matchAdvancedOrders + * and _fulfillAvailableAdvancedOrders in the current version. * * @param advancedOrders The orders to aggregate consideration * items from. @@ -506,7 +525,7 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { * indicating the order index and item index * of each candidate consideration item for * aggregation. - * @param execution The execution to apply the aggregation to. + * @param execution The execution to apply the aggregation to. */ function _aggregateValidFulfillmentConsiderationItems( AdvancedOrder[] memory advancedOrders, @@ -515,248 +534,195 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { ) internal pure { // Utilize assembly in order to efficiently aggregate the items. assembly { - // Declare function for reverts on invalid fulfillment data. - function throwInvalidFulfillmentComponentData() { - // Store the InvalidFulfillmentComponentData error signature. - mstore(0, InvalidFulfillmentComponentData_error_signature) - - // Return, supplying InvalidFulfillmentComponentData signature. - revert(0, InvalidFulfillmentComponentData_error_len) - } - - // Declare function for reverts due to arithmetic overflows. - function throwOverflow() { - // Store the Panic error signature. - mstore(0, Panic_error_signature) - - // Store the arithmetic (0x11) panic code as initial argument. - mstore(Panic_error_offset, Panic_arithmetic) - - // Return, supplying Panic signature and arithmetic code. - revert(0, Panic_error_length) - } - - // Get position in considerationComponents head. - let fulfillmentHeadPtr := add(considerationComponents, OneWord) - - // Retrieve the order index using the fulfillment pointer. - let orderIndex := mload(mload(fulfillmentHeadPtr)) - - // Ensure that the order index is not out of range. - if iszero(lt(orderIndex, mload(advancedOrders))) { - throwInvalidFulfillmentComponentData() - } - - // Read advancedOrders[orderIndex] pointer from its array head. - let orderPtr := mload( - // Calculate head position of advancedOrders[orderIndex]. - add(add(advancedOrders, OneWord), mul(orderIndex, OneWord)) - ) - - // Load consideration array pointer. - let considerationArrPtr := mload( - add( - // Read pointer to OrderParameters from the AdvancedOrder. - mload(orderPtr), - OrderParameters_consideration_head_offset - ) - ) - - // Retrieve item index using an offset of the fulfillment pointer. - let itemIndex := mload( - add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset) - ) - - // Ensure that the order index is not out of range. - if iszero(lt(itemIndex, mload(considerationArrPtr))) { - throwInvalidFulfillmentComponentData() - } - - // Retrieve consideration item pointer using the item index. - let considerationItemPtr := mload( - add( - // Get pointer to beginning of receivedItem. - add(considerationArrPtr, OneWord), - // Calculate offset to pointer for desired order. - mul(itemIndex, OneWord) - ) - ) - // Declare a variable for the final aggregated item amount. - let amount := 0 + let amount // Create variable to track errors encountered with amount. - let errorBuffer := 0 + let errorBuffer - // Only add consideration amount to execution amount if numerator is - // greater than zero. - if mload(add(orderPtr, AdvancedOrder_numerator_offset)) { - // Retrieve amount pointer using consideration item pointer. - let amountPtr := add(considerationItemPtr, Common_amount_offset) + // Declare variable for hash(itemType, token, identifier, recipient) + let dataHash - // Set the amount. - amount := mload(amountPtr) - - // Set error bit if amount is zero. - errorBuffer := iszero(amount) - - // Zero out amount on item to indicate it is credited. - mstore(amountPtr, 0) - } + for { + // Track position in considerationComponents head. + let fulfillmentHeadPtr := considerationComponents - // Retrieve ReceivedItem pointer from Execution. - let receivedItem := mload(execution) - - // Set the item type on the received item. - mstore(receivedItem, mload(considerationItemPtr)) - - // Set the token on the received item. - mstore( - add(receivedItem, Common_token_offset), - mload(add(considerationItemPtr, Common_token_offset)) - ) - - // Set the identifier on the received item. - mstore( - add(receivedItem, Common_identifier_offset), - mload(add(considerationItemPtr, Common_identifier_offset)) - ) - - // Set the recipient on the received item. - mstore( - add(receivedItem, ReceivedItem_recipient_offset), - mload( - add( - considerationItemPtr, - ConsiderationItem_recipient_offset - ) + // Get position one word past last element in head of array. + let endPtr := add( + considerationComponents, + shl(OneWordShift, mload(considerationComponents)) ) - ) - - // Calculate the hash of (itemType, token, identifier). - let dataHash := keccak256( - receivedItem, - ReceivedItem_CommonParams_size - ) - - // Get position one word past last element in head of array. - let endPtr := add( - considerationComponents, - mul(mload(considerationComponents), OneWord) - ) - - // Iterate over remaining offer components. - // prettier-ignore - for {} lt(fulfillmentHeadPtr, endPtr) {} { + } lt(fulfillmentHeadPtr, endPtr) { + + } { // Increment position in considerationComponents head. fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord) - // Get the order index using the fulfillment pointer. - orderIndex := mload(mload(fulfillmentHeadPtr)) + // Retrieve the order index using the fulfillment pointer. + let orderIndex := mload(mload(fulfillmentHeadPtr)) - // Ensure the order index is in range. + // Ensure that the order index is not out of range. if iszero(lt(orderIndex, mload(advancedOrders))) { - throwInvalidFulfillmentComponentData() + throwInvalidFulfillmentComponentData() } - // Get pointer to AdvancedOrder element. - orderPtr := mload( + // Read advancedOrders[orderIndex] pointer from its array head. + let orderPtr := mload( + // Calculate head position of advancedOrders[orderIndex]. add( add(advancedOrders, OneWord), - mul(orderIndex, OneWord) + shl(OneWordShift, orderIndex) ) ) - // Only continue if numerator is not zero. - if iszero( - mload(add(orderPtr, AdvancedOrder_numerator_offset)) - ) { - continue + // Retrieve item index using an offset of fulfillment pointer. + let itemIndex := mload( + add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset) + ) + + let considerationItemPtr + { + // Load consideration array pointer. + let considerationArrPtr := mload( + add( + // Read OrderParameters pointer from AdvancedOrder. + mload(orderPtr), + OrderParameters_consideration_head_offset + ) + ) + + // If the consideration item index is out of range or the + // numerator is zero, skip this item. + if or( + iszero(lt(itemIndex, mload(considerationArrPtr))), + iszero( + mload(add(orderPtr, AdvancedOrder_numerator_offset)) + ) + ) { + continue + } + + // Retrieve consideration item pointer using the item index. + considerationItemPtr := mload( + add( + // Get pointer to beginning of receivedItem. + add(considerationArrPtr, OneWord), + // Calculate offset to pointer for desired order. + shl(OneWordShift, itemIndex) + ) + ) } - // Load consideration array pointer from OrderParameters. - considerationArrPtr := mload( - add( - // Get pointer to OrderParameters from AdvancedOrder. - mload(orderPtr), - OrderParameters_consideration_head_offset + // Declare a separate scope for the amount update + { + // Retrieve amount pointer using consideration item pointer. + let amountPtr := add( + considerationItemPtr, + Common_amount_offset ) - ) - // Get the item index using the fulfillment pointer. - itemIndex := mload(add(mload(fulfillmentHeadPtr), OneWord)) + // Add consideration item amount to execution amount. + let newAmount := add(amount, mload(amountPtr)) - // Check if itemIndex is within the range of array. - if iszero(lt(itemIndex, mload(considerationArrPtr))) { - throwInvalidFulfillmentComponentData() + // Update error buffer: + // 1 = zero amount, 2 = overflow, 3 = both. + errorBuffer := or( + errorBuffer, + or( + shl(1, lt(newAmount, amount)), + iszero(mload(amountPtr)) + ) + ) + + // Update the amount to the new, summed amount. + amount := newAmount + + // Zero out original item amount to indicate it is credited. + mstore(amountPtr, 0) } - // Retrieve consideration item pointer using index. - considerationItemPtr := mload( - add( - // Get pointer to beginning of receivedItem. - add(considerationArrPtr, OneWord), - // Use offset to pointer for desired order. - mul(itemIndex, OneWord) - ) - ) + // Retrieve ReceivedItem pointer from Execution. + let receivedItem := mload(execution) - // Retrieve amount pointer using consideration item pointer. - let amountPtr := add( - considerationItemPtr, - Common_amount_offset - ) + switch iszero(dataHash) + case 1 { + // On first valid item, populate the received item in + // memory for later comparison. - // Add offer amount to execution amount. - let newAmount := add(amount, mload(amountPtr)) + // Set the item type on the received item. + mstore(receivedItem, mload(considerationItemPtr)) - // Update error buffer: 1 = zero amount, 2 = overflow, 3 = both. - errorBuffer := or( - errorBuffer, - or( - shl(1, lt(newAmount, amount)), - iszero(mload(amountPtr)) - ) - ) + // Set the token on the received item. + mstore( + add(receivedItem, Common_token_offset), + mload(add(considerationItemPtr, Common_token_offset)) + ) - // Update the amount to the new, summed amount. - amount := newAmount - - // Zero out amount on original item to indicate it is credited. - mstore(amountPtr, 0) - - // Ensure the indicated item matches original item. - if iszero( - and( - // Item recipients must match. - eq( - mload( - add( - considerationItemPtr, - ConsiderItem_recipient_offset - ) - ), - mload( - add( - receivedItem, - ReceivedItem_recipient_offset - ) + // Set the identifier on the received item. + mstore( + add(receivedItem, Common_identifier_offset), + mload( + add(considerationItemPtr, Common_identifier_offset) + ) + ) + + // Set the recipient on the received item. + // Note that this depends on the memory layout affected by + // _validateOrdersAndPrepareToFulfill. + mstore( + add(receivedItem, ReceivedItem_recipient_offset), + mload( + add( + considerationItemPtr, + ReceivedItem_recipient_offset ) - ), - // The itemType, token, identifier must match. - eq( - dataHash, - keccak256( - considerationItemPtr, - ReceivedItem_CommonParams_size - ) ) ) - ) { - // Throw if any of the requirements are not met. - throwInvalidFulfillmentComponentData() + + // Calculate the hash of (itemType, token, identifier, + // recipient). This is run after amount is set to zero, so + // there will be one blank word after identifier included in + // the hash buffer. + dataHash := keccak256( + considerationItemPtr, + ReceivedItem_size + ) + + // If component index > 0, swap component pointer with + // pointer to first component so that any remainder after + // fulfillment can be added back to the first item. + let firstFulfillmentHeadPtr := add( + considerationComponents, + OneWord + ) + if xor(firstFulfillmentHeadPtr, fulfillmentHeadPtr) { + let firstFulfillmentPtr := mload( + firstFulfillmentHeadPtr + ) + let fulfillmentPtr := mload(fulfillmentHeadPtr) + mstore(firstFulfillmentHeadPtr, fulfillmentPtr) + } + } + default { + // Compare every subsequent item to the first + // The itemType, token, identifier and recipient must match. + if xor( + dataHash, + // Calculate the hash of (itemType, token, identifier, + // recipient). This is run after amount is set to zero, + // so there will be one blank word after identifier + // included in the hash buffer. + keccak256(considerationItemPtr, ReceivedItem_size) + ) { + // Throw if any of the requirements are not met. + throwInvalidFulfillmentComponentData() + } } } + + // Retrieve ReceivedItem pointer from Execution. + let receivedItem := mload(execution) + // Write final amount to execution. mstore(add(receivedItem, Common_amount_offset), amount) @@ -764,17 +730,44 @@ contract FulfillmentApplier is FulfillmentApplicationErrors { if errorBuffer { // If errorBuffer is 1, an item had an amount of zero. if eq(errorBuffer, 1) { - // Store the MissingItemAmount error signature. - mstore(0, MissingItemAmount_error_signature) + // Store left-padded selector with push4, mem[28:32] + mstore(0, MissingItemAmount_error_selector) - // Return, supplying MissingItemAmount signature. - revert(0, MissingItemAmount_error_len) + // revert(abi.encodeWithSignature("MissingItemAmount()")) + revert( + Error_selector_offset, + MissingItemAmount_error_length + ) } - // If errorBuffer is not 1 or 0, the sum overflowed. + // If errorBuffer is not 1 or 0, `amount` overflowed. // Panic! throwOverflow() } + + // Declare function for reverts on invalid fulfillment data. + function throwInvalidFulfillmentComponentData() { + // Store the InvalidFulfillmentComponentData error signature. + mstore(0, InvalidFulfillmentComponentData_error_selector) + + // revert(abi.encodeWithSignature( + // "InvalidFulfillmentComponentData()" + // )) + revert( + Error_selector_offset, + InvalidFulfillmentComponentData_error_length + ) + } + + // Declare function for reverts due to arithmetic overflows. + function throwOverflow() { + // Store the Panic error signature. + mstore(0, Panic_error_selector) + // Store the arithmetic (0x11) panic code. + mstore(Panic_error_code_ptr, Panic_arithmetic) + // revert(abi.encodeWithSignature("Panic(uint256)", 0x11)) + revert(Error_selector_offset, Panic_error_length) + } } } } diff --git a/contracts/lib/GettersAndDerivers.sol b/contracts/lib/GettersAndDerivers.sol index 4b0d16357..46ff51f3a 100644 --- a/contracts/lib/GettersAndDerivers.sol +++ b/contracts/lib/GettersAndDerivers.sol @@ -1,11 +1,37 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { OrderParameters } from "./ConsiderationStructs.sol"; import { ConsiderationBase } from "./ConsiderationBase.sol"; -import "./ConsiderationConstants.sol"; +import { + Create2AddressDerivation_length, + Create2AddressDerivation_ptr, + EIP_712_PREFIX, + EIP712_ConsiderationItem_size, + EIP712_DigestPayload_size, + EIP712_DomainSeparator_offset, + EIP712_OfferItem_size, + EIP712_Order_size, + EIP712_OrderHash_offset, + FreeMemoryPointerSlot, + information_conduitController_offset, + information_domainSeparator_offset, + information_length, + information_version_cd_offset, + information_version_offset, + information_versionLengthPtr, + information_versionWithLength, + MaskOverByteTwelve, + MaskOverLastTwentyBytes, + OneWord, + OneWordShift, + OrderParameters_consideration_head_offset, + OrderParameters_counter_offset, + OrderParameters_offer_head_offset, + TwoWords +} from "./ConsiderationConstants.sol"; /** * @title GettersAndDerivers @@ -22,9 +48,9 @@ contract GettersAndDerivers is ConsiderationBase { * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ConsiderationBase(conduitController) - {} + constructor( + address conduitController + ) ConsiderationBase(conduitController) {} /** * @dev Internal view function to derive the order hash for a given order. @@ -33,7 +59,7 @@ contract GettersAndDerivers is ConsiderationBase { * caller. * * @param orderParameters The parameters of the order to hash. - * @param counter The counter of the order to hash. + * @param counter The counter of the order to hash. * * @return orderHash The hash. */ @@ -76,7 +102,6 @@ contract GettersAndDerivers is ConsiderationBase { offerArrPtr := add(offerArrPtr, OneWord) // Iterate over the offer items. - // prettier-ignore for { let i := 0 } lt(i, offerLength) { i := add(i, 1) } { @@ -104,7 +129,7 @@ contract GettersAndDerivers is ConsiderationBase { // Derive the offer hash using the hashes of each item. offerHash := keccak256( mload(FreeMemoryPointerSlot), - mul(offerLength, OneWord) + shl(OneWordShift, offerLength) ) } @@ -131,7 +156,6 @@ contract GettersAndDerivers is ConsiderationBase { ) // Iterate over the consideration items (not including tips). - // prettier-ignore for { let i := 0 } lt(i, originalConsiderationLength) { i := add(i, 1) } { @@ -162,7 +186,7 @@ contract GettersAndDerivers is ConsiderationBase { // Derive the consideration hash using the hashes of each item. considerationHash := keccak256( mload(FreeMemoryPointerSlot), - mul(originalConsiderationLength, OneWord) + shl(OneWordShift, originalConsiderationLength) ) } @@ -242,11 +266,9 @@ contract GettersAndDerivers is ConsiderationBase { * @return conduit The address of the conduit associated with the given * conduit key. */ - function _deriveConduit(bytes32 conduitKey) - internal - view - returns (address conduit) - { + function _deriveConduit( + bytes32 conduitKey + ) internal view returns (address conduit) { // Read conduit controller address from runtime and place on the stack. address conduitController = address(_CONDUIT_CONTROLLER); @@ -295,7 +317,6 @@ contract GettersAndDerivers is ConsiderationBase { * @return The domain separator. */ function _domainSeparator() internal view returns (bytes32) { - // prettier-ignore return block.chainid == _CHAIN_ID ? _DOMAIN_SEPARATOR : _deriveDomainSeparator(); @@ -305,31 +326,32 @@ contract GettersAndDerivers is ConsiderationBase { * @dev Internal view function to retrieve configuration information for * this contract. * - * @return version The contract version. - * @return domainSeparator The domain separator for this contract. - * @return conduitController The conduit Controller set for this contract. + * @return The contract version. + * @return The domain separator for this contract. + * @return The conduit Controller set for this contract. */ function _information() internal view returns ( - string memory version, - bytes32 domainSeparator, - address conduitController + string memory /* version */, + bytes32 /* domainSeparator */, + address /* conduitController */ ) { // Derive the domain separator. - domainSeparator = _domainSeparator(); + bytes32 domainSeparator = _domainSeparator(); // Declare variable as immutables cannot be accessed within assembly. - conduitController = address(_CONDUIT_CONTROLLER); - - // Allocate a string with the intended length. - version = new string(Version_length); + address conduitController = address(_CONDUIT_CONTROLLER); - // Set the version as data on the newly allocated string. + // Return the version, domain separator, and conduit controller. assembly { - mstore(add(version, OneWord), shl(Version_shift, Version)) + mstore(information_version_offset, information_version_cd_offset) + mstore(information_domainSeparator_offset, domainSeparator) + mstore(information_conduitController_offset, conduitController) + mstore(information_versionLengthPtr, information_versionWithLength) + return(information_version_offset, information_length) } } @@ -342,11 +364,10 @@ contract GettersAndDerivers is ConsiderationBase { * * @return value The hash. */ - function _deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) - internal - pure - returns (bytes32 value) - { + function _deriveEIP712Digest( + bytes32 domainSeparator, + bytes32 orderHash + ) internal pure returns (bytes32 value) { // Leverage scratch space to perform an efficient hash. assembly { // Place the EIP-712 prefix at the start of scratch space. diff --git a/contracts/lib/LowLevelHelpers.sol b/contracts/lib/LowLevelHelpers.sol index c3bba2398..199c44784 100644 --- a/contracts/lib/LowLevelHelpers.sol +++ b/contracts/lib/LowLevelHelpers.sol @@ -1,7 +1,15 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -import "./ConsiderationConstants.sol"; +import { + CostPerWord, + ExtraGasBuffer, + FreeMemoryPointerSlot, + MemoryExpansionCoefficientShift, + OneWord, + OneWordShift, + ThirtyOneBytes +} from "./ConsiderationConstants.sol"; /** * @title LowLevelHelpers @@ -10,34 +18,6 @@ import "./ConsiderationConstants.sol"; * operations. */ contract LowLevelHelpers { - /** - * @dev Internal view function to staticcall an arbitrary target with given - * calldata. Note that no data is written to memory and no contract - * size check is performed. - * - * @param target The account to staticcall. - * @param callData The calldata to supply when staticcalling the target. - * - * @return success The status of the staticcall to the target. - */ - function _staticcall(address target, bytes memory callData) - internal - view - returns (bool success) - { - assembly { - // Perform the staticcall. - success := staticcall( - gas(), - target, - add(callData, OneWord), - mload(callData), - 0, - 0 - ) - } - } - /** * @dev Internal view function to revert and pass along the revert reason if * data was returned by the last call and that the size of that data @@ -51,15 +31,18 @@ contract LowLevelHelpers { // Ensure that sufficient gas is available to copy returndata // while expanding memory where necessary. Start by computing // the word size of returndata and allocated memory. - let returnDataWords := div( - add(returndatasize(), AlmostOneWord), - OneWord + let returnDataWords := shr( + OneWordShift, + add(returndatasize(), ThirtyOneBytes) ) // Note: use the free memory pointer in place of msize() to work // around a Yul warning that prevents accessing msize directly // when the IR pipeline is activated. - let msizeWords := div(mload(FreeMemoryPointerSlot), OneWord) + let msizeWords := shr( + OneWordShift, + mload(FreeMemoryPointerSlot) + ) // Next, compute the cost of the returndatacopy. let cost := mul(CostPerWord, returnDataWords) @@ -70,12 +53,12 @@ contract LowLevelHelpers { cost, add( mul(sub(returnDataWords, msizeWords), CostPerWord), - div( + shr( + MemoryExpansionCoefficientShift, sub( mul(returnDataWords, returnDataWords), mul(msizeWords, msizeWords) - ), - MemoryExpansionCoefficient + ) ) ) ) @@ -95,31 +78,54 @@ contract LowLevelHelpers { } /** - * @dev Internal pure function to determine if the first word of returndata - * matches an expected magic value. + * @dev Internal view function to branchlessly select either the caller (if + * a supplied recipient is equal to zero) or the supplied recipient (if + * that recipient is a nonzero value). * - * @param expected The expected magic value. + * @param recipient The supplied recipient. * - * @return A boolean indicating whether the expected value matches the one - * located in the first word of returndata. + * @return updatedRecipient The updated recipient. */ - function _doesNotMatchMagic(bytes4 expected) internal pure returns (bool) { - // Declare a variable for the value held by the return data buffer. - bytes4 result; - - // Utilize assembly in order to read directly from returndata buffer. + function _substituteCallerForEmptyRecipient( + address recipient + ) internal view returns (address updatedRecipient) { + // Utilize assembly to perform a branchless operation on the recipient. assembly { - // Only put result on stack if return data is exactly one word. - if eq(returndatasize(), OneWord) { - // Copy the word directly from return data into scratch space. - returndatacopy(0, 0, OneWord) + // Add caller to recipient if recipient equals 0; otherwise add 0. + updatedRecipient := add(recipient, mul(iszero(recipient), caller())) + } + } - // Take value from scratch space and place it on the stack. - result := mload(0) - } + /** + * @dev Internal pure function to cast a `bool` value to a `uint256` value. + * + * @param b The `bool` value to cast. + * + * @return u The `uint256` value. + */ + function _cast(bool b) internal pure returns (uint256 u) { + assembly { + u := b } + } - // Return a boolean indicating whether expected and located value match. - return result != expected; + /** + * @dev Internal pure function to compare two addresses without first + * masking them. Note that dirty upper bits will cause otherwise equal + * addresses to be recognized as unequal. + * + * @param a The first address. + * @param b The second address + * + * @return areEqual A boolean representing whether the addresses are equal. + */ + function _unmaskedAddressComparison( + address a, + address b + ) internal pure returns (bool areEqual) { + // Utilize assembly to perform the comparison without masking. + assembly { + areEqual := eq(a, b) + } } } diff --git a/contracts/lib/OrderCombiner.sol b/contracts/lib/OrderCombiner.sol index 7de31ccef..fc47d4b07 100644 --- a/contracts/lib/OrderCombiner.sol +++ b/contracts/lib/OrderCombiner.sol @@ -1,27 +1,43 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -import { Side, ItemType } from "./ConsiderationEnums.sol"; +import { Side, ItemType, OrderType } from "./ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, + AdvancedOrder, ConsiderationItem, - ReceivedItem, - OrderParameters, + CriteriaResolver, + Execution, Fulfillment, FulfillmentComponent, - Execution, - Order, - AdvancedOrder, - CriteriaResolver + OfferItem, + OrderParameters, + ReceivedItem } from "./ConsiderationStructs.sol"; import { OrderFulfiller } from "./OrderFulfiller.sol"; import { FulfillmentApplier } from "./FulfillmentApplier.sol"; -import "./ConsiderationConstants.sol"; +import { + _revertConsiderationNotMet, + _revertInsufficientNativeTokensSupplied, + _revertInvalidNativeOfferItem, + _revertNoSpecifiedOrdersAvailable +} from "./ConsiderationErrors.sol"; + +import { + AccumulatorDisarmed, + ConsiderationItem_recipient_offset, + NonMatchSelector_InvalidErrorValue, + NonMatchSelector_MagicMask, + OneWord, + OneWordShift, + OrdersMatchedTopic0, + ReceivedItem_amount_offset, + ReceivedItem_recipient_offset, + TwoWords +} from "./ConsiderationConstants.sol"; /** * @title OrderCombiner @@ -107,17 +123,20 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { function _fulfillAvailableAdvancedOrders( AdvancedOrder[] memory advancedOrders, CriteriaResolver[] memory criteriaResolvers, - FulfillmentComponent[][] calldata offerFulfillments, - FulfillmentComponent[][] calldata considerationFulfillments, + FulfillmentComponent[][] memory offerFulfillments, + FulfillmentComponent[][] memory considerationFulfillments, bytes32 fulfillerConduitKey, address recipient, uint256 maximumFulfilled ) internal - returns (bool[] memory availableOrders, Execution[] memory executions) + returns ( + bool[] memory /* availableOrders */, + Execution[] memory /* executions */ + ) { // Validate orders, apply amounts, & determine if they utilize conduits. - _validateOrdersAndPrepareToFulfill( + bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill( advancedOrders, criteriaResolvers, false, // Signifies that invalid orders should NOT revert. @@ -126,22 +145,24 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { ); // Aggregate used offer and consideration items and execute transfers. - (availableOrders, executions) = _executeAvailableFulfillments( - advancedOrders, - offerFulfillments, - considerationFulfillments, - fulfillerConduitKey, - recipient - ); - - // Return order fulfillment details and executions. - return (availableOrders, executions); + return + _executeAvailableFulfillments( + advancedOrders, + offerFulfillments, + considerationFulfillments, + fulfillerConduitKey, + recipient, + orderHashes + ); } /** * @dev Internal function to validate a group of orders, update their * statuses, reduce amounts by their previously filled fractions, apply - * criteria resolvers, and emit OrderFulfilled events. + * criteria resolvers, and emit OrderFulfilled events. Note that this + * function needs to be called before + * _aggregateValidFulfillmentConsiderationItems to set the memory + * layout that _aggregateValidFulfillmentConsiderationItems depends on. * * @param advancedOrders The advanced orders to validate and reduce by * their previously filled amounts. @@ -157,7 +178,11 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { * order being invalid; setting this to false will * instead cause the invalid order to be skipped. * @param maximumFulfilled The maximum number of orders to fulfill. - * @param recipient The intended recipient for all received items. + * @param recipient The intended recipient for all items that do not + * already have a designated recipient and are not + * already used as part of a provided fulfillment. + * + * @return orderHashes The hashes of the orders being fulfilled. */ function _validateOrdersAndPrepareToFulfill( AdvancedOrder[] memory advancedOrders, @@ -165,64 +190,69 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { bool revertOnInvalid, uint256 maximumFulfilled, address recipient - ) internal { + ) internal returns (bytes32[] memory orderHashes) { // Ensure this function cannot be triggered during a reentrant call. - _setReentrancyGuard(); - - // Read length of orders array and place on the stack. - uint256 totalOrders = advancedOrders.length; - - // Track the order hash for each order being fulfilled. - bytes32[] memory orderHashes = new bytes32[](totalOrders); - - // Override orderHashes length to zero after memory has been allocated. - assembly { - mstore(orderHashes, 0) - } + _setReentrancyGuard(true); // Native tokens accepted during execution. // Declare an error buffer indicating status of any native offer items. - // {00} == 0 => In a match function, no native offer items: allow. - // {01} == 1 => In a match function, some native offer items: allow. - // {10} == 2 => Not in a match function, no native offer items: allow. - // {11} == 3 => Not in a match function, some native offer items: THROW. + // Native tokens may only be provided as part of contract orders or when + // fulfilling via matchOrders or matchAdvancedOrders; if bits indicating + // these conditions are not met have been set, throw. uint256 invalidNativeOfferItemErrorBuffer; // Use assembly to set the value for the second bit of the error buffer. assembly { - // Use the second bit of the error buffer to indicate whether the - // current function is not matchAdvancedOrders or matchOrders. - invalidNativeOfferItemErrorBuffer := shl( - 1, - gt( - // Take the remainder of the selector modulo a magic value. - mod( - shr(NumBitsAfterSelector, calldataload(0)), - NonMatchSelector_MagicModulus - ), - // Check if remainder is higher than the greatest remainder - // of the two match selectors modulo the magic value. - NonMatchSelector_MagicRemainder - ) + /** + * Use the 231st bit of the error buffer to indicate whether the + * current function is not matchAdvancedOrders or matchOrders. + * + * sig func + * ----------------------------------------------------------------- + * 1010100000010111010001000 0 000100 matchOrders + * 1111001011010001001010110 0 010010 matchAdvancedOrders + * 1110110110011000101001010 1 110100 fulfillAvailableOrders + * 1000011100100000000110110 1 000001 fulfillAvailableAdvancedOrders + * ^ 7th bit + */ + invalidNativeOfferItemErrorBuffer := and( + NonMatchSelector_MagicMask, + calldataload(0) ) } + // Declare variables for later use. + AdvancedOrder memory advancedOrder; + uint256 terminalMemoryOffset; + + unchecked { + // Read length of orders array and place on the stack. + uint256 totalOrders = advancedOrders.length; + + // Track the order hash for each order being fulfilled. + orderHashes = new bytes32[](totalOrders); + + // Determine the memory offset to terminate on during loops. + terminalMemoryOffset = (totalOrders + 1) << OneWordShift; + } + // Skip overflow checks as all for loops are indexed starting at zero. unchecked { + // Declare inner variables. + OfferItem[] memory offer; + ConsiderationItem[] memory consideration; + // Iterate over each order. - for (uint256 i = 0; i < totalOrders; ++i) { - // Retrieve the current order. - AdvancedOrder memory advancedOrder = advancedOrders[i]; + for (uint256 i = OneWord; i < terminalMemoryOffset; i += OneWord) { + // Retrieve order using assembly to bypass out-of-range check. + assembly { + advancedOrder := mload(add(advancedOrders, i)) + } // Determine if max number orders have already been fulfilled. if (maximumFulfilled == 0) { // Mark fill fraction as zero as the order will not be used. advancedOrder.numerator = 0; - // Update the length of the orderHashes array. - assembly { - mstore(orderHashes, add(i, 1)) - } - // Continue iterating through the remaining orders. continue; } @@ -234,16 +264,9 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { uint256 denominator ) = _validateOrderAndUpdateStatus( advancedOrder, - criteriaResolvers, - revertOnInvalid, - orderHashes + revertOnInvalid ); - // Update the length of the orderHashes array. - assembly { - mstore(orderHashes, add(i, 1)) - } - // Do not track hash or adjust prices if order is not fulfilled. if (numerator == 0) { // Mark fill fraction as zero if the order is not fulfilled. @@ -254,12 +277,14 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { } // Otherwise, track the order hash in question. - orderHashes[i] = orderHash; + assembly { + mstore(add(orderHashes, i), orderHash) + } // Decrement the number of fulfilled orders. // Skip underflow check as the condition before // implies that maximumFulfilled > 0. - maximumFulfilled--; + --maximumFulfilled; // Place the start time for the order on the stack. uint256 startTime = advancedOrder.parameters.startTime; @@ -268,23 +293,40 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { uint256 endTime = advancedOrder.parameters.endTime; // Retrieve array of offer items for the order in question. - OfferItem[] memory offer = advancedOrder.parameters.offer; + offer = advancedOrder.parameters.offer; // Read length of offer array and place on the stack. uint256 totalOfferItems = offer.length; + { + // Create a variable indicating if the order is not a + // contract order. Cache in scratch space to avoid stack + // depth errors. + OrderType orderType = advancedOrder.parameters.orderType; + assembly { + // Note that this check requires that there are no order + // types beyond the current set (0-4). It will need to + // be modified if more order types are added. + let isNonContract := lt(orderType, 4) + mstore(0, isNonContract) + } + } + // Iterate over each offer item on the order. for (uint256 j = 0; j < totalOfferItems; ++j) { // Retrieve the offer item. OfferItem memory offerItem = offer[j]; - assembly { - // If the offer item is for the native token, set the - // first bit of the error buffer to true. - invalidNativeOfferItemErrorBuffer := or( - invalidNativeOfferItemErrorBuffer, - iszero(mload(offerItem)) - ) + { + assembly { + // If the offer item is for the native token and the + // order type is not a contract order type, set the + // first bit of the error buffer to true. + invalidNativeOfferItemErrorBuffer := or( + invalidNativeOfferItemErrorBuffer, + lt(mload(offerItem), mload(0)) + ) + } } // Apply order fill fraction to offer item end amount. @@ -307,23 +349,23 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { ); } - // Update end amount in memory to match the derived amount. - offerItem.endAmount = endAmount; - // Adjust offer amount using current time; round down. - offerItem.startAmount = _locateCurrentAmount( + uint256 currentAmount = _locateCurrentAmount( offerItem.startAmount, - offerItem.endAmount, + endAmount, startTime, endTime, false // round down ); + + // Update amounts in memory to match the current amount. + // Note that the end amount is used to track spent amounts. + offerItem.startAmount = currentAmount; + offerItem.endAmount = currentAmount; } // Retrieve array of consideration items for order in question. - ConsiderationItem[] memory consideration = ( - advancedOrder.parameters.consideration - ); + consideration = (advancedOrder.parameters.consideration); // Read length of consideration array and place on the stack. uint256 totalConsiderationItems = consideration.length; @@ -358,22 +400,32 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { ); } - // Update end amount in memory to match the derived amount. - considerationItem.endAmount = endAmount; - // Adjust consideration amount using current time; round up. - considerationItem.startAmount = ( + uint256 currentAmount = ( _locateCurrentAmount( considerationItem.startAmount, - considerationItem.endAmount, + endAmount, startTime, endTime, true // round up ) ); - // Utilize assembly to manually "shift" the recipient value. + considerationItem.startAmount = currentAmount; + + // Utilize assembly to manually "shift" the recipient value, + // then to copy the start amount to the recipient. + // Note that this sets up the memory layout that is + // subsequently relied upon by + // _aggregateValidFulfillmentConsiderationItems. assembly { + // Derive the pointer to the recipient using the item + // pointer along with the offset to the recipient. + let considerationItemRecipientPtr := add( + considerationItem, + ConsiderationItem_recipient_offset // recipient + ) + // Write recipient to endAmount, as endAmount is not // used from this point on and can be repurposed to fit // the layout of a ReceivedItem. @@ -382,24 +434,28 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { considerationItem, ReceivedItem_recipient_offset // old endAmount ), - mload( - add( - considerationItem, - ConsiderationItem_recipient_offset - ) - ) + mload(considerationItemRecipientPtr) ) + + // Write startAmount to recipient, as recipient is not + // used from this point on and can be repurposed to + // track received amounts. + mstore(considerationItemRecipientPtr, currentAmount) } } } } - // If the first bit is set, a native offer item was encountered. If the - // second bit is set in the error buffer, the current function is not - // matchOrders or matchAdvancedOrders. If the value is three, both the - // first and second bits were set; in that case, revert with an error. - if (invalidNativeOfferItemErrorBuffer == 3) { - revert InvalidNativeOfferItem(); + // If the first bit is set, a native offer item was encountered on an + // order that is not a contract order. If the 231st bit is set in the + // error buffer, the current function is not matchOrders or + // matchAdvancedOrders. If the value is 1 + (1 << 230), then both the + // 1st and 231st bits were set; in that case, revert with an error. + if ( + invalidNativeOfferItemErrorBuffer == + NonMatchSelector_InvalidErrorValue + ) { + _revertInvalidNativeOfferItem(); } // Apply criteria resolvers to each order as applicable. @@ -408,21 +464,32 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { // Emit an event for each order signifying that it has been fulfilled. // Skip overflow checks as all for loops are indexed starting at zero. unchecked { + bytes32 orderHash; + // Iterate over each order. - for (uint256 i = 0; i < totalOrders; ++i) { + for (uint256 i = OneWord; i < terminalMemoryOffset; i += OneWord) { + assembly { + orderHash := mload(add(orderHashes, i)) + } + // Do not emit an event if no order hash is present. - if (orderHashes[i] == bytes32(0)) { + if (orderHash == bytes32(0)) { continue; } + // Retrieve order using assembly to bypass out-of-range check. + assembly { + advancedOrder := mload(add(advancedOrders, i)) + } + // Retrieve parameters for the order in question. OrderParameters memory orderParameters = ( - advancedOrders[i].parameters + advancedOrder.parameters ); // Emit an OrderFulfilled event. _emitOrderFulfilledEvent( - orderHashes[i], + orderHash, orderParameters.offerer, orderParameters.zone, recipient, @@ -473,8 +540,11 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { * approvals from. The zero hash signifies * that no conduit should be used, with * direct approvals set on Consideration. - * @param recipient The intended recipient for all received - * items. + * @param recipient The intended recipient for all items + * that do not already have a designated + * recipient and are not already used as + * part of a provided fulfillment. + * @param orderHashes An array of order hashes for each order. * * @return availableOrders An array of booleans indicating if each order * with an index corresponding to the index of the @@ -488,7 +558,8 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { FulfillmentComponent[][] memory offerFulfillments, FulfillmentComponent[][] memory considerationFulfillments, bytes32 fulfillerConduitKey, - address recipient + address recipient, + bytes32[] memory orderHashes ) internal returns (bool[] memory availableOrders, Execution[] memory executions) @@ -512,49 +583,52 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { uint256 totalFilteredExecutions = 0; // Iterate over each offer fulfillment. - for (uint256 i = 0; i < totalOfferFulfillments; ++i) { - /// Retrieve the offer fulfillment components in question. - FulfillmentComponent[] memory components = ( - offerFulfillments[i] - ); - + for (uint256 i = 0; i < totalOfferFulfillments; ) { // Derive aggregated execution corresponding with fulfillment. Execution memory execution = _aggregateAvailable( advancedOrders, Side.OFFER, - components, + offerFulfillments[i], fulfillerConduitKey, recipient ); // If offerer and recipient on the execution are the same... - if (execution.item.recipient == execution.offerer) { + if ( + _unmaskedAddressComparison( + execution.item.recipient, + execution.offerer + ) + ) { // Increment total filtered executions. ++totalFilteredExecutions; } else { // Otherwise, assign the execution to the executions array. executions[i - totalFilteredExecutions] = execution; } + + // Increment iterator. + ++i; } // Iterate over each consideration fulfillment. - for (uint256 i = 0; i < totalConsiderationFulfillments; ++i) { - /// Retrieve consideration fulfillment components in question. - FulfillmentComponent[] memory components = ( - considerationFulfillments[i] - ); - + for (uint256 i = 0; i < totalConsiderationFulfillments; ) { // Derive aggregated execution corresponding with fulfillment. Execution memory execution = _aggregateAvailable( advancedOrders, Side.CONSIDERATION, - components, + considerationFulfillments[i], fulfillerConduitKey, address(0) // unused ); // If offerer and recipient on the execution are the same... - if (execution.item.recipient == execution.offerer) { + if ( + _unmaskedAddressComparison( + execution.item.recipient, + execution.offerer + ) + ) { // Increment total filtered executions. ++totalFilteredExecutions; } else { @@ -563,6 +637,9 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { i + totalOfferFulfillments - totalFilteredExecutions ] = execution; } + + // Increment iterator. + ++i; } // If some number of executions have been filtered... @@ -579,13 +656,15 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { // Revert if no orders are available. if (executions.length == 0) { - revert NoSpecifiedOrdersAvailable(); + _revertNoSpecifiedOrdersAvailable(); } // Perform final checks and return. availableOrders = _performFinalChecksAndExecuteOrders( advancedOrders, - executions + executions, + orderHashes, + recipient ); return (availableOrders, executions); @@ -600,62 +679,29 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { * @param executions An array of elements indicating the sequence of * transfers to perform when fulfilling the given * orders. + * @param orderHashes An array of order hashes for each order. + * @param recipient The intended recipient for all items that do + * not already have a designated recipient and are + * not used as part of a provided fulfillment. * - * @return availableOrders An array of booleans indicating if each order - * with an index corresponding to the index of the - * returned boolean was fulfillable or not. + * @return availableOrders An array of booleans indicating if each order + * with an index corresponding to the index of the + * returned boolean was fulfillable or not. */ function _performFinalChecksAndExecuteOrders( AdvancedOrder[] memory advancedOrders, - Execution[] memory executions - ) internal returns (bool[] memory availableOrders) { + Execution[] memory executions, + bytes32[] memory orderHashes, + address recipient + ) internal returns (bool[] memory /* availableOrders */) { + // Declare a variable for the available native token balance. + uint256 nativeTokenBalance; + // Retrieve the length of the advanced orders array and place on stack. uint256 totalOrders = advancedOrders.length; // Initialize array for tracking available orders. - availableOrders = new bool[](totalOrders); - - // Skip overflow checks as all for loops are indexed starting at zero. - unchecked { - // Iterate over orders to ensure all considerations are met. - for (uint256 i = 0; i < totalOrders; ++i) { - // Retrieve the order in question. - AdvancedOrder memory advancedOrder = advancedOrders[i]; - - // Skip consideration item checks for order if not fulfilled. - if (advancedOrder.numerator == 0) { - // Note: orders do not need to be marked as unavailable as a - // new memory region has been allocated. Review carefully if - // altering compiler version or managing memory manually. - continue; - } - - // Mark the order as available. - availableOrders[i] = true; - - // Retrieve consideration items to ensure they are fulfilled. - ConsiderationItem[] memory consideration = ( - advancedOrder.parameters.consideration - ); - - // Read length of consideration array and place on the stack. - uint256 totalConsiderationItems = consideration.length; - - // Iterate over each consideration item to ensure it is met. - for (uint256 j = 0; j < totalConsiderationItems; ++j) { - // Retrieve remaining amount on the consideration item. - uint256 unmetAmount = consideration[j].startAmount; - - // Revert if the remaining amount is not zero. - if (unmetAmount != 0) { - revert ConsiderationNotMet(i, j, unmetAmount); - } - } - } - } - - // Put ether value supplied by the caller on the stack. - uint256 etherRemaining = msg.value; + bool[] memory availableOrders = new bool[](totalOrders); // Initialize an accumulator array. From this point forward, no new // memory regions can be safely allocated until the accumulator is no @@ -675,14 +721,14 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { // If execution transfers native tokens, reduce value available. if (item.itemType == ItemType.NATIVE) { - // Ensure that sufficient native tokens are still available. - if (item.amount > etherRemaining) { - revert InsufficientEtherSupplied(); + // Get the current available balance of native tokens. + assembly { + nativeTokenBalance := selfbalance() } - // Skip underflow check as amount is less than ether remaining. - unchecked { - etherRemaining -= item.amount; + // Ensure that sufficient native tokens are still available. + if (item.amount > nativeTokenBalance) { + _revertInsufficientNativeTokensSupplied(); } } @@ -700,19 +746,168 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { } } + // Skip overflow checks as all for loops are indexed starting at zero. + unchecked { + // duplicate recipient address to stack to avoid stack-too-deep + address _recipient = recipient; + + // Iterate over orders to ensure all consideration items are met. + for (uint256 i = 0; i < totalOrders; ++i) { + // Retrieve the order in question. + AdvancedOrder memory advancedOrder = advancedOrders[i]; + + // Skip consideration item checks for order if not fulfilled. + if (advancedOrder.numerator == 0) { + // This is required because the current memory region, which + // was previously used by the accumulator, might be dirty. + availableOrders[i] = false; + continue; + } + + // Mark the order as available. + availableOrders[i] = true; + + // Retrieve the order parameters. + OrderParameters memory parameters = advancedOrder.parameters; + + { + // Retrieve offer items. + OfferItem[] memory offer = parameters.offer; + + // Read length of offer array & place on the stack. + uint256 totalOfferItems = offer.length; + + // Iterate over each offer item to restore it. + for (uint256 j = 0; j < totalOfferItems; ++j) { + OfferItem memory offerItem = offer[j]; + // Retrieve original amount on the offer item. + uint256 originalAmount = offerItem.endAmount; + // Retrieve remaining amount on the offer item. + uint256 unspentAmount = offerItem.startAmount; + + // Transfer to recipient if unspent amount is not zero. + // Note that the transfer will not be reflected in the + // executions array. + if (unspentAmount != 0) { + _transfer( + _convertOfferItemToReceivedItemWithRecipient( + offerItem, + _recipient + ), + parameters.offerer, + parameters.conduitKey, + accumulator + ); + } + + // Restore original amount on the offer item. + offerItem.startAmount = originalAmount; + } + } + + { + // Retrieve consideration items & ensure they are fulfilled. + ConsiderationItem[] memory consideration = ( + parameters.consideration + ); + + // Read length of consideration array & place on the stack. + uint256 totalConsiderationItems = consideration.length; + + // Iterate over each consideration item to ensure it is met. + for (uint256 j = 0; j < totalConsiderationItems; ++j) { + ConsiderationItem memory considerationItem = ( + consideration[j] + ); + + // Retrieve remaining amount on the consideration item. + uint256 unmetAmount = considerationItem.startAmount; + + // Revert if the remaining amount is not zero. + if (unmetAmount != 0) { + _revertConsiderationNotMet(i, j, unmetAmount); + } + + // Utilize assembly to restore the original value. + assembly { + // Write recipient to startAmount. + mstore( + add( + considerationItem, + ReceivedItem_amount_offset + ), + mload( + add( + considerationItem, + ConsiderationItem_recipient_offset + ) + ) + ) + } + } + } + + // Check restricted orders and contract orders. + _assertRestrictedAdvancedOrderValidity( + advancedOrder, + orderHashes, + orderHashes[i] + ); + } + } + // Trigger any remaining accumulated transfers via call to the conduit. _triggerIfArmed(accumulator); - // If any ether remains after fulfillments, return it to the caller. - if (etherRemaining != 0) { - _transferEth(payable(msg.sender), etherRemaining); + // Determine whether any native token balance remains. + assembly { + nativeTokenBalance := selfbalance() + } + + // Return any remaining native token balance to the caller. + if (nativeTokenBalance != 0) { + _transferNativeTokens(payable(msg.sender), nativeTokenBalance); } // Clear the reentrancy guard. _clearReentrancyGuard(); // Return the array containing available orders. - return (availableOrders); + return availableOrders; + } + + /** + * @dev Internal function to emit an OrdersMatched event using the same + * memory region as the existing order hash array. + * + * @param orderHashes An array of order hashes to include as an argument for + * the OrdersMatched event. + */ + function _emitOrdersMatched(bytes32[] memory orderHashes) internal { + assembly { + // Load the array length from memory. + let length := mload(orderHashes) + + // Get the full size of the event data - one word for the offset, + // one for the array length and one per hash. + let dataSize := add(TwoWords, shl(OneWordShift, length)) + + // Get pointer to start of data, reusing word before array length + // for the offset. + let dataPointer := sub(orderHashes, OneWord) + + // Cache the existing word in memory at the offset pointer. + let cache := mload(dataPointer) + + // Write an offset of 32. + mstore(dataPointer, OneWord) + + // Emit the OrdersMatched event. + log1(dataPointer, dataSize, OrdersMatchedTopic0) + + // Restore the cached word. + mstore(dataPointer, cache) + } } /** @@ -746,6 +941,8 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { * to consideration components. Note that each * consideration component must be fully met in * order for the match operation to be valid. + * @param recipient The intended recipient for all unspent offer + * item amounts. * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given @@ -754,19 +951,29 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { function _matchAdvancedOrders( AdvancedOrder[] memory advancedOrders, CriteriaResolver[] memory criteriaResolvers, - Fulfillment[] calldata fulfillments - ) internal returns (Execution[] memory executions) { + Fulfillment[] memory fulfillments, + address recipient + ) internal returns (Execution[] memory /* executions */) { // Validate orders, update order status, and determine item amounts. - _validateOrdersAndPrepareToFulfill( + bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill( advancedOrders, criteriaResolvers, true, // Signifies that invalid orders should revert. advancedOrders.length, - address(0) // OrderFulfilled event has no recipient when matching. + recipient ); - // Fulfill the orders using the supplied fulfillments. - return _fulfillAdvancedOrders(advancedOrders, fulfillments); + // Emit OrdersMatched event, providing an array of matched order hashes. + _emitOrdersMatched(orderHashes); + + // Fulfill the orders using the supplied fulfillments and recipient. + return + _fulfillAdvancedOrders( + advancedOrders, + fulfillments, + orderHashes, + recipient + ); } /** @@ -781,14 +988,20 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { * that the final amount of each consideration * component must be zero for a match operation to * be considered valid. + * @param orderHashes An array of order hashes for each order. + * @param recipient The intended recipient for all items that do + * not already have a designated recipient and are + * not used as part of a provided fulfillment. * - * @return executions An array of elements indicating the sequence of - * transfers performed as part of matching the given - * orders. + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the + * given orders. */ function _fulfillAdvancedOrders( AdvancedOrder[] memory advancedOrders, - Fulfillment[] calldata fulfillments + Fulfillment[] memory fulfillments, + bytes32[] memory orderHashes, + address recipient ) internal returns (Execution[] memory executions) { // Retrieve fulfillments array length and place on the stack. uint256 totalFulfillments = fulfillments.length; @@ -804,17 +1017,23 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { // Iterate over each fulfillment. for (uint256 i = 0; i < totalFulfillments; ++i) { /// Retrieve the fulfillment in question. - Fulfillment calldata fulfillment = fulfillments[i]; + Fulfillment memory fulfillment = fulfillments[i]; // Derive the execution corresponding with the fulfillment. Execution memory execution = _applyFulfillment( advancedOrders, fulfillment.offerComponents, - fulfillment.considerationComponents + fulfillment.considerationComponents, + i ); // If offerer and recipient on the execution are the same... - if (execution.item.recipient == execution.offerer) { + if ( + _unmaskedAddressComparison( + execution.item.recipient, + execution.offerer + ) + ) { // Increment total filtered executions. ++totalFilteredExecutions; } else { @@ -836,9 +1055,14 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { } // Perform final checks and execute orders. - _performFinalChecksAndExecuteOrders(advancedOrders, executions); + _performFinalChecksAndExecuteOrders( + advancedOrders, + executions, + orderHashes, + recipient + ); // Return the executions array. - return (executions); + return executions; } } diff --git a/contracts/lib/OrderFulfiller.sol b/contracts/lib/OrderFulfiller.sol index 4a7fe6a21..405508355 100644 --- a/contracts/lib/OrderFulfiller.sol +++ b/contracts/lib/OrderFulfiller.sol @@ -1,18 +1,16 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -import { ItemType } from "./ConsiderationEnums.sol"; +import { ItemType, OrderType } from "./ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, + AdvancedOrder, ConsiderationItem, - SpentItem, - ReceivedItem, + CriteriaResolver, + OfferItem, OrderParameters, - Order, - AdvancedOrder, - CriteriaResolver + ReceivedItem, + SpentItem } from "./ConsiderationStructs.sol"; import { BasicOrderFulfiller } from "./BasicOrderFulfiller.sol"; @@ -21,7 +19,17 @@ import { CriteriaResolution } from "./CriteriaResolution.sol"; import { AmountDeriver } from "./AmountDeriver.sol"; -import "./ConsiderationConstants.sol"; +import { + _revertInsufficientNativeTokensSupplied, + _revertInvalidNativeOfferItem +} from "./ConsiderationErrors.sol"; + +import { + AccumulatorDisarmed, + ConsiderationItem_recipient_offset, + ReceivedItem_amount_offset, + ReceivedItem_recipient_offset +} from "./ConsiderationConstants.sol"; /** * @title OrderFulfiller @@ -43,9 +51,9 @@ contract OrderFulfiller is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - BasicOrderFulfiller(conduitController) - {} + constructor( + address conduitController + ) BasicOrderFulfiller(conduitController) {} /** * @dev Internal function to validate an order and update its status, adjust @@ -80,22 +88,17 @@ contract OrderFulfiller is address recipient ) internal returns (bool) { // Ensure this function cannot be triggered during a reentrant call. - _setReentrancyGuard(); - - // Declare empty bytes32 array (unused, will remain empty). - bytes32[] memory priorOrderHashes; + _setReentrancyGuard( + // Native tokens accepted during execution for contract order types. + advancedOrder.parameters.orderType == OrderType.CONTRACT + ); // Validate order, update status, and determine fraction to fill. ( bytes32 orderHash, uint256 fillNumerator, uint256 fillDenominator - ) = _validateOrderAndUpdateStatus( - advancedOrder, - criteriaResolvers, - true, - priorOrderHashes - ); + ) = _validateOrderAndUpdateStatus(advancedOrder, true); // Create an array with length 1 containing the order. AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](1); @@ -118,6 +121,17 @@ contract OrderFulfiller is recipient ); + // Declare empty bytes32 array and populate with the order hash. + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = orderHash; + + // Ensure restricted orders have a valid submitter or pass a zone check. + _assertRestrictedAdvancedOrderValidity( + advancedOrders[0], + orderHashes, + orderHash + ); + // Emit an event signifying that the order has been fulfilled. _emitOrderFulfilledEvent( orderHash, @@ -170,10 +184,10 @@ contract OrderFulfiller is // As of solidity 0.6.0, inline assembly cannot directly access function // definitions, but can still access locally scoped function variables. - // This means that in order to recast the type of a function, we need to - // create a local variable to reference the internal function definition - // (using the same type) and a local variable with the desired type, - // and then cast the original function pointer to the desired type. + // This means that a local variable to reference the internal function + // definition (using the same type), along with a local variable with + // the desired type, must first be created. Then, the original function + // pointer can be recast to the desired type. /** * Repurpose existing OfferItem memory regions on the offer array for @@ -190,36 +204,26 @@ contract OrderFulfiller is // Declare a nested scope to minimize stack depth. unchecked { - // Declare a virtual function pointer taking an OfferItem argument. - function(OfferItem memory, address, bytes32, bytes memory) - internal _transferOfferItem; - - { - // Assign _transfer function to a new function pointer (it takes - // a ReceivedItem as its initial argument) - function(ReceivedItem memory, address, bytes32, bytes memory) - internal _transferReceivedItem = _transfer; - - // Utilize assembly to override the virtual function pointer. - assembly { - // Cast initial ReceivedItem type to an OfferItem type. - _transferOfferItem := _transferReceivedItem - } - } - // Read offer array length from memory and place on stack. uint256 totalOfferItems = orderParameters.offer.length; + // Create a variable to indicate whether the order has any + // native offer items + uint256 anyNativeItems; + // Iterate over each offer on the order. // Skip overflow check as for loop is indexed starting at zero. for (uint256 i = 0; i < totalOfferItems; ++i) { // Retrieve the offer item. OfferItem memory offerItem = orderParameters.offer[i]; - // Offer items for the native token can not be received - // outside of a match order function. - if (offerItem.itemType == ItemType.NATIVE) { - revert InvalidNativeOfferItem(); + // Offer items for the native token can not be received outside + // of a match order function except as part of a contract order. + { + ItemType itemType = offerItem.itemType; + assembly { + anyNativeItems := or(anyNativeItems, iszero(itemType)) + } } // Declare an additional nested scope to minimize stack depth. @@ -252,17 +256,36 @@ contract OrderFulfiller is } // Transfer the item from the offerer to the recipient. - _transferOfferItem( + _toOfferItemInput(_transfer)( offerItem, orderParameters.offerer, orderParameters.conduitKey, accumulator ); } + + // If a non-contract order has native offer items, throw with an + // `InvalidNativeOfferItem` custom error. + { + OrderType orderType = orderParameters.orderType; + uint256 invalidNativeOfferItem; + assembly { + invalidNativeOfferItem := and( + // Note that this check requires that there are no order + // types beyond the current set (0-4). It will need to + // be modified if more order types are added. + lt(orderType, 4), + anyNativeItems + ) + } + if (invalidNativeOfferItem != 0) { + _revertInvalidNativeOfferItem(); + } + } } - // Put ether value supplied by the caller on the stack. - uint256 etherRemaining = msg.value; + // Declare a variable for the available native token balance. + uint256 nativeTokenBalance; /** * Repurpose existing ConsiderationItem memory regions on the @@ -281,22 +304,6 @@ contract OrderFulfiller is // Declare a nested scope to minimize stack depth. unchecked { - // Declare virtual function pointer with ConsiderationItem argument. - function(ConsiderationItem memory, address, bytes32, bytes memory) - internal _transferConsiderationItem; - { - // Reassign _transfer function to a new function pointer (it - // takes a ReceivedItem as its initial argument). - function(ReceivedItem memory, address, bytes32, bytes memory) - internal _transferReceivedItem = _transfer; - - // Utilize assembly to override the virtual function pointer. - assembly { - // Cast ReceivedItem type to ConsiderationItem type. - _transferConsiderationItem := _transferReceivedItem - } - } - // Read consideration array length from memory and place on stack. uint256 totalConsiderationItems = orderParameters .consideration @@ -343,17 +350,19 @@ contract OrderFulfiller is // Reduce available value if offer spent ETH or a native token. if (considerationItem.itemType == ItemType.NATIVE) { - // Ensure that sufficient native tokens are still available. - if (amount > etherRemaining) { - revert InsufficientEtherSupplied(); + // Get the current available balance of native tokens. + assembly { + nativeTokenBalance := selfbalance() } - // Skip underflow check as a comparison has just been made. - etherRemaining -= amount; + // Ensure that sufficient native tokens are still available. + if (amount > nativeTokenBalance) { + _revertInsufficientNativeTokensSupplied(); + } } // Transfer item from caller to recipient specified by the item. - _transferConsiderationItem( + _toConsiderationItemInput(_transfer)( considerationItem, msg.sender, fulfillerConduitKey, @@ -365,10 +374,14 @@ contract OrderFulfiller is // Trigger any remaining accumulated transfers via call to the conduit. _triggerIfArmed(accumulator); - // If any ether remains after fulfillments... - if (etherRemaining != 0) { - // return it to the caller. - _transferEth(payable(msg.sender), etherRemaining); + // Determine whether any native token balance remains. + assembly { + nativeTokenBalance := selfbalance() + } + + // Return any remaining native token balance to the caller. + if (nativeTokenBalance != 0) { + _transferNativeTokens(payable(msg.sender), nativeTokenBalance); } } @@ -380,7 +393,7 @@ contract OrderFulfiller is * @param orderHash The order hash. * @param offerer The offerer for the order. * @param zone The zone for the order. - * @param fulfiller The fulfiller of the order, or the null address if + * @param recipient The recipient of the order, or the null address if * the order was fulfilled via order matching. * @param offer The offer items for the order. * @param consideration The consideration items for the order. @@ -389,7 +402,7 @@ contract OrderFulfiller is bytes32 orderHash, address offerer, address zone, - address fulfiller, + address recipient, OfferItem[] memory offer, ConsiderationItem[] memory consideration ) internal { @@ -410,64 +423,9 @@ contract OrderFulfiller is orderHash, offerer, zone, - fulfiller, + recipient, spentItems, receivedItems ); } - - /** - * @dev Internal pure function to convert an order to an advanced order with - * numerator and denominator of 1 and empty extraData. - * - * @param order The order to convert. - * - * @return advancedOrder The new advanced order. - */ - function _convertOrderToAdvanced(Order calldata order) - internal - pure - returns (AdvancedOrder memory advancedOrder) - { - // Convert to partial order (1/1 or full fill) and return new value. - advancedOrder = AdvancedOrder( - order.parameters, - 1, - 1, - order.signature, - "" - ); - } - - /** - * @dev Internal pure function to convert an array of orders to an array of - * advanced orders with numerator and denominator of 1. - * - * @param orders The orders to convert. - * - * @return advancedOrders The new array of partial orders. - */ - function _convertOrdersToAdvanced(Order[] calldata orders) - internal - pure - returns (AdvancedOrder[] memory advancedOrders) - { - // Read the number of orders from calldata and place on the stack. - uint256 totalOrders = orders.length; - - // Allocate new empty array for each partial order in memory. - advancedOrders = new AdvancedOrder[](totalOrders); - - // Skip overflow check as the index for the loop starts at zero. - unchecked { - // Iterate over the given orders. - for (uint256 i = 0; i < totalOrders; ++i) { - // Convert to partial order (1/1 or full fill) and update array. - advancedOrders[i] = _convertOrderToAdvanced(orders[i]); - } - } - - // Return the array of advanced orders. - return advancedOrders; - } } diff --git a/contracts/lib/OrderValidator.sol b/contracts/lib/OrderValidator.sol index 70e05f049..afc927d25 100644 --- a/contracts/lib/OrderValidator.sol +++ b/contracts/lib/OrderValidator.sol @@ -1,24 +1,56 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { OrderType } from "./ConsiderationEnums.sol"; -// prettier-ignore import { - OrderParameters, - Order, AdvancedOrder, + ConsiderationItem, + OfferItem, + Order, OrderComponents, - OrderStatus, - CriteriaResolver + OrderParameters, + OrderStatus } from "./ConsiderationStructs.sol"; -import "./ConsiderationConstants.sol"; +import { + _revertBadFraction, + _revertCannotCancelOrder, + _revertConsiderationLengthNotEqualToTotalOriginal, + _revertInvalidContractOrder, + _revertPartialFillsNotEnabledForOrder +} from "./ConsiderationErrors.sol"; import { Executor } from "./Executor.sol"; import { ZoneInteraction } from "./ZoneInteraction.sol"; +import { MemoryPointer } from "../helpers/PointerLibraries.sol"; + +import { + AdvancedOrder_denominator_offset, + AdvancedOrder_numerator_offset, + BasicOrder_offerer_cdPtr, + Common_amount_offset, + Common_endAmount_offset, + Common_identifier_offset, + Common_token_offset, + ConsiderItem_recipient_offset, + ContractOrder_orderHash_offerer_shift, + MaxUint120, + OrderStatus_filledDenominator_offset, + OrderStatus_filledNumerator_offset, + OrderStatus_ValidatedAndNotCancelled +} from "./ConsiderationConstants.sol"; + +import { + Error_selector_offset, + Panic_arithmetic, + Panic_error_code_ptr, + Panic_error_length, + Panic_error_selector +} from "./ConsiderationErrorConstants.sol"; + /** * @title OrderValidator * @author 0age @@ -29,6 +61,9 @@ contract OrderValidator is Executor, ZoneInteraction { // Track status of each order (validated, cancelled, and fraction filled). mapping(bytes32 => OrderStatus) private _orderStatus; + // Track nonces for contract offerers. + mapping(address => uint256) internal _contractNonces; + /** * @dev Derive and set hashes, reference chainId, and associated domain * separator during deployment. @@ -41,17 +76,25 @@ contract OrderValidator is Executor, ZoneInteraction { /** * @dev Internal function to verify and update the status of a basic order. + * Note that this function may only be safely called as part of basic + * orders, as it assumes a specific calldata encoding structure that + * must first be validated. * * @param orderHash The hash of the order. - * @param offerer The offerer of the order. * @param signature A signature from the offerer indicating that the order * has been approved. */ function _validateBasicOrderAndUpdateStatus( bytes32 orderHash, - address offerer, - bytes memory signature + bytes calldata signature ) internal { + // Retrieve offerer directly using fixed calldata offset based on strict + // basic parameter encoding. + address offerer; + assembly { + offerer := calldataload(BasicOrder_offerer_cdPtr) + } + // Retrieve the order status for the given order hash. OrderStatus storage orderStatus = _orderStatus[orderHash]; @@ -84,37 +127,20 @@ contract OrderValidator is Executor, ZoneInteraction { * fill. Note that all offer and consideration * amounts must divide with no remainder in order * for a partial fill to be valid. - * @param criteriaResolvers An array where each element contains a reference - * to a specific offer or consideration, a token - * identifier, and a proof that the supplied token - * identifier is contained in the order's merkle - * root. Note that a criteria of zero indicates - * that any (transferable) token identifier is - * valid and that no proof needs to be supplied. * @param revertOnInvalid A boolean indicating whether to revert if the * order is invalid due to the time or status. - * @param priorOrderHashes The order hashes of each order supplied prior to - * the current order as part of a "match" variety - * of order fulfillment (e.g. this array will be - * empty for single or "fulfill available"). * * @return orderHash The order hash. - * @return newNumerator A value indicating the portion of the order that + * @return numerator A value indicating the portion of the order that * will be filled. - * @return newDenominator A value indicating the total size of the order. + * @return denominator A value indicating the total size of the order. */ function _validateOrderAndUpdateStatus( AdvancedOrder memory advancedOrder, - CriteriaResolver[] memory criteriaResolvers, - bool revertOnInvalid, - bytes32[] memory priorOrderHashes + bool revertOnInvalid ) internal - returns ( - bytes32 orderHash, - uint256 newNumerator, - uint256 newDenominator - ) + returns (bytes32 orderHash, uint256 numerator, uint256 denominator) { // Retrieve the parameters for the order. OrderParameters memory orderParameters = advancedOrder.parameters; @@ -132,38 +158,74 @@ contract OrderValidator is Executor, ZoneInteraction { } // Read numerator and denominator from memory and place on the stack. - uint256 numerator = uint256(advancedOrder.numerator); - uint256 denominator = uint256(advancedOrder.denominator); + // Note that overflowed values are masked. + assembly { + numerator := and( + mload(add(advancedOrder, AdvancedOrder_numerator_offset)), + MaxUint120 + ) + + denominator := and( + mload(add(advancedOrder, AdvancedOrder_denominator_offset)), + MaxUint120 + ) + } - // Ensure that the supplied numerator and denominator are valid. - if (numerator > denominator || numerator == 0) { - revert BadFraction(); + // Declare variable for tracking the validity of the supplied fraction. + bool invalidFraction; + + // If the order is a contract order, return the generated order. + if (orderParameters.orderType == OrderType.CONTRACT) { + // Ensure that the numerator and denominator are both equal to 1. + assembly { + // (1 ^ nd =/= 0) => (nd =/= 1) => (n =/= 1) || (d =/= 1) + // It's important that the values are 120-bit masked before + // multiplication is applied. Otherwise, the last implication + // above is not correct (mod 2^256). + invalidFraction := xor(mul(numerator, denominator), 1) + } + + // Revert if the supplied numerator and denominator are not valid. + if (invalidFraction) { + _revertBadFraction(); + } + + // Return the generated order based on the order params and the + // provided extra data. If revertOnInvalid is true, the function + // will revert if the input is invalid. + return + _getGeneratedOrder( + orderParameters, + advancedOrder.extraData, + revertOnInvalid + ); + } + + // Ensure numerator does not exceed denominator and is not zero. + assembly { + invalidFraction := or(gt(numerator, denominator), iszero(numerator)) + } + + // Revert if the supplied numerator and denominator are not valid. + if (invalidFraction) { + _revertBadFraction(); } // If attempting partial fill (n < d) check order type & ensure support. if ( - numerator < denominator && - _doesNotSupportPartialFills(orderParameters.orderType) + _doesNotSupportPartialFills( + orderParameters.orderType, + numerator, + denominator + ) ) { // Revert if partial fill was attempted on an unsupported order. - revert PartialFillsNotEnabledForOrder(); + _revertPartialFillsNotEnabledForOrder(); } // Retrieve current counter & use it w/ parameters to derive order hash. orderHash = _assertConsiderationLengthAndGetOrderHash(orderParameters); - // Ensure restricted orders have a valid submitter or pass a zone check. - _assertRestrictedAdvancedOrderValidity( - advancedOrder, - criteriaResolvers, - priorOrderHashes, - orderHash, - orderParameters.zoneHash, - orderParameters.orderType, - orderParameters.offerer, - orderParameters.zone - ); - // Retrieve the order status using the derived order hash. OrderStatus storage orderStatus = _orderStatus[orderHash]; @@ -189,43 +251,79 @@ contract OrderValidator is Executor, ZoneInteraction { ); } - // Read filled amount as numerator and denominator and put on the stack. - uint256 filledNumerator = orderStatus.numerator; - uint256 filledDenominator = orderStatus.denominator; - - // If order (orderStatus) currently has a non-zero denominator it is - // partially filled. - if (filledDenominator != 0) { - // If denominator of 1 supplied, fill all remaining amount on order. - if (denominator == 1) { - // Scale numerator & denominator to match current denominator. - numerator = filledDenominator; - denominator = filledDenominator; - } - // Otherwise, if supplied denominator differs from current one... - else if (filledDenominator != denominator) { - // scale current numerator by the supplied denominator, then... - filledNumerator *= denominator; - - // the supplied numerator & denominator by current denominator. - numerator *= filledDenominator; - denominator *= filledDenominator; - } + assembly { + let orderStatusSlot := orderStatus.slot + // Read filled amount as numerator and denominator and put on stack. + let filledNumerator := sload(orderStatusSlot) + let filledDenominator := shr( + OrderStatus_filledDenominator_offset, + filledNumerator + ) - // Once adjusted, if current+supplied numerator exceeds denominator: - if (filledNumerator + numerator > denominator) { - // Skip underflow check: denominator >= orderStatus.numerator - unchecked { - // Reduce current numerator so it + supplied = denominator. - numerator = denominator - filledNumerator; + for { + + } 1 { + + } { + if iszero(filledDenominator) { + filledNumerator := numerator + + break } - } - // Increment the filled numerator by the new numerator. - filledNumerator += numerator; + // Shift and mask to calculate the current filled numerator. + filledNumerator := and( + shr(OrderStatus_filledNumerator_offset, filledNumerator), + MaxUint120 + ) + + // If denominator of 1 supplied, fill entire remaining amount. + if eq(denominator, 1) { + numerator := sub(filledDenominator, filledNumerator) + denominator := filledDenominator + filledNumerator := filledDenominator + + break + } + + // If supplied denominator equals to the current one: + if eq(denominator, filledDenominator) { + // Increment the filled numerator by the new numerator. + filledNumerator := add(numerator, filledNumerator) + + // Once adjusted, if current + supplied numerator exceeds + // the denominator: + let carry := mul( + sub(filledNumerator, denominator), + gt(filledNumerator, denominator) + ) + + numerator := sub(numerator, carry) + + filledNumerator := sub(filledNumerator, carry) + + break + } + + // Otherwise, if supplied denominator differs from current one: + filledNumerator := mul(filledNumerator, denominator) + numerator := mul(numerator, filledDenominator) + denominator := mul(denominator, filledDenominator) + + // Increment the filled numerator by the new numerator. + filledNumerator := add(numerator, filledNumerator) + + // Once adjusted, if current + supplied numerator exceeds + // denominator: + let carry := mul( + sub(filledNumerator, denominator), + gt(filledNumerator, denominator) + ) + + numerator := sub(numerator, carry) + + filledNumerator := sub(filledNumerator, carry) - // Use assembly to ensure fractional amounts are below max uint120. - assembly { // Check filledNumerator and denominator for uint120 overflow. if or( gt(filledNumerator, MaxUint120), @@ -263,34 +361,305 @@ contract OrderValidator is Executor, ZoneInteraction { gt(denominator, MaxUint120) ) { // Store the Panic error signature. - mstore(0, Panic_error_signature) + mstore(0, Panic_error_selector) + // Store the arithmetic (0x11) panic code. + mstore(Panic_error_code_ptr, Panic_arithmetic) + + // revert(abi.encodeWithSignature( + // "Panic(uint256)", 0x11 + // )) + revert(Error_selector_offset, Panic_error_length) + } + } - // Set arithmetic (0x11) panic code as initial argument. - mstore(Panic_error_offset, Panic_arithmetic) + break + } - // Return, supplying Panic signature & arithmetic code. - revert(0, Panic_error_length) - } + // Update order status and fill amount, packing struct values. + // [denominator: 15 bytes] [numerator: 15 bytes] + // [isCancelled: 1 byte] [isValidated: 1 byte] + sstore( + orderStatusSlot, + or( + OrderStatus_ValidatedAndNotCancelled, + or( + shl( + OrderStatus_filledNumerator_offset, + filledNumerator + ), + shl(OrderStatus_filledDenominator_offset, denominator) + ) + ) + ) + } + } + + /** + * @dev Internal pure function to check the compatibility of two offer + * or consideration items for contract orders. Note that the itemType + * and identifier are reset in cases where criteria = 0 (collection- + * wide offers), which means that a contract offerer has full latitude + * to choose any identifier it wants mid-flight, in contrast to the + * normal behavior, where the fulfiller can pick which identifier to + * receive by providing a CriteriaResolver. + * + * @param originalItem The original offer or consideration item. + * @param newItem The new offer or consideration item. + * + * @return isInvalid Error buffer indicating if items are incompatible. + */ + function _compareItems( + MemoryPointer originalItem, + MemoryPointer newItem + ) internal pure returns (uint256 isInvalid) { + assembly { + let itemType := mload(originalItem) + let identifier := mload(add(originalItem, Common_identifier_offset)) + + // Set returned identifier for criteria-based items w/ criteria = 0. + if and(gt(itemType, 3), iszero(identifier)) { + // replace item type + itemType := sub(3, eq(itemType, 4)) + identifier := mload(add(newItem, Common_identifier_offset)) + } + + let originalAmount := mload(add(originalItem, Common_amount_offset)) + let newAmount := mload(add(newItem, Common_amount_offset)) + + isInvalid := iszero( + and( + // originalItem.token == newItem.token && + // originalItem.itemType == newItem.itemType + and( + eq( + mload(add(originalItem, Common_token_offset)), + mload(add(newItem, Common_token_offset)) + ), + eq(itemType, mload(newItem)) + ), + // originalItem.identifier == newItem.identifier && + // originalItem.startAmount == originalItem.endAmount + and( + eq( + identifier, + mload(add(newItem, Common_identifier_offset)) + ), + eq( + originalAmount, + mload(add(originalItem, Common_endAmount_offset)) + ) + ) + ) + ) + } + } + + /** + * @dev Internal pure function to check the compatibility of two recipients + * on consideration items for contract orders. This check is skipped if + * no recipient is originally supplied. + * + * @param originalRecipient The original consideration item recipient. + * @param newRecipient The new consideration item recipient. + * + * @return isInvalid Error buffer indicating if recipients are incompatible. + */ + function _checkRecipients( + address originalRecipient, + address newRecipient + ) internal pure returns (uint256 isInvalid) { + assembly { + isInvalid := iszero( + or( + iszero(originalRecipient), + eq(newRecipient, originalRecipient) + ) + ) + } + } + + /** + * @dev Internal function to generate a contract order. When a + * collection-wide criteria-based item (criteria = 0) is provided as an + * input to a contract order, the contract offerer has full latitude to + * choose any identifier it wants mid-flight, which differs from the + * usual behavior. For regular criteria-based orders with + * identifierOrCriteria = 0, the fulfiller can pick which identifier to + * receive by providing a CriteriaResolver. For contract offers with + * identifierOrCriteria = 0, Seaport does not expect a corresponding + * CriteriaResolver, and will revert if one is provided. + * + * @param orderParameters The parameters for the order. + * @param context The context for generating the order. + * @param revertOnInvalid Whether to revert on invalid input. + * + * @return orderHash The order hash. + * @return numerator The numerator. + * @return denominator The denominator. + */ + function _getGeneratedOrder( + OrderParameters memory orderParameters, + bytes memory context, + bool revertOnInvalid + ) + internal + returns (bytes32 orderHash, uint256 numerator, uint256 denominator) + { + // Ensure that consideration array length is equal to the total original + // consideration items value. + if ( + orderParameters.consideration.length != + orderParameters.totalOriginalConsiderationItems + ) { + _revertConsiderationLengthNotEqualToTotalOriginal(); + } + + { + address offerer = orderParameters.offerer; + bool success; + (MemoryPointer cdPtr, uint256 size) = _encodeGenerateOrder( + orderParameters, + context + ); + assembly { + success := call(gas(), offerer, 0, cdPtr, size, 0, 0) + } + + { + // Note: overflow impossible; nonce can't increment that high. + uint256 contractNonce; + unchecked { + // Note: nonce will be incremented even for skipped orders, + // and even if generateOrder's return data does not satisfy + // all the constraints. This is the case when errorBuffer + // != 0 and revertOnInvalid == false. + contractNonce = _contractNonces[offerer]++; + } + + assembly { + // Shift offerer address up 96 bytes and combine with nonce. + orderHash := xor( + contractNonce, + shl(ContractOrder_orderHash_offerer_shift, offerer) + ) } } - // Skip overflow check: checked above unless numerator is reduced. - unchecked { - // Update order status and fill amount, packing struct values. - orderStatus.isValidated = true; - orderStatus.isCancelled = false; - orderStatus.numerator = uint120(filledNumerator); - orderStatus.denominator = uint120(denominator); + + // Revert or skip if the call to generate the contract order failed. + if (!success) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); } - } else { - // Update order status and fill amount, packing struct values. - orderStatus.isValidated = true; - orderStatus.isCancelled = false; - orderStatus.numerator = uint120(numerator); - orderStatus.denominator = uint120(denominator); } - // Return order hash, a modified numerator, and a modified denominator. - return (orderHash, numerator, denominator); + // Decode the returned contract order and/or update the error buffer. + ( + uint256 errorBuffer, + OfferItem[] memory offer, + ConsiderationItem[] memory consideration + ) = _convertGetGeneratedOrderResult(_decodeGenerateOrderReturndata)(); + + // Revert or skip if the returndata could not be decoded correctly. + if (errorBuffer != 0) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + { + // Designate lengths. + uint256 originalOfferLength = orderParameters.offer.length; + uint256 newOfferLength = offer.length; + + // Explicitly specified offer items cannot be removed. + if (originalOfferLength > newOfferLength) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + // Iterate over each specified offer (e.g. minimumReceived) item. + for (uint256 i = 0; i < originalOfferLength; ) { + // Retrieve the pointer to the originally supplied item. + MemoryPointer mPtrOriginal = orderParameters + .offer[i] + .toMemoryPointer(); + + // Retrieve the pointer to the newly returned item. + MemoryPointer mPtrNew = offer[i].toMemoryPointer(); + + // Compare the items and update the error buffer accordingly. + errorBuffer |= + _cast( + mPtrOriginal + .offset(Common_amount_offset) + .readUint256() > + mPtrNew.offset(Common_amount_offset).readUint256() + ) | + _compareItems(mPtrOriginal, mPtrNew); + + // Increment the array (cannot overflow as index starts at 0). + unchecked { + ++i; + } + } + + // Assign the returned offer item in place of the original item. + orderParameters.offer = offer; + } + + { + // Designate lengths & memory locations. + ConsiderationItem[] memory originalConsiderationArray = ( + orderParameters.consideration + ); + uint256 newConsiderationLength = consideration.length; + + // New consideration items cannot be created. + if (newConsiderationLength > originalConsiderationArray.length) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + // Iterate over returned consideration & do not exceed maximumSpent. + for (uint256 i = 0; i < newConsiderationLength; ) { + // Retrieve the pointer to the originally supplied item. + MemoryPointer mPtrOriginal = originalConsiderationArray[i] + .toMemoryPointer(); + + // Retrieve the pointer to the newly returned item. + MemoryPointer mPtrNew = consideration[i].toMemoryPointer(); + + // Compare the items and update the error buffer accordingly + // and ensure that the recipients are equal when provided. + errorBuffer |= + _cast( + mPtrNew.offset(Common_amount_offset).readUint256() > + mPtrOriginal + .offset(Common_amount_offset) + .readUint256() + ) | + _compareItems(mPtrOriginal, mPtrNew) | + _checkRecipients( + mPtrOriginal + .offset(ConsiderItem_recipient_offset) + .readAddress(), + mPtrNew + .offset(ConsiderItem_recipient_offset) + .readAddress() + ); + + // Increment the array (cannot overflow as index starts at 0). + unchecked { + ++i; + } + } + + // Assign returned consideration item in place of the original item. + orderParameters.consideration = consideration; + } + + // Revert or skip if any item comparison failed. + if (errorBuffer != 0) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + // Return order hash and full fill amount (numerator & denominator = 1). + return (orderHash, 1, 1); } /** @@ -298,23 +667,24 @@ contract OrderValidator is Executor, ZoneInteraction { * only the offerer or the zone of a given order may cancel it. Callers * should ensure that the intended order was cancelled by calling * `getOrderStatus` and confirming that `isCancelled` returns `true`. + * Also note that contract orders are not cancellable. * * @param orders The orders to cancel. * * @return cancelled A boolean indicating whether the supplied orders were * successfully cancelled. */ - function _cancel(OrderComponents[] calldata orders) - internal - returns (bool cancelled) - { + function _cancel( + OrderComponents[] calldata orders + ) internal returns (bool cancelled) { // Ensure that the reentrancy guard is not currently set. _assertNonReentrant(); // Declare variables outside of the loop. OrderStatus storage orderStatus; - address offerer; - address zone; + + // Declare a variable for tracking invariants in the loop. + bool anyInvalidCallerOrContractOrder; // Skip overflow check as for loop is indexed starting at zero. unchecked { @@ -326,29 +696,30 @@ contract OrderValidator is Executor, ZoneInteraction { // Retrieve the order. OrderComponents calldata order = orders[i]; - offerer = order.offerer; - zone = order.zone; - - // Ensure caller is either offerer or zone of the order. - if (msg.sender != offerer && msg.sender != zone) { - revert InvalidCanceller(); + address offerer = order.offerer; + address zone = order.zone; + OrderType orderType = order.orderType; + + assembly { + // If caller is neither the offerer nor zone, or a contract + // order is present, flag anyInvalidCallerOrContractOrder. + anyInvalidCallerOrContractOrder := or( + anyInvalidCallerOrContractOrder, + // orderType == CONTRACT || + // !(caller == offerer || caller == zone) + or( + eq(orderType, 4), + iszero( + or(eq(caller(), offerer), eq(caller(), zone)) + ) + ) + ) } - // Derive order hash using the order parameters and the counter. bytes32 orderHash = _deriveOrderHash( - OrderParameters( - offerer, - zone, - order.offer, - order.consideration, - order.orderType, - order.startTime, - order.endTime, - order.zoneHash, - order.salt, - order.conduitKey, - order.consideration.length - ), + _toOrderParametersReturnType( + _decodeOrderComponentsAsOrderParameters + )(order.toCalldataPointer()), order.counter ); @@ -367,6 +738,10 @@ contract OrderValidator is Executor, ZoneInteraction { } } + if (anyInvalidCallerOrContractOrder) { + _revertCannotCancelOrder(); + } + // Return a boolean indicating that orders were successfully cancelled. cancelled = true; } @@ -386,10 +761,9 @@ contract OrderValidator is Executor, ZoneInteraction { * @return validated A boolean indicating whether the supplied orders were * successfully validated. */ - function _validate(Order[] calldata orders) - internal - returns (bool validated) - { + function _validate( + Order[] memory orders + ) internal returns (bool validated) { // Ensure that the reentrancy guard is not currently set. _assertNonReentrant(); @@ -404,12 +778,17 @@ contract OrderValidator is Executor, ZoneInteraction { uint256 totalOrders = orders.length; // Iterate over each order. - for (uint256 i = 0; i < totalOrders; ) { + for (uint256 i = 0; i < totalOrders; ++i) { // Retrieve the order. - Order calldata order = orders[i]; + Order memory order = orders[i]; // Retrieve the order parameters. - OrderParameters calldata orderParameters = order.parameters; + OrderParameters memory orderParameters = order.parameters; + + // Skip contract orders. + if (orderParameters.orderType == OrderType.CONTRACT) { + continue; + } // Move offerer from memory to the stack. offerer = orderParameters.offerer; @@ -432,6 +811,15 @@ contract OrderValidator is Executor, ZoneInteraction { // If the order has not already been validated... if (!orderStatus.isValidated) { + // Ensure that consideration array length is equal to the + // total original consideration items value. + if ( + orderParameters.consideration.length != + orderParameters.totalOriginalConsiderationItems + ) { + _revertConsiderationLengthNotEqualToTotalOriginal(); + } + // Verify the supplied signature. _verifySignature(offerer, orderHash, order.signature); @@ -439,15 +827,8 @@ contract OrderValidator is Executor, ZoneInteraction { orderStatus.isValidated = true; // Emit an event signifying the order has been validated. - emit OrderValidated( - orderHash, - offerer, - orderParameters.zone - ); + emit OrderValidated(orderHash, orderParameters); } - - // Increment counter inside body of the loop for gas efficiency. - ++i; } } @@ -472,7 +853,9 @@ contract OrderValidator is Executor, ZoneInteraction { * @return totalSize The total size of the order that is either filled or * unfilled (i.e. the "denominator"). */ - function _getOrderStatus(bytes32 orderHash) + function _getOrderStatus( + bytes32 orderHash + ) internal view returns ( @@ -494,26 +877,58 @@ contract OrderValidator is Executor, ZoneInteraction { ); } + /** + * @dev Internal pure function to either revert or return an empty tuple + * depending on the value of `revertOnInvalid`. + * + * @param revertOnInvalid Whether to revert on invalid input. + * @param contractOrderHash The contract order hash. + * + * @return orderHash The order hash. + * @return numerator The numerator. + * @return denominator The denominator. + */ + function _revertOrReturnEmpty( + bool revertOnInvalid, + bytes32 contractOrderHash + ) + internal + pure + returns (bytes32 orderHash, uint256 numerator, uint256 denominator) + { + if (revertOnInvalid) { + _revertInvalidContractOrder(contractOrderHash); + } + + return (contractOrderHash, 0, 0); + } + /** * @dev Internal pure function to check whether a given order type indicates * that partial fills are not supported (e.g. only "full fills" are * allowed for the order in question). * - * @param orderType The order type in question. + * @param orderType The order type in question. + * @param numerator The numerator in question. + * @param denominator The denominator in question. * * @return isFullOrder A boolean indicating whether the order type only * supports full fills. */ - function _doesNotSupportPartialFills(OrderType orderType) - internal - pure - returns (bool isFullOrder) - { + function _doesNotSupportPartialFills( + OrderType orderType, + uint256 numerator, + uint256 denominator + ) internal pure returns (bool isFullOrder) { // The "full" order types are even, while "partial" order types are odd. - // Bitwise and by 1 is equivalent to modulo by 2, but 2 gas cheaper. + // Bitwise and by 1 is equivalent to modulo by 2, but 2 gas cheaper. The + // check is only necessary if numerator is less than denominator. assembly { // Equivalent to `uint256(orderType) & 1 == 0`. - isFullOrder := iszero(and(orderType, 1)) + isFullOrder := and( + lt(numerator, denominator), + iszero(and(orderType, 1)) + ) } } } diff --git a/contracts/lib/ReentrancyGuard.sol b/contracts/lib/ReentrancyGuard.sol index 69169fe0a..35aca6ad2 100644 --- a/contracts/lib/ReentrancyGuard.sol +++ b/contracts/lib/ReentrancyGuard.sol @@ -1,9 +1,20 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { ReentrancyErrors } from "../interfaces/ReentrancyErrors.sol"; -import "./ConsiderationConstants.sol"; +import { LowLevelHelpers } from "./LowLevelHelpers.sol"; + +import { + _revertInvalidMsgValue, + _revertNoReentrantCalls +} from "./ConsiderationErrors.sol"; + +import { + _ENTERED_AND_ACCEPTING_NATIVE_TOKENS, + _ENTERED, + _NOT_ENTERED +} from "./ConsiderationConstants.sol"; /** * @title ReentrancyGuard @@ -11,7 +22,7 @@ import "./ConsiderationConstants.sol"; * @notice ReentrancyGuard contains a storage variable and related functionality * for protecting against reentrancy. */ -contract ReentrancyGuard is ReentrancyErrors { +contract ReentrancyGuard is ReentrancyErrors, LowLevelHelpers { // Prevent reentrant calls on protected functions. uint256 private _reentrancyGuard; @@ -24,16 +35,25 @@ contract ReentrancyGuard is ReentrancyErrors { } /** - * @dev Internal function to ensure that the sentinel value for the - * reentrancy guard is not currently set and, if not, to set the - * sentinel value for the reentrancy guard. + * @dev Internal function to ensure that a sentinel value for the reentrancy + * guard is not currently set and, if not, to set a sentinel value for + * the reentrancy guard based on whether or not native tokens may be + * received during execution or not. + * + * @param acceptNativeTokens A boolean indicating whether native tokens may + * be received during execution or not. */ - function _setReentrancyGuard() internal { + function _setReentrancyGuard(bool acceptNativeTokens) internal { // Ensure that the reentrancy guard is not already set. _assertNonReentrant(); - // Set the reentrancy guard. - _reentrancyGuard = _ENTERED; + // Set the reentrancy guard. A value of 2 indicates that native tokens + // may not be accepted during execution, whereas a value of 3 indicates + // that they will be accepted (with any remaining native tokens returned + // to the caller). + unchecked { + _reentrancyGuard = _ENTERED + _cast(acceptNativeTokens); + } } /** @@ -45,13 +65,24 @@ contract ReentrancyGuard is ReentrancyErrors { } /** - * @dev Internal view function to ensure that the sentinel value for the + * @dev Internal view function to ensure that a sentinel value for the reentrancy guard is not currently set. */ function _assertNonReentrant() internal view { // Ensure that the reentrancy guard is not currently set. if (_reentrancyGuard != _NOT_ENTERED) { - revert NoReentrantCalls(); + _revertNoReentrantCalls(); + } + } + + /** + * @dev Internal view function to ensure that the sentinel value indicating + * native tokens may be received during execution is currently set. + */ + function _assertAcceptingNativeTokens() internal view { + // Ensure that the reentrancy guard is not currently set. + if (_reentrancyGuard != _ENTERED_AND_ACCEPTING_NATIVE_TOKENS) { + _revertInvalidMsgValue(msg.value); } } } diff --git a/contracts/lib/SignatureVerification.sol b/contracts/lib/SignatureVerification.sol index 023d3c25f..a30ab656c 100644 --- a/contracts/lib/SignatureVerification.sol +++ b/contracts/lib/SignatureVerification.sol @@ -1,16 +1,42 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; -import { EIP1271Interface } from "../interfaces/EIP1271Interface.sol"; - -// prettier-ignore import { SignatureVerificationErrors } from "../interfaces/SignatureVerificationErrors.sol"; import { LowLevelHelpers } from "./LowLevelHelpers.sol"; -import "./ConsiderationConstants.sol"; +import { + ECDSA_MaxLength, + ECDSA_signature_s_offset, + ECDSA_signature_v_offset, + ECDSA_twentySeventhAndTwentyEighthBytesSet, + Ecrecover_args_size, + Ecrecover_precompile, + EIP1271_isValidSignature_calldata_baseLength, + EIP1271_isValidSignature_digest_negativeOffset, + EIP1271_isValidSignature_selector_negativeOffset, + EIP1271_isValidSignature_selector, + EIP1271_isValidSignature_signature_head_offset, + EIP2098_allButHighestBitMask, + MaxUint8, + OneWord, + Signature_lower_v +} from "./ConsiderationConstants.sol"; + +import { + BadContractSignature_error_length, + BadContractSignature_error_selector, + BadSignatureV_error_length, + BadSignatureV_error_selector, + BadSignatureV_error_v_ptr, + Error_selector_offset, + InvalidSignature_error_length, + InvalidSignature_error_selector, + InvalidSigner_error_length, + InvalidSigner_error_selector +} from "./ConsiderationErrorConstants.sol"; /** * @title SignatureVerification @@ -24,14 +50,19 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { * is not 64 or 65 bytes or if the recovered signer does not match the * supplied signer. * - * @param signer The signer for the order. - * @param digest The digest to verify the signature against. - * @param signature A signature from the signer indicating that the order - * has been approved. + * @param signer The signer for the order. + * @param digest The digest to verify signature against. + * @param originalDigest The original digest to verify signature + * against. + * @param originalSignatureLength The original signature length. + * @param signature A signature from the signer indicating + * that the order has been approved. */ function _assertValidSignature( address signer, bytes32 digest, + bytes32 originalDigest, + uint256 originalSignatureLength, bytes memory signature ) internal view { // Declare value for ecrecover equality or 1271 call success status. @@ -42,9 +73,6 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // Ensure that first word of scratch space is empty. mstore(0, 0) - // Declare value for v signature parameter. - let v - // Get the length of the signature. let signatureLength := mload(signature) @@ -79,7 +107,7 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // signature is 65 bytes, this will be the real `v` value. // If not, it will need to be modified - doing it this way // saves an extra condition. - v := byte( + let v := byte( 0, mload(add(signature, ECDSA_signature_v_offset)) ) @@ -151,6 +179,9 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // If the signature was not verified with ecrecover, try EIP1271. if iszero(success) { + // Reset the original signature length. + mstore(signature, originalSignatureLength) + // Temporarily overwrite the word before the signature length // and use it as the head of the signature input to // `isValidSignature`, which has a value of 64. @@ -168,20 +199,25 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // Cache the value currently stored at the selector pointer. let cachedWordOverwrittenBySelector := mload(selectorPtr) - // Get pointer to use for `digest` input to `isValidSignature`. - let digestPtr := sub( - signature, - EIP1271_isValidSignature_digest_negativeOffset - ) - // Cache the value currently stored at the digest pointer. - let cachedWordOverwrittenByDigest := mload(digestPtr) + let cachedWordOverwrittenByDigest := mload( + sub( + signature, + EIP1271_isValidSignature_digest_negativeOffset + ) + ) // Write the selector first, since it overlaps the digest. mstore(selectorPtr, EIP1271_isValidSignature_selector) - // Next, write the digest. - mstore(digestPtr, digest) + // Next, write the original digest. + mstore( + sub( + signature, + EIP1271_isValidSignature_digest_negativeOffset + ), + originalDigest + ) // Call signer with `isValidSignature` to validate signature. success := staticcall( @@ -189,7 +225,7 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { signer, selectorPtr, add( - signatureLength, + originalSignatureLength, EIP1271_isValidSignature_calldata_baseLength ), 0, @@ -204,30 +240,82 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // Revert with bad 1271 signature if signer has code. if extcodesize(signer) { // Bad contract signature. - mstore(0, BadContractSignature_error_signature) - revert(0, BadContractSignature_error_length) + // Store left-padded selector with push4, mem[28:32] + mstore(0, BadContractSignature_error_selector) + + // revert(abi.encodeWithSignature( + // "BadContractSignature()" + // )) + revert( + Error_selector_offset, + BadContractSignature_error_length + ) } // Check if signature length was invalid. if gt(sub(ECDSA_MaxLength, signatureLength), 1) { // Revert with generic invalid signature error. - mstore(0, InvalidSignature_error_signature) - revert(0, InvalidSignature_error_length) + // Store left-padded selector with push4, mem[28:32] + mstore(0, InvalidSignature_error_selector) + + // revert(abi.encodeWithSignature( + // "InvalidSignature()" + // )) + revert( + Error_selector_offset, + InvalidSignature_error_length + ) } // Check if v was invalid. - if iszero( - byte(v, ECDSA_twentySeventhAndTwentyEighthBytesSet) + if and( + eq(signatureLength, ECDSA_MaxLength), + iszero( + byte( + byte( + 0, + mload( + add( + signature, + ECDSA_signature_v_offset + ) + ) + ), + ECDSA_twentySeventhAndTwentyEighthBytesSet + ) + ) ) { // Revert with invalid v value. - mstore(0, BadSignatureV_error_signature) - mstore(BadSignatureV_error_offset, v) - revert(0, BadSignatureV_error_length) + // Store left-padded selector with push4, mem[28:32] + mstore(0, BadSignatureV_error_selector) + mstore( + BadSignatureV_error_v_ptr, + byte( + 0, + mload( + add(signature, ECDSA_signature_v_offset) + ) + ) + ) + + // revert(abi.encodeWithSignature( + // "BadSignatureV(uint8)", v + // )) + revert( + Error_selector_offset, + BadSignatureV_error_length + ) } // Revert with generic invalid signer error message. - mstore(0, InvalidSigner_error_signature) - revert(0, InvalidSigner_error_length) + // Store left-padded selector with push4, mem[28:32] + mstore(0, InvalidSigner_error_selector) + + // revert(abi.encodeWithSignature("InvalidSigner()")) + revert( + Error_selector_offset, + InvalidSigner_error_length + ) } } @@ -235,7 +323,13 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // signature head. mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature) mstore(selectorPtr, cachedWordOverwrittenBySelector) - mstore(digestPtr, cachedWordOverwrittenByDigest) + mstore( + sub( + signature, + EIP1271_isValidSignature_digest_negativeOffset + ), + cachedWordOverwrittenByDigest + ) } } @@ -246,8 +340,10 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers { // Otherwise, revert with error indicating bad contract signature. assembly { - mstore(0, BadContractSignature_error_signature) - revert(0, BadContractSignature_error_length) + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, BadContractSignature_error_selector) + // revert(abi.encodeWithSignature("BadContractSignature()")) + revert(Error_selector_offset, BadContractSignature_error_length) } } } diff --git a/contracts/lib/TokenTransferrer.sol b/contracts/lib/TokenTransferrer.sol index 197a28355..05f1e7a0b 100644 --- a/contracts/lib/TokenTransferrer.sol +++ b/contracts/lib/TokenTransferrer.sol @@ -1,9 +1,84 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; -import "./TokenTransferrerConstants.sol"; +import { + BadReturnValueFromERC20OnTransfer_error_amount_ptr, + BadReturnValueFromERC20OnTransfer_error_from_ptr, + BadReturnValueFromERC20OnTransfer_error_length, + BadReturnValueFromERC20OnTransfer_error_selector, + BadReturnValueFromERC20OnTransfer_error_to_ptr, + BadReturnValueFromERC20OnTransfer_error_token_ptr, + BatchTransfer1155Params_amounts_head_ptr, + BatchTransfer1155Params_calldata_baseSize, + BatchTransfer1155Params_data_head_ptr, + BatchTransfer1155Params_data_length_basePtr, + BatchTransfer1155Params_ids_head_ptr, + BatchTransfer1155Params_ids_length_offset, + BatchTransfer1155Params_ids_length_ptr, + BatchTransfer1155Params_ptr, + ConduitBatch1155Transfer_amounts_length_baseOffset, + ConduitBatch1155Transfer_from_offset, + ConduitBatch1155Transfer_ids_head_offset, + ConduitBatch1155Transfer_ids_length_offset, + ConduitBatch1155Transfer_usable_head_size, + ConduitBatchTransfer_amounts_head_offset, + CostPerWord, + DefaultFreeMemoryPointer, + ERC1155_safeBatchTransferFrom_signature, + ERC1155_safeTransferFrom_amount_ptr, + ERC1155_safeTransferFrom_data_length_offset, + ERC1155_safeTransferFrom_data_length_ptr, + ERC1155_safeTransferFrom_data_offset_ptr, + ERC1155_safeTransferFrom_from_ptr, + ERC1155_safeTransferFrom_id_ptr, + ERC1155_safeTransferFrom_length, + ERC1155_safeTransferFrom_sig_ptr, + ERC1155_safeTransferFrom_signature, + ERC1155_safeTransferFrom_to_ptr, + ERC1155BatchTransferGenericFailure_error_signature, + ERC1155BatchTransferGenericFailure_ids_offset, + ERC1155BatchTransferGenericFailure_token_ptr, + ERC20_transferFrom_amount_ptr, + ERC20_transferFrom_from_ptr, + ERC20_transferFrom_length, + ERC20_transferFrom_sig_ptr, + ERC20_transferFrom_signature, + ERC20_transferFrom_to_ptr, + ERC721_transferFrom_from_ptr, + ERC721_transferFrom_id_ptr, + ERC721_transferFrom_length, + ERC721_transferFrom_sig_ptr, + ERC721_transferFrom_signature, + ERC721_transferFrom_to_ptr, + ExtraGasBuffer, + FreeMemoryPointerSlot, + Generic_error_selector_offset, + Invalid1155BatchTransferEncoding_length, + Invalid1155BatchTransferEncoding_ptr, + Invalid1155BatchTransferEncoding_selector, + MemoryExpansionCoefficientShift, + NoContract_error_account_ptr, + NoContract_error_length, + NoContract_error_selector, + OneWord, + OneWordShift, + Slot0x80, + Slot0xA0, + Slot0xC0, + ThirtyOneBytes, + TokenTransferGenericFailure_err_identifier_ptr, + TokenTransferGenericFailure_error_amount_ptr, + TokenTransferGenericFailure_error_from_ptr, + TokenTransferGenericFailure_error_identifier_ptr, + TokenTransferGenericFailure_error_length, + TokenTransferGenericFailure_error_selector, + TokenTransferGenericFailure_error_to_ptr, + TokenTransferGenericFailure_error_token_ptr, + TwoWords, + TwoWordsShift, + ZeroSlot +} from "./TokenTransferrerConstants.sol"; -// prettier-ignore import { TokenTransferrerErrors } from "../interfaces/TokenTransferrerErrors.sol"; @@ -100,16 +175,16 @@ contract TokenTransferrer is TokenTransferrerErrors { // necessary. Start by computing the word size // of returndata and allocated memory. Round up // to the nearest full word. - let returnDataWords := div( - add(returndatasize(), AlmostOneWord), - OneWord + let returnDataWords := shr( + OneWordShift, + add(returndatasize(), ThirtyOneBytes) ) // Note: use the free memory pointer in place of // msize() to work around a Yul warning that // prevents accessing msize directly when the IR // pipeline is activated. - let msizeWords := div(memPointer, OneWord) + let msizeWords := shr(OneWordShift, memPointer) // Next, compute the cost of the returndatacopy. let cost := mul(CostPerWord, returnDataWords) @@ -126,15 +201,15 @@ contract TokenTransferrer is TokenTransferrerErrors { ), CostPerWord ), - div( + shr( + MemoryExpansionCoefficientShift, sub( mul( returnDataWords, returnDataWords ), mul(msizeWords, msizeWords) - ), - MemoryExpansionCoefficient + ) ) ) ) @@ -154,10 +229,10 @@ contract TokenTransferrer is TokenTransferrerErrors { } } - // Otherwise revert with a generic error message. + // Store left-padded selector with push4, mem[28:32] mstore( - TokenTransferGenericFailure_error_sig_ptr, - TokenTransferGenericFailure_error_signature + 0, + TokenTransferGenericFailure_error_selector ) mstore( TokenTransferGenericFailure_error_token_ptr, @@ -168,22 +243,33 @@ contract TokenTransferrer is TokenTransferrerErrors { from ) mstore(TokenTransferGenericFailure_error_to_ptr, to) - mstore(TokenTransferGenericFailure_error_id_ptr, 0) + mstore( + TokenTransferGenericFailure_err_identifier_ptr, + 0 + ) mstore( TokenTransferGenericFailure_error_amount_ptr, amount ) + + // revert(abi.encodeWithSignature( + // "TokenTransferGenericFailure( + // address,address,address,uint256,uint256 + // )", token, from, to, identifier, amount + // )) revert( - TokenTransferGenericFailure_error_sig_ptr, + Generic_error_selector_offset, TokenTransferGenericFailure_error_length ) } // Otherwise revert with a message about the token // returning false or non-compliant return values. + + // Store left-padded selector with push4, mem[28:32] mstore( - BadReturnValueFromERC20OnTransfer_error_sig_ptr, - BadReturnValueFromERC20OnTransfer_error_signature + 0, + BadReturnValueFromERC20OnTransfer_error_selector ) mstore( BadReturnValueFromERC20OnTransfer_error_token_ptr, @@ -201,16 +287,30 @@ contract TokenTransferrer is TokenTransferrerErrors { BadReturnValueFromERC20OnTransfer_error_amount_ptr, amount ) + + // revert(abi.encodeWithSignature( + // "BadReturnValueFromERC20OnTransfer( + // address,address,address,uint256 + // )", token, from, to, amount + // )) revert( - BadReturnValueFromERC20OnTransfer_error_sig_ptr, + Generic_error_selector_offset, BadReturnValueFromERC20OnTransfer_error_length ) } // Otherwise, revert with error about token not having code: - mstore(NoContract_error_sig_ptr, NoContract_error_signature) - mstore(NoContract_error_token_ptr, token) - revert(NoContract_error_sig_ptr, NoContract_error_length) + // Store left-padded selector with push4, mem[28:32] + mstore(0, NoContract_error_selector) + mstore(NoContract_error_account_ptr, token) + + // revert(abi.encodeWithSignature( + // "NoContract(address)", account + // )) + revert( + Generic_error_selector_offset, + NoContract_error_length + ) } // Otherwise, the token just returned no data despite the call @@ -248,9 +348,14 @@ contract TokenTransferrer is TokenTransferrerErrors { assembly { // If the token has no code, revert. if iszero(extcodesize(token)) { - mstore(NoContract_error_sig_ptr, NoContract_error_signature) - mstore(NoContract_error_token_ptr, token) - revert(NoContract_error_sig_ptr, NoContract_error_length) + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, NoContract_error_selector) + mstore(NoContract_error_account_ptr, token) + + // revert(abi.encodeWithSignature( + // "NoContract(address)", account + // )) + revert(Generic_error_selector_offset, NoContract_error_length) } // The free memory pointer memory slot will be used when populating @@ -283,15 +388,15 @@ contract TokenTransferrer is TokenTransferrerErrors { // returndata while expanding memory where necessary. Start // by computing word size of returndata & allocated memory. // Round up to the nearest full word. - let returnDataWords := div( - add(returndatasize(), AlmostOneWord), - OneWord + let returnDataWords := shr( + OneWordShift, + add(returndatasize(), ThirtyOneBytes) ) // Note: use the free memory pointer in place of msize() to // work around a Yul warning that prevents accessing msize // directly when the IR pipeline is activated. - let msizeWords := div(memPointer, OneWord) + let msizeWords := shr(OneWordShift, memPointer) // Next, compute the cost of the returndatacopy. let cost := mul(CostPerWord, returnDataWords) @@ -305,12 +410,12 @@ contract TokenTransferrer is TokenTransferrerErrors { sub(returnDataWords, msizeWords), CostPerWord ), - div( + shr( + MemoryExpansionCoefficientShift, sub( mul(returnDataWords, returnDataWords), mul(msizeWords, msizeWords) - ), - MemoryExpansionCoefficient + ) ) ) ) @@ -329,17 +434,24 @@ contract TokenTransferrer is TokenTransferrerErrors { } // Otherwise revert with a generic error message. - mstore( - TokenTransferGenericFailure_error_sig_ptr, - TokenTransferGenericFailure_error_signature - ) + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, TokenTransferGenericFailure_error_selector) mstore(TokenTransferGenericFailure_error_token_ptr, token) mstore(TokenTransferGenericFailure_error_from_ptr, from) mstore(TokenTransferGenericFailure_error_to_ptr, to) - mstore(TokenTransferGenericFailure_error_id_ptr, identifier) + mstore( + TokenTransferGenericFailure_error_identifier_ptr, + identifier + ) mstore(TokenTransferGenericFailure_error_amount_ptr, 1) + + // revert(abi.encodeWithSignature( + // "TokenTransferGenericFailure( + // address,address,address,uint256,uint256 + // )", token, from, to, identifier, amount + // )) revert( - TokenTransferGenericFailure_error_sig_ptr, + Generic_error_selector_offset, TokenTransferGenericFailure_error_length ) } @@ -376,9 +488,14 @@ contract TokenTransferrer is TokenTransferrerErrors { assembly { // If the token has no code, revert. if iszero(extcodesize(token)) { - mstore(NoContract_error_sig_ptr, NoContract_error_signature) - mstore(NoContract_error_token_ptr, token) - revert(NoContract_error_sig_ptr, NoContract_error_length) + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, NoContract_error_selector) + mstore(NoContract_error_account_ptr, token) + + // revert(abi.encodeWithSignature( + // "NoContract(address)", account + // )) + revert(Generic_error_selector_offset, NoContract_error_length) } // The following memory slots will be used when populating call data @@ -423,15 +540,15 @@ contract TokenTransferrer is TokenTransferrerErrors { // returndata while expanding memory where necessary. Start // by computing word size of returndata & allocated memory. // Round up to the nearest full word. - let returnDataWords := div( - add(returndatasize(), AlmostOneWord), - OneWord + let returnDataWords := shr( + OneWordShift, + add(returndatasize(), ThirtyOneBytes) ) // Note: use the free memory pointer in place of msize() to // work around a Yul warning that prevents accessing msize // directly when the IR pipeline is activated. - let msizeWords := div(memPointer, OneWord) + let msizeWords := shr(OneWordShift, memPointer) // Next, compute the cost of the returndatacopy. let cost := mul(CostPerWord, returnDataWords) @@ -445,12 +562,12 @@ contract TokenTransferrer is TokenTransferrerErrors { sub(returnDataWords, msizeWords), CostPerWord ), - div( + shr( + MemoryExpansionCoefficientShift, sub( mul(returnDataWords, returnDataWords), mul(msizeWords, msizeWords) - ), - MemoryExpansionCoefficient + ) ) ) ) @@ -469,17 +586,25 @@ contract TokenTransferrer is TokenTransferrerErrors { } // Otherwise revert with a generic error message. - mstore( - TokenTransferGenericFailure_error_sig_ptr, - TokenTransferGenericFailure_error_signature - ) + + // Store left-padded selector with push4, mem[28:32] = selector + mstore(0, TokenTransferGenericFailure_error_selector) mstore(TokenTransferGenericFailure_error_token_ptr, token) mstore(TokenTransferGenericFailure_error_from_ptr, from) mstore(TokenTransferGenericFailure_error_to_ptr, to) - mstore(TokenTransferGenericFailure_error_id_ptr, identifier) + mstore( + TokenTransferGenericFailure_error_identifier_ptr, + identifier + ) mstore(TokenTransferGenericFailure_error_amount_ptr, amount) + + // revert(abi.encodeWithSignature( + // "TokenTransferGenericFailure( + // address,address,address,uint256,uint256 + // )", token, from, to, identifier, amount + // )) revert( - TokenTransferGenericFailure_error_sig_ptr, + Generic_error_selector_offset, TokenTransferGenericFailure_error_length ) } @@ -553,9 +678,17 @@ contract TokenTransferrer is TokenTransferrerErrors { // If the token has no code, revert. if iszero(extcodesize(token)) { - mstore(NoContract_error_sig_ptr, NoContract_error_signature) - mstore(NoContract_error_token_ptr, token) - revert(NoContract_error_sig_ptr, NoContract_error_length) + // Store left-padded selector with push4, mem[28:32] + mstore(0, NoContract_error_selector) + mstore(NoContract_error_account_ptr, token) + + // revert(abi.encodeWithSignature( + // "NoContract(address)", account + // )) + revert( + Generic_error_selector_offset, + NoContract_error_length + ) } // Get the total number of supplied ids. @@ -566,7 +699,7 @@ contract TokenTransferrer is TokenTransferrerErrors { // Determine the expected offset for the amounts array. let expectedAmountsOffset := add( ConduitBatch1155Transfer_amounts_length_baseOffset, - mul(idsLength, OneWord) + shl(OneWordShift, idsLength) ) // Validate struct encoding. @@ -604,10 +737,15 @@ contract TokenTransferrer is TokenTransferrerErrors { // Revert with an error if the encoding is not valid. if invalidEncoding { + // Store left-padded selector with push4, mem[28:32] mstore( Invalid1155BatchTransferEncoding_ptr, Invalid1155BatchTransferEncoding_selector ) + + // revert(abi.encodeWithSignature( + // "Invalid1155BatchTransferEncoding()" + // )) revert( Invalid1155BatchTransferEncoding_ptr, Invalid1155BatchTransferEncoding_length @@ -626,7 +764,10 @@ contract TokenTransferrer is TokenTransferrerErrors { // Determine size of calldata required for ids and amounts. Note // that the size includes both lengths as well as the data. - let idsAndAmountsSize := add(TwoWords, mul(idsLength, TwoWords)) + let idsAndAmountsSize := add( + TwoWords, + shl(TwoWordsShift, idsLength) + ) // Update the offset for the data array in memory. mstore( @@ -679,9 +820,9 @@ contract TokenTransferrer is TokenTransferrerErrors { // returndata while expanding memory where necessary. // Start by computing word size of returndata and // allocated memory. Round up to the nearest full word. - let returnDataWords := div( - add(returndatasize(), AlmostOneWord), - OneWord + let returnDataWords := shr( + OneWordShift, + add(returndatasize(), ThirtyOneBytes) ) // Note: use transferDataSize in place of msize() to @@ -692,7 +833,7 @@ contract TokenTransferrer is TokenTransferrerErrors { // manually and does not update it, and transferDataSize // should be the largest memory value used (unless a // previous batch was larger). - let msizeWords := div(transferDataSize, OneWord) + let msizeWords := shr(OneWordShift, transferDataSize) // Next, compute the cost of the returndatacopy. let cost := mul(CostPerWord, returnDataWords) @@ -706,15 +847,15 @@ contract TokenTransferrer is TokenTransferrerErrors { sub(returnDataWords, msizeWords), CostPerWord ), - div( + shr( + MemoryExpansionCoefficientShift, sub( mul( returnDataWords, returnDataWords ), mul(msizeWords, msizeWords) - ), - MemoryExpansionCoefficient + ) ) ) ) diff --git a/contracts/lib/TokenTransferrerConstants.sol b/contracts/lib/TokenTransferrerConstants.sol index adb01c58d..111a756c9 100644 --- a/contracts/lib/TokenTransferrerConstants.sol +++ b/contracts/lib/TokenTransferrerConstants.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; /* * -------------------------- Disambiguation & Other Notes --------------------- @@ -8,7 +8,7 @@ pragma solidity >=0.8.7; * offset or pointer to the body of a dynamic type. In calldata, the head * is always an offset (relative to the parent object), while in memory, * the head is always the pointer to the body. More information found here: - * https://docs.soliditylang.org/en/v0.8.14/abi-spec.html#argument-encoding + * https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#argument-encoding * - Note that the length of an array is separate from and precedes the * head of the array. * @@ -33,11 +33,14 @@ pragma solidity >=0.8.7; * codebase but have been left in for readability. */ -uint256 constant AlmostOneWord = 0x1f; +uint256 constant ThirtyOneBytes = 0x1f; uint256 constant OneWord = 0x20; uint256 constant TwoWords = 0x40; uint256 constant ThreeWords = 0x60; +uint256 constant OneWordShift = 0x5; +uint256 constant TwoWordsShift = 0x6; + uint256 constant FreeMemoryPointerSlot = 0x40; uint256 constant ZeroSlot = 0x60; uint256 constant DefaultFreeMemoryPointer = 0x80; @@ -46,6 +49,8 @@ uint256 constant Slot0x80 = 0x80; uint256 constant Slot0xA0 = 0xa0; uint256 constant Slot0xC0 = 0xc0; +uint256 constant Generic_error_selector_offset = 0x1c; + // abi.encodeWithSignature("transferFrom(address,address,uint256)") uint256 constant ERC20_transferFrom_signature = ( 0x23b872dd00000000000000000000000000000000000000000000000000000000 @@ -79,59 +84,61 @@ uint256 constant ERC1155_safeBatchTransferFrom_signature = ( 0x2eb2c2d600000000000000000000000000000000000000000000000000000000 ); -bytes4 constant ERC1155_safeBatchTransferFrom_selector = bytes4( - bytes32(ERC1155_safeBatchTransferFrom_signature) -); +// bytes4 constant ERC1155_safeBatchTransferFrom_selector = bytes4( +// bytes32(ERC1155_safeBatchTransferFrom_signature) +// ); -uint256 constant ERC721_transferFrom_signature = ERC20_transferFrom_signature; +uint256 constant ERC721_transferFrom_signature = ( + 0x23b872dd00000000000000000000000000000000000000000000000000000000 +); uint256 constant ERC721_transferFrom_sig_ptr = 0x0; uint256 constant ERC721_transferFrom_from_ptr = 0x04; uint256 constant ERC721_transferFrom_to_ptr = 0x24; uint256 constant ERC721_transferFrom_id_ptr = 0x44; uint256 constant ERC721_transferFrom_length = 0x64; // 4 + 32 * 3 == 100 -// abi.encodeWithSignature("NoContract(address)") -uint256 constant NoContract_error_signature = ( - 0x5f15d67200000000000000000000000000000000000000000000000000000000 -); -uint256 constant NoContract_error_sig_ptr = 0x0; -uint256 constant NoContract_error_token_ptr = 0x4; -uint256 constant NoContract_error_length = 0x24; // 4 + 32 == 36 +/* + * error NoContract(address account) + * - Defined in TokenTransferrerErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x00: account + * Revert buffer is memory[0x1c:0x40] + */ +uint256 constant NoContract_error_selector = 0x5f15d672; +uint256 constant NoContract_error_account_ptr = 0x20; +uint256 constant NoContract_error_length = 0x24; -// abi.encodeWithSignature( -// "TokenTransferGenericFailure(address,address,address,uint256,uint256)" -// ) -uint256 constant TokenTransferGenericFailure_error_signature = ( - 0xf486bc8700000000000000000000000000000000000000000000000000000000 -); -uint256 constant TokenTransferGenericFailure_error_sig_ptr = 0x0; -uint256 constant TokenTransferGenericFailure_error_token_ptr = 0x4; -uint256 constant TokenTransferGenericFailure_error_from_ptr = 0x24; -uint256 constant TokenTransferGenericFailure_error_to_ptr = 0x44; -uint256 constant TokenTransferGenericFailure_error_id_ptr = 0x64; -uint256 constant TokenTransferGenericFailure_error_amount_ptr = 0x84; - -// 4 + 32 * 5 == 164 +/* + * error TokenTransferGenericFailure( + * address token, + * address from, + * address to, + * uint256 identifier, + * uint256 amount + * ) + * - Defined in TokenTransferrerErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x20: token + * - 0x40: from + * - 0x60: to + * - 0x80: identifier + * - 0xa0: amount + * Revert buffer is memory[0x1c:0xc0] + */ +uint256 constant TokenTransferGenericFailure_error_selector = 0xf486bc87; +uint256 constant TokenTransferGenericFailure_error_token_ptr = 0x20; +uint256 constant TokenTransferGenericFailure_error_from_ptr = 0x40; +uint256 constant TokenTransferGenericFailure_error_to_ptr = 0x60; +uint256 constant TokenTransferGenericFailure_error_identifier_ptr = 0x80; +uint256 constant TokenTransferGenericFailure_err_identifier_ptr = 0x80; +uint256 constant TokenTransferGenericFailure_error_amount_ptr = 0xa0; uint256 constant TokenTransferGenericFailure_error_length = 0xa4; -// abi.encodeWithSignature( -// "BadReturnValueFromERC20OnTransfer(address,address,address,uint256)" -// ) -uint256 constant BadReturnValueFromERC20OnTransfer_error_signature = ( - 0x9889192300000000000000000000000000000000000000000000000000000000 -); -uint256 constant BadReturnValueFromERC20OnTransfer_error_sig_ptr = 0x0; -uint256 constant BadReturnValueFromERC20OnTransfer_error_token_ptr = 0x4; -uint256 constant BadReturnValueFromERC20OnTransfer_error_from_ptr = 0x24; -uint256 constant BadReturnValueFromERC20OnTransfer_error_to_ptr = 0x44; -uint256 constant BadReturnValueFromERC20OnTransfer_error_amount_ptr = 0x64; - -// 4 + 32 * 4 == 132 -uint256 constant BadReturnValueFromERC20OnTransfer_error_length = 0x84; - uint256 constant ExtraGasBuffer = 0x20; -uint256 constant CostPerWord = 3; -uint256 constant MemoryExpansionCoefficient = 0x200; +uint256 constant CostPerWord = 0x3; +uint256 constant MemoryExpansionCoefficientShift = 0x9; // Values are offset by 32 bytes in order to write the token to the beginning // in the event of a revert @@ -145,17 +152,17 @@ uint256 constant BatchTransfer1155Params_calldata_baseSize = 0xc4; uint256 constant BatchTransfer1155Params_ids_length_ptr = 0xc4; uint256 constant BatchTransfer1155Params_ids_length_offset = 0xa0; -uint256 constant BatchTransfer1155Params_amounts_length_baseOffset = 0xc0; -uint256 constant BatchTransfer1155Params_data_length_baseOffset = 0xe0; +// uint256 constant BatchTransfer1155Params_amounts_length_baseOffset = 0xc0; +// uint256 constant BatchTransfer1155Params_data_length_baseOffset = 0xe0; uint256 constant ConduitBatch1155Transfer_usable_head_size = 0x80; uint256 constant ConduitBatch1155Transfer_from_offset = 0x20; uint256 constant ConduitBatch1155Transfer_ids_head_offset = 0x60; -uint256 constant ConduitBatch1155Transfer_amounts_head_offset = 0x80; +// uint256 constant ConduitBatch1155Transfer_amounts_head_offset = 0x80; uint256 constant ConduitBatch1155Transfer_ids_length_offset = 0xa0; uint256 constant ConduitBatch1155Transfer_amounts_length_baseOffset = 0xc0; -uint256 constant ConduitBatch1155Transfer_calldata_baseSize = 0xc0; +// uint256 constant ConduitBatch1155Transfer_calldata_baseSize = 0xc0; // Note: abbreviated version of above constant to adhere to line length limit. uint256 constant ConduitBatchTransfer_amounts_head_offset = 0x80; @@ -171,3 +178,23 @@ uint256 constant ERC1155BatchTransferGenericFailure_error_signature = ( ); uint256 constant ERC1155BatchTransferGenericFailure_token_ptr = 0x04; uint256 constant ERC1155BatchTransferGenericFailure_ids_offset = 0xc0; + +/* + * error BadReturnValueFromERC20OnTransfer( + * address token, address from, address to, uint256 amount + * ) + * - Defined in TokenTransferrerErrors.sol + * Memory layout: + * - 0x00: Left-padded selector (data begins at 0x1c) + * - 0x00: token + * - 0x20: from + * - 0x40: to + * - 0x60: amount + * Revert buffer is memory[0x1c:0xa0] + */ +uint256 constant BadReturnValueFromERC20OnTransfer_error_selector = 0x98891923; +uint256 constant BadReturnValueFromERC20OnTransfer_error_token_ptr = 0x20; +uint256 constant BadReturnValueFromERC20OnTransfer_error_from_ptr = 0x40; +uint256 constant BadReturnValueFromERC20OnTransfer_error_to_ptr = 0x60; +uint256 constant BadReturnValueFromERC20OnTransfer_error_amount_ptr = 0x80; +uint256 constant BadReturnValueFromERC20OnTransfer_error_length = 0x84; diff --git a/contracts/lib/TypehashDirectory.sol b/contracts/lib/TypehashDirectory.sol new file mode 100644 index 000000000..9fd733109 --- /dev/null +++ b/contracts/lib/TypehashDirectory.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { + FreeMemoryPointerSlot, + OneWord, + OneWordShift, + ThirtyOneBytes +} from "../lib/ConsiderationConstants.sol"; + +/** + * @title TypehashDirectory + * @notice The typehash directory contains 24 bulk order EIP-712 typehashes, + * depending on the height of the tree in each bulk order payload, as + * its runtime code (with an invalid opcode prefix so that the contract + * cannot be called normally). This runtime code is designed to be read + * from by Seaport using `extcodecopy` while verifying bulk signatures. + */ +contract TypehashDirectory { + // Encodes "[2]" for use in deriving typehashes. + bytes3 internal constant twoSubstring = 0x5B325D; + uint256 internal constant twoSubstringLength = 0x3; + + // Dictates maximum bulk order group size; 24 => 2^24 => 16,777,216 orders. + uint256 internal constant MaxTreeHeight = 0x18; + + uint256 internal constant InvalidOpcode = 0xfe; + + /** + * @dev Derive 24 bulk order EIP-712 typehashes, one for each supported + * tree height from 1 to 24, and write them to runtime code. + */ + constructor() { + // Declare an array where each type hash will be written. + bytes32[] memory typeHashes = new bytes32[](MaxTreeHeight); + + // Derive a string of 24 "[2]" substrings. + bytes memory brackets = getMaxTreeBrackets(MaxTreeHeight); + + // Derive a string of subtypes for the order parameters. + bytes memory subTypes = getTreeSubTypes(); + + // Cache memory pointer before each loop so memory doesn't expand by the + // full string size on each loop. + uint256 freeMemoryPointer; + assembly { + freeMemoryPointer := mload(FreeMemoryPointerSlot) + } + + // Iterate over each tree height. + for (uint256 i = 0; i < MaxTreeHeight; ) { + // The actual height is one greater than its respective index. + uint256 height = i + 1; + + // Slice brackets length to size needed for `height`. + assembly { + mstore(brackets, mul(twoSubstringLength, height)) + } + + // Encode the type string for the BulkOrder struct. + bytes memory bulkOrderTypeString = bytes.concat( + "BulkOrder(OrderComponents", + brackets, + " tree)", + subTypes + ); + + // Derive EIP712 type hash. + bytes32 typeHash = keccak256(bulkOrderTypeString); + typeHashes[i] = typeHash; + + // Reset the free memory pointer. + assembly { + mstore(FreeMemoryPointerSlot, freeMemoryPointer) + } + + unchecked { + ++i; + } + } + + assembly { + // Overwrite length with zero to give the contract an INVALID prefix + // and deploy the type hashes array as a contract. + mstore(typeHashes, InvalidOpcode) + + return( + add(typeHashes, ThirtyOneBytes), + add(shl(OneWordShift, MaxTreeHeight), 1) + ) + } + } + + /** + * @dev Internal pure function that returns a string of "[2]" substrings, + * with a number of substrings equal to the provided height. + * + * @param maxHeight The number of "[2]" substrings to include. + * + * @return A bytes array representing the string. + */ + function getMaxTreeBrackets( + uint256 maxHeight + ) internal pure returns (bytes memory) { + bytes memory suffixes = new bytes(twoSubstringLength * maxHeight); + assembly { + // Retrieve the pointer to the array head. + let ptr := add(suffixes, OneWord) + + // Derive the terminal pointer. + let endPtr := add(ptr, mul(maxHeight, twoSubstringLength)) + + // Iterate over each pointer until terminal pointer is reached. + for { + + } lt(ptr, endPtr) { + ptr := add(ptr, twoSubstringLength) + } { + // Insert "[2]" substring directly at current pointer location. + mstore(ptr, twoSubstring) + } + } + + // Return the fully populated array of substrings. + return suffixes; + } + + /** + * @dev Internal pure function that returns a string of subtypes used in + * generating bulk order EIP-712 typehashes. + * + * @return A bytes array representing the string. + */ + function getTreeSubTypes() internal pure returns (bytes memory) { + // Construct the OfferItem type string. + bytes memory offerItemTypeString = bytes( + "OfferItem(" + "uint8 itemType," + "address token," + "uint256 identifierOrCriteria," + "uint256 startAmount," + "uint256 endAmount" + ")" + ); + + // Construct the ConsiderationItem type string. + bytes memory considerationItemTypeString = bytes( + "ConsiderationItem(" + "uint8 itemType," + "address token," + "uint256 identifierOrCriteria," + "uint256 startAmount," + "uint256 endAmount," + "address recipient" + ")" + ); + + // Construct the OrderComponents type string, not including the above. + bytes memory orderComponentsPartialTypeString = bytes( + "OrderComponents(" + "address offerer," + "address zone," + "OfferItem[] offer," + "ConsiderationItem[] consideration," + "uint8 orderType," + "uint256 startTime," + "uint256 endTime," + "bytes32 zoneHash," + "uint256 salt," + "bytes32 conduitKey," + "uint256 counter" + ")" + ); + + // Return the combined string. + return + bytes.concat( + considerationItemTypeString, + offerItemTypeString, + orderComponentsPartialTypeString + ); + } +} diff --git a/contracts/lib/Verifiers.sol b/contracts/lib/Verifiers.sol index baba8da90..65fd80f0e 100644 --- a/contracts/lib/Verifiers.sol +++ b/contracts/lib/Verifiers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity 0.8.17; import { OrderStatus } from "./ConsiderationStructs.sol"; @@ -7,6 +7,27 @@ import { Assertions } from "./Assertions.sol"; import { SignatureVerification } from "./SignatureVerification.sol"; +import { + _revertInvalidTime, + _revertOrderAlreadyFilled, + _revertOrderIsCancelled, + _revertOrderPartiallyFilled +} from "./ConsiderationErrors.sol"; + +import { + BulkOrderProof_keyShift, + BulkOrderProof_keySize, + BulkOrderProof_lengthAdjustmentBeforeMask, + BulkOrderProof_lengthRangeAfterMask, + BulkOrderProof_minSize, + BulkOrderProof_rangeSize, + ECDSA_MaxLength, + OneWord, + OneWordShift, + ThirtyOneBytes, + TwoWords +} from "./ConsiderationConstants.sol"; + /** * @title Verifiers * @author 0age @@ -39,26 +60,25 @@ contract Verifiers is Assertions, SignatureVerification { uint256 endTime, bool revertOnInvalid ) internal view returns (bool valid) { - // Revert if order's timespan hasn't started yet or has already ended. - if (startTime > block.timestamp || endTime <= block.timestamp) { - // Only revert if revertOnInvalid has been supplied as true. - if (revertOnInvalid) { - revert InvalidTime(); - } - - // Return false as the order is invalid. - return false; + // Mark as valid if order has started and has not already ended. + assembly { + valid := and( + iszero(gt(startTime, timestamp())), + gt(endTime, timestamp()) + ) } - // Return true as the order time is valid. - valid = true; + // Only revert on invalid if revertOnInvalid has been supplied as true. + if (revertOnInvalid && !valid) { + _revertInvalidTime(startTime, endTime); + } } /** * @dev Internal view function to verify the signature of an order. An * ERC-1271 fallback will be attempted if either the signature length - * is not 32 or 33 bytes or if the recovered signer does not match the - * supplied offerer. Note that in cases where a 32 or 33 byte signature + * is not 64 or 65 bytes or if the recovered signer does not match the + * supplied offerer. Note that in cases where a 64 or 65 byte signature * is supplied, only standard ECDSA signatures that recover to a * non-zero address are supported. * @@ -73,15 +93,144 @@ contract Verifiers is Assertions, SignatureVerification { bytes memory signature ) internal view { // Skip signature verification if the offerer is the caller. - if (offerer == msg.sender) { + if (_unmaskedAddressComparison(offerer, msg.sender)) { return; } - // Derive EIP-712 digest using the domain separator and the order hash. - bytes32 digest = _deriveEIP712Digest(_domainSeparator(), orderHash); + bytes32 domainSeparator = _domainSeparator(); + + // Derive original EIP-712 digest using domain separator and order hash. + bytes32 originalDigest = _deriveEIP712Digest( + domainSeparator, + orderHash + ); + + uint256 originalSignatureLength = signature.length; + + bytes32 digest; + if (_isValidBulkOrderSize(originalSignatureLength)) { + // Rederive order hash and digest using bulk order proof. + (orderHash) = _computeBulkOrderProof(signature, orderHash); + digest = _deriveEIP712Digest(domainSeparator, orderHash); + } else { + digest = originalDigest; + } // Ensure that the signature for the digest is valid for the offerer. - _assertValidSignature(offerer, digest, signature); + _assertValidSignature( + offerer, + digest, + originalDigest, + originalSignatureLength, + signature + ); + } + + /** + * @dev Determines whether the specified bulk order size is valid. + * + * @param signatureLength The signature length of the bulk order to check. + * + * @return validLength True if bulk order size is valid, false otherwise. + */ + function _isValidBulkOrderSize( + uint256 signatureLength + ) internal pure returns (bool validLength) { + // Utilize assembly to validate the length; the equivalent logic is + // (64 + x) + 3 + 32y where (0 <= x <= 1) and (1 <= y <= 24). + assembly { + validLength := and( + lt( + sub(signatureLength, BulkOrderProof_minSize), + BulkOrderProof_rangeSize + ), + lt( + and( + add( + signatureLength, + BulkOrderProof_lengthAdjustmentBeforeMask + ), + ThirtyOneBytes + ), + BulkOrderProof_lengthRangeAfterMask + ) + ) + } + } + + /** + * @dev Computes the bulk order hash for the specified proof and leaf. Note + * that if an index that exceeds the number of orders in the bulk order + * payload will instead "wrap around" and refer to an earlier index. + * + * @param proofAndSignature The proof and signature of the bulk order. + * @param leaf The leaf of the bulk order tree. + * + * @return bulkOrderHash The bulk order hash. + */ + function _computeBulkOrderProof( + bytes memory proofAndSignature, + bytes32 leaf + ) internal view returns (bytes32 bulkOrderHash) { + // Declare arguments for the root hash and the height of the proof. + bytes32 root; + uint256 height; + + // Utilize assembly to efficiently derive the root hash using the proof. + assembly { + // Retrieve the length of the proof, key, and signature combined. + let fullLength := mload(proofAndSignature) + + // If proofAndSignature has odd length, it is a compact signature + // with 64 bytes. + let signatureLength := sub(ECDSA_MaxLength, and(fullLength, 1)) + + // Derive height (or depth of tree) with signature and proof length. + height := shr(OneWordShift, sub(fullLength, signatureLength)) + + // Update the length in memory to only include the signature. + mstore(proofAndSignature, signatureLength) + + // Derive the pointer for the key using the signature length. + let keyPtr := add(proofAndSignature, add(OneWord, signatureLength)) + + // Retrieve the three-byte key using the derived pointer. + let key := shr(BulkOrderProof_keyShift, mload(keyPtr)) + + /// Retrieve pointer to first proof element by applying a constant + // for the key size to the derived key pointer. + let proof := add(keyPtr, BulkOrderProof_keySize) + + // Compute level 1. + let scratchPtr1 := shl(OneWordShift, and(key, 1)) + mstore(scratchPtr1, leaf) + mstore(xor(scratchPtr1, OneWord), mload(proof)) + + // Compute remaining proofs. + for { + let i := 1 + } lt(i, height) { + i := add(i, 1) + } { + proof := add(proof, OneWord) + let scratchPtr := shl(OneWordShift, and(shr(i, key), 1)) + mstore(scratchPtr, keccak256(0, TwoWords)) + mstore(xor(scratchPtr, OneWord), mload(proof)) + } + + // Compute root hash. + root := keccak256(0, TwoWords) + } + + // Retrieve appropriate typehash from runtime storage based on height. + bytes32 rootTypeHash = _lookupBulkOrderTypehash(height); + + // Use the typehash and the root hash to derive final bulk order hash. + assembly { + mstore(0, rootTypeHash) + mstore(OneWord, root) + bulkOrderHash := keccak256(0, TwoWords) + } } /** @@ -109,7 +258,7 @@ contract Verifiers is Assertions, SignatureVerification { if (orderStatus.isCancelled) { // Only revert if revertOnInvalid has been supplied as true. if (revertOnInvalid) { - revert OrderIsCancelled(orderHash); + _revertOrderIsCancelled(orderHash); } // Return false as the order status is invalid. @@ -124,13 +273,13 @@ contract Verifiers is Assertions, SignatureVerification { // ensure the order has not been partially filled when not allowed. if (onlyAllowUnused) { // Always revert on partial fills when onlyAllowUnused is true. - revert OrderPartiallyFilled(orderHash); + _revertOrderPartiallyFilled(orderHash); } // Otherwise, ensure that order has not been entirely filled. else if (orderStatusNumerator >= orderStatus.denominator) { // Only revert if revertOnInvalid has been supplied as true. if (revertOnInvalid) { - revert OrderAlreadyFilled(orderHash); + _revertOrderAlreadyFilled(orderHash); } // Return false as the order status is invalid. diff --git a/contracts/lib/ZoneInteraction.sol b/contracts/lib/ZoneInteraction.sol index fbe88a514..26868af40 100644 --- a/contracts/lib/ZoneInteraction.sol +++ b/contracts/lib/ZoneInteraction.sol @@ -1,175 +1,261 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; - -import { ZoneInterface } from "../interfaces/ZoneInterface.sol"; +pragma solidity 0.8.17; import { OrderType } from "./ConsiderationEnums.sol"; -// prettier-ignore -import { AdvancedOrder, CriteriaResolver } from "./ConsiderationStructs.sol"; +import { + AdvancedOrder, + BasicOrderParameters, + OrderParameters +} from "./ConsiderationStructs.sol"; + +import { ZoneInteractionErrors } from "../interfaces/ZoneInteractionErrors.sol"; -import "./ConsiderationConstants.sol"; +import { LowLevelHelpers } from "./LowLevelHelpers.sol"; + +import { ConsiderationEncoder } from "./ConsiderationEncoder.sol"; + +import { MemoryPointer } from "../helpers/PointerLibraries.sol"; -// prettier-ignore import { - ZoneInteractionErrors -} from "../interfaces/ZoneInteractionErrors.sol"; + ContractOrder_orderHash_offerer_shift, + MaskOverFirstFourBytes, + OneWord, + OrderParameters_zone_offset +} from "./ConsiderationConstants.sol"; -import { LowLevelHelpers } from "./LowLevelHelpers.sol"; +import { + Error_selector_offset, + InvalidContractOrder_error_selector, + InvalidRestrictedOrder_error_length, + InvalidRestrictedOrder_error_orderHash_ptr, + InvalidRestrictedOrder_error_selector +} from "./ConsiderationErrorConstants.sol"; /** * @title ZoneInteraction * @author 0age * @notice ZoneInteraction contains logic related to interacting with zones. */ -contract ZoneInteraction is ZoneInteractionErrors, LowLevelHelpers { +contract ZoneInteraction is + ConsiderationEncoder, + ZoneInteractionErrors, + LowLevelHelpers +{ /** - * @dev Internal view function to determine if an order has a restricted - * order type and, if so, to ensure that either the offerer or the zone - * are the fulfiller or that a staticcall to `isValidOrder` on the zone - * returns a magic value indicating that the order is currently valid. + * @dev Internal function to determine if an order has a restricted order + * type and, if so, to ensure that either the zone is the caller or + * that a call to `validateOrder` on the zone returns a magic value + * indicating that the order is currently valid. Note that contract + * orders are not accessible via the basic fulfillment method. * - * @param orderHash The hash of the order. - * @param zoneHash The hash to provide upon calling the zone. - * @param orderType The type of the order. - * @param offerer The offerer in question. - * @param zone The zone in question. + * @param orderHash The hash of the order. + * @param orderType The order type. + * @param parameters The parameters of the basic order. */ function _assertRestrictedBasicOrderValidity( bytes32 orderHash, - bytes32 zoneHash, OrderType orderType, - address offerer, - address zone - ) internal view { - // Order type 2-3 require zone or offerer be caller or zone to approve. - if ( - uint256(orderType) > 1 && - msg.sender != zone && - msg.sender != offerer - ) { - // Perform minimal staticcall to the zone. - _callIsValidOrder(zone, orderHash, offerer, zoneHash); - } - } - - function _callIsValidOrder( - address zone, - bytes32 orderHash, - address offerer, - bytes32 zoneHash - ) internal view { - // Perform minimal staticcall to the zone. - bool success = _staticcall( - zone, - abi.encodeWithSelector( - ZoneInterface.isValidOrder.selector, + BasicOrderParameters calldata parameters + ) internal { + // Order type 2-3 require zone be caller or zone to approve. + // Note that in cases where fulfiller == zone, the restricted order + // validation will be skipped. + if (_isRestrictedAndCallerNotZone(orderType, parameters.zone)) { + // Encode the `validateOrder` call in memory. + (MemoryPointer callData, uint256 size) = _encodeValidateBasicOrder( orderHash, - msg.sender, - offerer, - zoneHash - ) - ); + parameters + ); - // Ensure call was successful and returned the correct magic value. - _assertIsValidOrderStaticcallSuccess(success, orderHash); + // Perform `validateOrder` call and ensure magic value was returned. + _callAndCheckStatus( + parameters.zone, + orderHash, + callData, + size, + InvalidRestrictedOrder_error_selector + ); + } } /** - * @dev Internal view function to determine whether an order is a restricted - * order and, if so, to ensure that it was either submitted by the - * offerer or the zone for the order, or that the zone returns the - * expected magic value upon performing a staticcall to `isValidOrder` - * or `isValidOrderIncludingExtraData` depending on whether the order - * fulfillment specifies extra data or criteria resolvers. + * @dev Internal function to determine the post-execution validity of + * restricted and contract orders. Restricted orders where the caller + * is not the zone must successfully call `validateOrder` with the + * correct magic value returned. Contract orders must successfully call + * `ratifyOrder` with the correct magic value returned. * - * @param advancedOrder The advanced order in question. - * @param criteriaResolvers An array where each element contains a reference - * to a specific offer or consideration, a token - * identifier, and a proof that the supplied token - * identifier is contained in the order's merkle - * root. Note that a criteria of zero indicates - * that any (transferable) token identifier is - * valid and that no proof needs to be supplied. - * @param priorOrderHashes The order hashes of each order supplied prior to - * the current order as part of a "match" variety - * of order fulfillment (e.g. this array will be - * empty for single or "fulfill available"). - * @param orderHash The hash of the order. - * @param zoneHash The hash to provide upon calling the zone. - * @param orderType The type of the order. - * @param offerer The offerer in question. - * @param zone The zone in question. + * @param advancedOrder The advanced order in question. + * @param orderHashes The order hashes of each order included as part of + * the current fulfillment. + * @param orderHash The hash of the order. */ function _assertRestrictedAdvancedOrderValidity( AdvancedOrder memory advancedOrder, - CriteriaResolver[] memory criteriaResolvers, - bytes32[] memory priorOrderHashes, - bytes32 orderHash, - bytes32 zoneHash, - OrderType orderType, - address offerer, - address zone - ) internal view { - // Order type 2-3 require zone or offerer be caller or zone to approve. + bytes32[] memory orderHashes, + bytes32 orderHash + ) internal { + // Declare variables that will be assigned based on the order type. + address target; + uint256 errorSelector; + MemoryPointer callData; + uint256 size; + + // Retrieve the parameters of the order in question. + OrderParameters memory parameters = advancedOrder.parameters; + + // OrderType 2-3 require zone to be caller or approve via validateOrder. if ( - uint256(orderType) > 1 && - msg.sender != zone && - msg.sender != offerer + _isRestrictedAndCallerNotZone(parameters.orderType, parameters.zone) ) { - // If no extraData or criteria resolvers are supplied... - if ( - advancedOrder.extraData.length == 0 && - criteriaResolvers.length == 0 - ) { - // Perform minimal staticcall to the zone. - _callIsValidOrder(zone, orderHash, offerer, zoneHash); - } else { - // Otherwise, extra data or criteria resolvers were supplied; in - // that event, perform a more verbose staticcall to the zone. - bool success = _staticcall( - zone, - abi.encodeWithSelector( - ZoneInterface.isValidOrderIncludingExtraData.selector, - orderHash, - msg.sender, - advancedOrder, - priorOrderHashes, - criteriaResolvers - ) - ); - - // Ensure call was successful and returned correct magic value. - _assertIsValidOrderStaticcallSuccess(success, orderHash); + // Encode the `validateOrder` call in memory. + (callData, size) = _encodeValidateOrder( + orderHash, + parameters, + advancedOrder.extraData, + orderHashes + ); + + // Set the target to the zone. + target = ( + parameters + .toMemoryPointer() + .offset(OrderParameters_zone_offset) + .readAddress() + ); + + // Set the restricted-order-specific error selector. + errorSelector = InvalidRestrictedOrder_error_selector; + } else if (parameters.orderType == OrderType.CONTRACT) { + // Set the target to the offerer (note the offerer has no offset). + target = parameters.toMemoryPointer().readAddress(); + + // Shift the target 96 bits to the left. + uint256 shiftedOfferer; + assembly { + shiftedOfferer := shl( + ContractOrder_orderHash_offerer_shift, + target + ) } + + // Encode the `ratifyOrder` call in memory. + (callData, size) = _encodeRatifyOrder( + orderHash, + parameters, + advancedOrder.extraData, + orderHashes, + shiftedOfferer + ); + + // Set the contract-order-specific error selector. + errorSelector = InvalidContractOrder_error_selector; + } else { + return; } + + // Perform call and ensure a corresponding magic value was returned. + _callAndCheckStatus(target, orderHash, callData, size, errorSelector); } /** - * @dev Internal view function to ensure that a staticcall to `isValidOrder` - * or `isValidOrderIncludingExtraData` as part of validating a - * restricted order that was not submitted by the named offerer or zone - * was successful and returned the required magic value. + * @dev Determines whether the specified order type is restricted and the + * caller is not the specified zone. + * + * @param orderType The type of the order to check. + * @param zone The address of the zone to check against. * - * @param success A boolean indicating the status of the staticcall. - * @param orderHash The order hash of the order in question. + * @return mustValidate True if the order type is restricted and the caller + * is not the specified zone, false otherwise. */ - function _assertIsValidOrderStaticcallSuccess( - bool success, - bytes32 orderHash - ) internal view { - // If the call failed... + function _isRestrictedAndCallerNotZone( + OrderType orderType, + address zone + ) internal view returns (bool mustValidate) { + assembly { + mustValidate := and( + // Note that this check requires that there are no order types + // beyond the current set (0-4). It will need to be modified if + // more order types are added. + and(lt(orderType, 4), gt(orderType, 1)), + iszero(eq(caller(), zone)) + ) + } + } + + /** + * @dev Calls the specified target with the given data and checks the status + * of the call. Revert reasons will be "bubbled up" if one is returned, + * otherwise reverting calls will throw a generic error based on the + * supplied error handler. + * + * @param target The address of the contract to call. + * @param orderHash The hash of the order associated with the call. + * @param callData The data to pass to the contract call. + * @param size The size of calldata. + * @param errorSelector The error handling function to call if the call + * fails or the magic value does not match. + */ + function _callAndCheckStatus( + address target, + bytes32 orderHash, + MemoryPointer callData, + uint256 size, + uint256 errorSelector + ) internal { + bool success; + bool magicMatch; + assembly { + // Get magic value from the selector at start of provided calldata. + let magic := and(mload(callData), MaskOverFirstFourBytes) + + // Clear the start of scratch space. + mstore(0, 0) + + // Perform call, placing result in the first word of scratch space. + success := call(gas(), target, 0, callData, size, 0, OneWord) + + // Determine if returned magic value matches the calldata selector. + magicMatch := eq(magic, mload(0)) + } + + // Revert if the call was not successful. if (!success) { // Revert and pass reason along if one was returned. _revertWithReasonIfOneIsReturned(); - // Otherwise, revert with a generic error message. - revert InvalidRestrictedOrder(orderHash); + // If no reason was returned, revert with supplied error selector. + assembly { + mstore(0, errorSelector) + mstore(InvalidRestrictedOrder_error_orderHash_ptr, orderHash) + // revert(abi.encodeWithSelector( + // "InvalidRestrictedOrder(bytes32)", + // orderHash + // )) + revert( + Error_selector_offset, + InvalidRestrictedOrder_error_length + ) + } } - // Ensure result was extracted and matches isValidOrder magic value. - if (_doesNotMatchMagic(ZoneInterface.isValidOrder.selector)) { - revert InvalidRestrictedOrder(orderHash); + // Revert if the correct magic value was not returned. + if (!magicMatch) { + // Revert with a generic error message. + assembly { + mstore(0, errorSelector) + mstore(InvalidRestrictedOrder_error_orderHash_ptr, orderHash) + + // revert(abi.encodeWithSelector( + // "InvalidRestrictedOrder(bytes32)", + // orderHash + // )) + revert( + Error_selector_offset, + InvalidRestrictedOrder_error_length + ) + } } } } diff --git a/contracts/test/ConduitControllerMock.sol b/contracts/test/ConduitControllerMock.sol new file mode 100644 index 000000000..a14d164cd --- /dev/null +++ b/contracts/test/ConduitControllerMock.sol @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ConduitControllerInterface +} from "../interfaces/ConduitControllerInterface.sol"; + +import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; + +import { ConduitMock } from "../test/ConduitMock.sol"; + +import { ConduitMockInvalidMagic } from "../test/ConduitMockInvalidMagic.sol"; + +import { + ConduitMockRevertNoReason +} from "../test/ConduitMockRevertNoReason.sol"; + +import { ConduitMockRevertBytes } from "../test/ConduitMockRevertBytes.sol"; + +contract ConduitControllerMock is ConduitControllerInterface { + // Register keys, owners, new potential owners, and channels by conduit. + mapping(address => ConduitProperties) internal _conduits; + + // Set conduit creation code and runtime code hashes as immutable arguments. + bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH; + bytes32 internal immutable _CONDUIT_RUNTIME_CODE_HASH; + + uint256 private conduitNum; + + /** + * @dev Initialize contract by deploying a conduit and setting the creation + * code and runtime code hashes as immutable arguments. + */ + constructor(uint256 _conduitNum) { + conduitNum = _conduitNum; + + bytes32 creationCodeHash; + bytes32 runtimeCodeHash; + + if (conduitNum == 0) { + creationCodeHash = keccak256(type(ConduitMock).creationCode); + ConduitMock zeroConduit = new ConduitMock{ salt: bytes32(0) }(); + runtimeCodeHash = address(zeroConduit).codehash; + } else if (conduitNum == 1) { + creationCodeHash = keccak256( + type(ConduitMockRevertNoReason).creationCode + ); + ConduitMockRevertNoReason zeroConduit = new ConduitMockRevertNoReason{ + salt: bytes32(0) + }(); + runtimeCodeHash = address(zeroConduit).codehash; + } else if (conduitNum == 2) { + creationCodeHash = keccak256( + type(ConduitMockInvalidMagic).creationCode + ); + ConduitMockInvalidMagic zeroConduit = new ConduitMockInvalidMagic{ + salt: bytes32(0) + }(); + runtimeCodeHash = address(zeroConduit).codehash; + } else if (conduitNum == 3) { + creationCodeHash = keccak256( + type(ConduitMockRevertBytes).creationCode + ); + ConduitMockRevertBytes zeroConduit = new ConduitMockRevertBytes{ + salt: bytes32(0) + }(); + runtimeCodeHash = address(zeroConduit).codehash; + } + _CONDUIT_CREATION_CODE_HASH = creationCodeHash; + _CONDUIT_RUNTIME_CODE_HASH = runtimeCodeHash; + } + + /** + * @notice Deploy a new conduit using a supplied conduit key and assigning + * an initial owner for the deployed conduit. Note that the first + * twenty bytes of the supplied conduit key must match the caller + * and that a new conduit cannot be created if one has already been + * deployed using the same conduit key. + * + * @param conduitKey The conduit key used to deploy the conduit. Note that + * the first twenty bytes of the conduit key must match + * the caller of this contract. + * @param initialOwner The initial owner to set for the new conduit. + * + * @return conduit The address of the newly deployed conduit. + */ + function createConduit( + bytes32 conduitKey, + address initialOwner + ) external override returns (address conduit) { + // Ensure that an initial owner has been supplied. + if (initialOwner == address(0)) { + revert InvalidInitialOwner(); + } + + // If the first 20 bytes of the conduit key do not match the caller... + if (address(uint160(bytes20(conduitKey))) != msg.sender) { + // Revert with an error indicating that the creator is invalid. + revert InvalidCreator(); + } + + // Derive address from deployer, conduit key and creation code hash. + conduit = address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + conduitKey, + _CONDUIT_CREATION_CODE_HASH + ) + ) + ) + ) + ); + + // If derived conduit exists, as evidenced by comparing runtime code... + if (conduit.codehash == _CONDUIT_RUNTIME_CODE_HASH) { + // Revert with an error indicating that the conduit already exists. + revert ConduitAlreadyExists(conduit); + } + + // Deploy the conduit via CREATE2 using the conduit key as the salt. + if (conduitNum == 0) { + new ConduitMock{ salt: conduitKey }(); + } else if (conduitNum == 1) { + new ConduitMockRevertNoReason{ salt: conduitKey }(); + } else if (conduitNum == 2) { + new ConduitMockInvalidMagic{ salt: conduitKey }(); + } else if (conduitNum == 3) { + new ConduitMockRevertBytes{ salt: conduitKey }(); + } + // Initialize storage variable referencing conduit properties. + ConduitProperties storage conduitProperties = _conduits[conduit]; + + // Set the supplied initial owner as the owner of the conduit. + conduitProperties.owner = initialOwner; + + // Set conduit key used to deploy the conduit to enable reverse lookup. + conduitProperties.key = conduitKey; + + // Emit an event indicating that the conduit has been deployed. + emit NewConduit(conduit, conduitKey); + + // Emit an event indicating that conduit ownership has been assigned. + emit OwnershipTransferred(conduit, address(0), initialOwner); + } + + /** + * @notice Open or close a channel on a given conduit, thereby allowing the + * specified account to execute transfers against that conduit. + * Extreme care must be taken when updating channels, as malicious + * or vulnerable channels can transfer any ERC20, ERC721 and ERC1155 + * tokens where the token holder has granted the conduit approval. + * Only the owner of the conduit in question may call this function. + * + * @param conduit The conduit for which to open or close the channel. + * @param channel The channel to open or close on the conduit. + * @param isOpen A boolean indicating whether to open or close the channel. + */ + function updateChannel( + address conduit, + address channel, + bool isOpen + ) external override { + // Ensure the caller is the current owner of the conduit in question. + _assertCallerIsConduitOwner(conduit); + + // Call the conduit, updating the channel. + ConduitInterface(conduit).updateChannel(channel, isOpen); + + // Retrieve storage region where channels for the conduit are tracked. + ConduitProperties storage conduitProperties = _conduits[conduit]; + + // Retrieve the index, if one currently exists, for the updated channel. + uint256 channelIndexPlusOne = ( + conduitProperties.channelIndexesPlusOne[channel] + ); + + // Determine whether the updated channel is already tracked as open. + bool channelPreviouslyOpen = channelIndexPlusOne != 0; + + // If the channel has been set to open and was previously closed... + if (isOpen && !channelPreviouslyOpen) { + // Add the channel to the channels array for the conduit. + conduitProperties.channels.push(channel); + + // Add new open channel length to associated mapping as index + 1. + conduitProperties.channelIndexesPlusOne[channel] = ( + conduitProperties.channels.length + ); + } else if (!isOpen && channelPreviouslyOpen) { + // Set a previously open channel as closed via "swap & pop" method. + // Decrement located index to get the index of the closed channel. + uint256 removedChannelIndex; + + // Skip underflow check as channelPreviouslyOpen being true ensures + // that channelIndexPlusOne is nonzero. + unchecked { + removedChannelIndex = channelIndexPlusOne - 1; + } + + // Use length of channels array to determine index of last channel. + uint256 finalChannelIndex = conduitProperties.channels.length - 1; + + // If closed channel is not last channel in the channels array... + if (finalChannelIndex != removedChannelIndex) { + // Retrieve the final channel and place the value on the stack. + address finalChannel = ( + conduitProperties.channels[finalChannelIndex] + ); + + // Overwrite the removed channel using the final channel value. + conduitProperties.channels[removedChannelIndex] = finalChannel; + + // Update final index in associated mapping to removed index. + conduitProperties.channelIndexesPlusOne[finalChannel] = ( + channelIndexPlusOne + ); + } + + // Remove the last channel from the channels array for the conduit. + conduitProperties.channels.pop(); + + // Remove the closed channel from associated mapping of indexes. + delete conduitProperties.channelIndexesPlusOne[channel]; + } + } + + /** + * @notice Initiate conduit ownership transfer by assigning a new potential + * owner for the given conduit. Once set, the new potential owner + * may call `acceptOwnership` to claim ownership of the conduit. + * Only the owner of the conduit in question may call this function. + * + * @param conduit The conduit for which to initiate ownership transfer. + * @param newPotentialOwner The new potential owner of the conduit. + */ + function transferOwnership( + address conduit, + address newPotentialOwner + ) external override { + // Ensure the caller is the current owner of the conduit in question. + _assertCallerIsConduitOwner(conduit); + + // Ensure the new potential owner is not an invalid address. + if (newPotentialOwner == address(0)) { + revert NewPotentialOwnerIsZeroAddress(conduit); + } + + // Ensure the new potential owner is not already set. + if (newPotentialOwner == _conduits[conduit].potentialOwner) { + revert NewPotentialOwnerAlreadySet(conduit, newPotentialOwner); + } + + // Emit an event indicating that the potential owner has been updated. + emit PotentialOwnerUpdated(newPotentialOwner); + + // Set the new potential owner as the potential owner of the conduit. + _conduits[conduit].potentialOwner = newPotentialOwner; + } + + /** + * @notice Clear the currently set potential owner, if any, from a conduit. + * Only the owner of the conduit in question may call this function. + * + * @param conduit The conduit for which to cancel ownership transfer. + */ + function cancelOwnershipTransfer(address conduit) external override { + // Ensure the caller is the current owner of the conduit in question. + _assertCallerIsConduitOwner(conduit); + + // Ensure that ownership transfer is currently possible. + if (_conduits[conduit].potentialOwner == address(0)) { + revert NoPotentialOwnerCurrentlySet(conduit); + } + + // Emit an event indicating that the potential owner has been cleared. + emit PotentialOwnerUpdated(address(0)); + + // Clear the current new potential owner from the conduit. + _conduits[conduit].potentialOwner = address(0); + } + + /** + * @notice Accept ownership of a supplied conduit. Only accounts that the + * current owner has set as the new potential owner may call this + * function. + * + * @param conduit The conduit for which to accept ownership. + */ + function acceptOwnership(address conduit) external override { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // If caller does not match current potential owner of the conduit... + if (msg.sender != _conduits[conduit].potentialOwner) { + // Revert, indicating that caller is not current potential owner. + revert CallerIsNotNewPotentialOwner(conduit); + } + + // Emit an event indicating that the potential owner has been cleared. + emit PotentialOwnerUpdated(address(0)); + + // Clear the current new potential owner from the conduit. + _conduits[conduit].potentialOwner = address(0); + + // Emit an event indicating conduit ownership has been transferred. + emit OwnershipTransferred( + conduit, + _conduits[conduit].owner, + msg.sender + ); + + // Set the caller as the owner of the conduit. + _conduits[conduit].owner = msg.sender; + } + + /** + * @notice Retrieve the current owner of a deployed conduit. + * + * @param conduit The conduit for which to retrieve the associated owner. + * + * @return owner The owner of the supplied conduit. + */ + function ownerOf( + address conduit + ) external view override returns (address owner) { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // Retrieve the current owner of the conduit in question. + owner = _conduits[conduit].owner; + } + + /** + * @notice Retrieve the conduit key for a deployed conduit via reverse + * lookup. + * + * @param conduit The conduit for which to retrieve the associated conduit + * key. + * + * @return conduitKey The conduit key used to deploy the supplied conduit. + */ + function getKey( + address conduit + ) external view override returns (bytes32 conduitKey) { + // Attempt to retrieve a conduit key for the conduit in question. + conduitKey = _conduits[conduit].key; + + // Revert if no conduit key was located. + if (conduitKey == bytes32(0)) { + revert NoConduit(); + } + } + + /** + * @notice Derive the conduit associated with a given conduit key and + * determine whether that conduit exists (i.e. whether it has been + * deployed). + * + * @param conduitKey The conduit key used to derive the conduit. + * + * @return conduit The derived address of the conduit. + * @return exists A boolean indicating whether the derived conduit has been + * deployed or not. + */ + function getConduit( + bytes32 conduitKey + ) external view override returns (address conduit, bool exists) { + // Derive address from deployer, conduit key and creation code hash. + conduit = address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + conduitKey, + _CONDUIT_CREATION_CODE_HASH + ) + ) + ) + ) + ); + + // Determine whether conduit exists by retrieving its runtime code. + exists = (conduit.codehash == _CONDUIT_RUNTIME_CODE_HASH); + } + + /** + * @notice Retrieve the potential owner, if any, for a given conduit. The + * current owner may set a new potential owner via + * `transferOwnership` and that owner may then accept ownership of + * the conduit in question via `acceptOwnership`. + * + * @param conduit The conduit for which to retrieve the potential owner. + * + * @return potentialOwner The potential owner, if any, for the conduit. + */ + function getPotentialOwner( + address conduit + ) external view override returns (address potentialOwner) { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // Retrieve the current potential owner of the conduit in question. + potentialOwner = _conduits[conduit].potentialOwner; + } + + /** + * @notice Retrieve the status (either open or closed) of a given channel on + * a conduit. + * + * @param conduit The conduit for which to retrieve the channel status. + * @param channel The channel for which to retrieve the status. + * + * @return isOpen The status of the channel on the given conduit. + */ + function getChannelStatus( + address conduit, + address channel + ) external view override returns (bool isOpen) { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // Retrieve the current channel status for the conduit in question. + isOpen = _conduits[conduit].channelIndexesPlusOne[channel] != 0; + } + + /** + * @notice Retrieve the total number of open channels for a given conduit. + * + * @param conduit The conduit for which to retrieve the total channel count. + * + * @return totalChannels The total number of open channels for the conduit. + */ + function getTotalChannels( + address conduit + ) external view override returns (uint256 totalChannels) { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // Retrieve the total open channel count for the conduit in question. + totalChannels = _conduits[conduit].channels.length; + } + + /** + * @notice Retrieve an open channel at a specific index for a given conduit. + * Note that the index of a channel can change as a result of other + * channels being closed on the conduit. + * + * @param conduit The conduit for which to retrieve the open channel. + * @param channelIndex The index of the channel in question. + * + * @return channel The open channel, if any, at the specified channel index. + */ + function getChannel( + address conduit, + uint256 channelIndex + ) external view override returns (address channel) { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // Retrieve the total open channel count for the conduit in question. + uint256 totalChannels = _conduits[conduit].channels.length; + + // Ensure that the supplied index is within range. + if (channelIndex >= totalChannels) { + revert ChannelOutOfRange(conduit); + } + + // Retrieve the channel at the given index. + channel = _conduits[conduit].channels[channelIndex]; + } + + /** + * @notice Retrieve all open channels for a given conduit. Note that calling + * this function for a conduit with many channels will revert with + * an out-of-gas error. + * + * @param conduit The conduit for which to retrieve open channels. + * + * @return channels An array of open channels on the given conduit. + */ + function getChannels( + address conduit + ) external view override returns (address[] memory channels) { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // Retrieve all of the open channels on the conduit in question. + channels = _conduits[conduit].channels; + } + + /** + * @dev Retrieve the conduit creation code and runtime code hashes. + */ + function getConduitCodeHashes() + external + view + override + returns (bytes32 creationCodeHash, bytes32 runtimeCodeHash) + { + // Retrieve the conduit creation code hash from runtime. + creationCodeHash = _CONDUIT_CREATION_CODE_HASH; + + // Retrieve the conduit runtime code hash from runtime. + runtimeCodeHash = _CONDUIT_RUNTIME_CODE_HASH; + } + + /** + * @dev Private view function to revert if the caller is not the owner of a + * given conduit. + * + * @param conduit The conduit for which to assert ownership. + */ + function _assertCallerIsConduitOwner(address conduit) private view { + // Ensure that the conduit in question exists. + _assertConduitExists(conduit); + + // If the caller does not match the current owner of the conduit... + if (msg.sender != _conduits[conduit].owner) { + // Revert, indicating that the caller is not the owner. + revert CallerIsNotOwner(conduit); + } + } + + /** + * @dev Private view function to revert if a given conduit does not exist. + * + * @param conduit The conduit for which to assert existence. + */ + function _assertConduitExists(address conduit) private view { + // Attempt to retrieve a conduit key for the conduit in question. + if (_conduits[conduit].key == bytes32(0)) { + // Revert if no conduit key was located. + revert NoConduit(); + } + } +} diff --git a/contracts/test/ConduitMock.sol b/contracts/test/ConduitMock.sol new file mode 100644 index 000000000..aad1342e1 --- /dev/null +++ b/contracts/test/ConduitMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; + +import { + ConduitBatch1155Transfer, + ConduitTransfer +} from "../conduit/lib/ConduitStructs.sol"; + +contract ConduitMock is ConduitInterface { + constructor() {} + + function execute( + ConduitTransfer[] calldata /* transfers */ + ) external pure override returns (bytes4) { + // Return the valid magic value. + return 0x4ce34aa2; + } + + function executeBatch1155( + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function executeWithBatch1155( + ConduitTransfer[] calldata /* standardTransfers */, + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function updateChannel(address channel, bool isOpen) external override {} +} diff --git a/contracts/test/ConduitMockInvalidMagic.sol b/contracts/test/ConduitMockInvalidMagic.sol new file mode 100644 index 000000000..0a822677c --- /dev/null +++ b/contracts/test/ConduitMockInvalidMagic.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; + +import { + ConduitBatch1155Transfer, + ConduitTransfer +} from "../conduit/lib/ConduitStructs.sol"; + +contract ConduitMockInvalidMagic is ConduitInterface { + constructor() {} + + function execute( + ConduitTransfer[] calldata /* transfers */ + ) external pure override returns (bytes4) { + return 0xabcd0000; + } + + function executeBatch1155( + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function executeWithBatch1155( + ConduitTransfer[] calldata /* standardTransfers */, + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function updateChannel(address channel, bool isOpen) external override {} +} diff --git a/contracts/test/ConduitMockRevertBytes.sol b/contracts/test/ConduitMockRevertBytes.sol new file mode 100644 index 000000000..38e97dfa9 --- /dev/null +++ b/contracts/test/ConduitMockRevertBytes.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; + +import { + ConduitBatch1155Transfer, + ConduitTransfer +} from "../conduit/lib/ConduitStructs.sol"; + +contract ConduitMockRevertBytes is ConduitInterface { + constructor() {} + + error CustomError(); + + function execute( + ConduitTransfer[] calldata /* transfers */ + ) external pure override returns (bytes4) { + revert CustomError(); + } + + function executeBatch1155( + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function executeWithBatch1155( + ConduitTransfer[] calldata /* standardTransfers */, + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function updateChannel(address channel, bool isOpen) external override {} +} diff --git a/contracts/test/ConduitMockRevertNoReason.sol b/contracts/test/ConduitMockRevertNoReason.sol new file mode 100644 index 000000000..c11155e19 --- /dev/null +++ b/contracts/test/ConduitMockRevertNoReason.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ConduitInterface } from "../interfaces/ConduitInterface.sol"; + +import { + ConduitBatch1155Transfer, + ConduitTransfer +} from "../conduit/lib/ConduitStructs.sol"; + +contract ConduitMockRevertNoReason is ConduitInterface { + constructor() {} + + function execute( + ConduitTransfer[] calldata /* transfers */ + ) external pure override returns (bytes4) { + // Revert without reason string. + revert(); + } + + function executeBatch1155( + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function executeWithBatch1155( + ConduitTransfer[] calldata /* standardTransfers */, + ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ + ) external view override returns (bytes4 magicValue) {} + + function updateChannel(address channel, bool isOpen) external override {} +} diff --git a/contracts/test/EIP1271Wallet.sol b/contracts/test/EIP1271Wallet.sol index b0e5b50a1..997e6768d 100644 --- a/contracts/test/EIP1271Wallet.sol +++ b/contracts/test/EIP1271Wallet.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; interface ERC20ApprovalInterface { function approve(address, uint256) external returns (bool); @@ -58,11 +58,10 @@ contract EIP1271Wallet { token.setApprovalForAll(operator, true); } - function isValidSignature(bytes32 digest, bytes memory signature) - external - view - returns (bytes4) - { + function isValidSignature( + bytes32 digest, + bytes memory signature + ) external view returns (bytes4) { if (digestApproved[digest]) { return _EIP_1271_MAGIC_VALUE; } diff --git a/contracts/test/ERC1155BatchRecipient.sol b/contracts/test/ERC1155BatchRecipient.sol index 2704c169d..360ad9d1d 100644 --- a/contracts/test/ERC1155BatchRecipient.sol +++ b/contracts/test/ERC1155BatchRecipient.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; contract ERC1155BatchRecipient { error UnexpectedBatchData(); diff --git a/contracts/test/ERC721ReceiverMock.sol b/contracts/test/ERC721ReceiverMock.sol new file mode 100644 index 000000000..b6c409e9c --- /dev/null +++ b/contracts/test/ERC721ReceiverMock.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { IERC721Receiver } from "../interfaces/IERC721Receiver.sol"; + +contract ERC721ReceiverMock is IERC721Receiver { + enum Error { + None, + RevertWithMessage, + RevertWithoutMessage, + Panic + } + + bytes4 private immutable _retval; + Error private immutable _error; + + event Received( + address operator, + address from, + uint256 tokenId, + bytes data, + uint256 gas + ); + + constructor(bytes4 retval, Error error) { + _retval = retval; + _error = error; + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes memory data + ) public override returns (bytes4) { + if (_error == Error.RevertWithMessage) { + revert("ERC721ReceiverMock: reverting"); + } else if (_error == Error.RevertWithoutMessage) { + revert(); + } else if (_error == Error.Panic) { + uint256 a = uint256(0) / uint256(0); + a; + } + emit Received(operator, from, tokenId, data, gasleft()); + return _retval; + } +} diff --git a/contracts/test/ExcessReturnDataRecipient.sol b/contracts/test/ExcessReturnDataRecipient.sol index 7196920ab..8d7fd3363 100644 --- a/contracts/test/ExcessReturnDataRecipient.sol +++ b/contracts/test/ExcessReturnDataRecipient.sol @@ -1,5 +1,5 @@ -//SPDX-License-Identifier: Unlicense -pragma solidity >=0.8.7; +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; contract ExcessReturnDataRecipient { uint256 private revertDataSize; diff --git a/contracts/test/InvalidERC721Recipient.sol b/contracts/test/InvalidERC721Recipient.sol new file mode 100644 index 000000000..66a4b2d42 --- /dev/null +++ b/contracts/test/InvalidERC721Recipient.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +interface IERC721Receiver { + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) external returns (bytes4); +} + +contract InvalidERC721Recipient is IERC721Receiver { + function onERC721Received( + address /* operator */, + address /* from */, + uint256 /* tokenId */, + bytes calldata /* data */ + ) external pure override returns (bytes4) { + return 0xabcd0000; + } +} diff --git a/contracts/test/InvalidEthRecipient.sol b/contracts/test/InvalidEthRecipient.sol new file mode 100644 index 000000000..831fc9a01 --- /dev/null +++ b/contracts/test/InvalidEthRecipient.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +contract InvalidEthRecipient { + receive() external payable { + revert( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + } +} diff --git a/contracts/test/Reenterer.sol b/contracts/test/Reenterer.sol index 18d7eda0d..03eb2239d 100644 --- a/contracts/test/Reenterer.sol +++ b/contracts/test/Reenterer.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; contract Reenterer { + bool public isPrepared; address public target; uint256 public msgValue; bytes public callData; @@ -16,20 +17,24 @@ contract Reenterer { target = targetToUse; msgValue = msgValueToUse; callData = callDataToUse; + isPrepared = true; } receive() external payable { - (bool success, bytes memory returnData) = target.call{ - value: msgValue - }(callData); + if (isPrepared) { + (bool success, bytes memory returnData) = target.call{ + value: msgValue + }(callData); - if (!success) { - assembly { - returndatacopy(0, 0, returndatasize()) - revert(0, returndatasize()) + if (!success) { + assembly { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } } - } + emit Reentered(returnData); - emit Reentered(returnData); + isPrepared = false; + } } } diff --git a/contracts/test/TestBadContractOfferer.sol b/contracts/test/TestBadContractOfferer.sol new file mode 100644 index 000000000..8b2272eae --- /dev/null +++ b/contracts/test/TestBadContractOfferer.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ERC721Interface } from "../interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../interfaces/ContractOffererInterface.sol"; + +import { ItemType } from "../lib/ConsiderationEnums.sol"; + +import { + ReceivedItem, + Schema, + SpentItem +} from "../lib/ConsiderationStructs.sol"; + +contract TestBadContractOfferer is ContractOffererInterface { + error IntentionalRevert(); + + address private immutable seaport; + ERC721Interface token; + + constructor(address _seaport, ERC721Interface _token) { + seaport = _seaport; + token = _token; + ERC721Interface(token).setApprovalForAll(seaport, true); + } + + receive() external payable {} + + /** + * @dev Generates an order with the specified minimum and maximum spent items, + * and the optional extra data. + * + * @param a Fulfiller, unused here. + * @param b The minimum items that the caller is willing to + * receive. + * @param c maximumSent, unused here. + * @param d context, unused here. + * + * @return offer A tuple containing the offer items. + * @return consideration A tuple containing the consideration items. + */ + function generateOrder( + address a, + SpentItem[] calldata b, + SpentItem[] calldata c, + bytes calldata d + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + return previewOrder(a, a, b, c, d); + } + + /** + * @dev View function to preview an order generated in response to a minimum + * set of received items, maximum set of spent items, and context + * (supplied as extraData). + * + * @param - caller, unused here. + * @param - fulfiller, unused here. + * @param minimumReceived The minimum received set. + * @param - maximumSpent, unused here. + * @param - context, unused here. + * + * @return offer The offer for the order. + * @return consideration The consideration for the order. + */ + function previewOrder( + address, + address, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata + ) + public + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + if (minimumReceived[0].identifier == 1) { + offer = minimumReceived; + consideration = new ReceivedItem[](1); + consideration[0] = ReceivedItem({ + itemType: ItemType.NATIVE, + token: address(0), + identifier: 0, + amount: 100, + recipient: payable(address(this)) + }); + return (offer, consideration); + } else if (minimumReceived[0].identifier == 2) { + // return nothing + assembly { + return(0, 0) + } + } else if (minimumReceived[0].identifier == 3) { + revert IntentionalRevert(); + } else { + // return garbage + bytes32 h1 = keccak256(abi.encode(minimumReceived)); + bytes32 h2 = keccak256(abi.encode(maximumSpent)); + assembly { + mstore(0x00, h1) + mstore(0x20, h2) + return(0, 0x100) + } + } + } + + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata /* context */, + bytes32[] calldata /* orderHashes */, + uint256 /* contractNonce */ + ) external pure override returns (bytes4 /* ratifyOrderMagicValue */) { + return TestBadContractOfferer.ratifyOrder.selector; + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("TestBadContractOfferer", schemas); + } +} diff --git a/contracts/test/TestContractOfferer.sol b/contracts/test/TestContractOfferer.sol new file mode 100644 index 000000000..4840b16e8 --- /dev/null +++ b/contracts/test/TestContractOfferer.sol @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ERC20Interface, + ERC721Interface, + ERC1155Interface +} from "../interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../interfaces/ContractOffererInterface.sol"; + +import { ItemType } from "../lib/ConsiderationEnums.sol"; + +import { + ReceivedItem, + Schema, + SpentItem +} from "../lib/ConsiderationStructs.sol"; + +/** + * @title TestContractOfferer + * @author 0age + * @notice TestContractOfferer is a maximally simple contract offerer. It offers + * a single item and expects to receive back another single item, and + * ignores all parameters supplied to it when previewing or generating + * an order. The offered item is placed into this contract as part of + * deployment and the corresponding token approvals are set for Seaport. + */ +contract TestContractOfferer is ContractOffererInterface { + error OrderUnavailable(); + + address private immutable _SEAPORT; + + SpentItem private _available; + SpentItem private _required; + + bool public ready; + bool public fulfilled; + + uint256 public extraAvailable; + uint256 public extraRequired; + + constructor(address seaport) { + // Set immutable values and storage variables. + _SEAPORT = seaport; + fulfilled = false; + ready = false; + extraAvailable = 0; + extraRequired = 0; + } + + receive() external payable {} + + /// In case of criteria based orders and non-wildcard items, the member + /// `available.identifier` would correspond to the `identifierOrCriteria` + /// i.e., the merkle-root. + /// @param identifier corresponds to the actual token-id that gets transferred. + function activateWithCriteria( + SpentItem memory available, + SpentItem memory required, + uint256 identifier + ) public { + if (ready || fulfilled) { + revert OrderUnavailable(); + } + + if (available.itemType == ItemType.ERC721_WITH_CRITERIA) { + ERC721Interface token = ERC721Interface(available.token); + + token.transferFrom(msg.sender, address(this), identifier); + + token.setApprovalForAll(_SEAPORT, true); + } else if (available.itemType == ItemType.ERC1155_WITH_CRITERIA) { + ERC1155Interface token = ERC1155Interface(available.token); + + token.safeTransferFrom( + msg.sender, + address(this), + identifier, + available.amount, + "" + ); + + token.setApprovalForAll(_SEAPORT, true); + } + + // Set storage variables. + _available = available; + _required = required; + ready = true; + } + + function activate( + SpentItem memory available, + SpentItem memory required + ) public payable { + if (ready || fulfilled) { + revert OrderUnavailable(); + } + + // Retrieve the offered item and set associated approvals. + if (available.itemType == ItemType.NATIVE) { + available.amount = address(this).balance; + } else if (available.itemType == ItemType.ERC20) { + ERC20Interface token = ERC20Interface(available.token); + + token.transferFrom(msg.sender, address(this), available.amount); + + token.approve(_SEAPORT, available.amount); + } else if (available.itemType == ItemType.ERC721) { + ERC721Interface token = ERC721Interface(available.token); + + token.transferFrom(msg.sender, address(this), available.identifier); + + token.setApprovalForAll(_SEAPORT, true); + } else if (available.itemType == ItemType.ERC1155) { + ERC1155Interface token = ERC1155Interface(available.token); + + token.safeTransferFrom( + msg.sender, + address(this), + available.identifier, + available.amount, + "" + ); + + token.setApprovalForAll(_SEAPORT, true); + } + + // Set storage variables. + _available = available; + _required = required; + ready = true; + } + + function extendAvailable() public { + if (!ready || fulfilled) { + revert OrderUnavailable(); + } + + extraAvailable++; + + _available.amount /= 2; + } + + function extendRequired() public { + if (!ready || fulfilled) { + revert OrderUnavailable(); + } + + extraRequired++; + } + + function generateOrder( + address, + SpentItem[] calldata, + SpentItem[] calldata, + bytes calldata context + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Ensure the caller is Seaport & the order has not yet been fulfilled. + if ( + !ready || + fulfilled || + msg.sender != _SEAPORT || + context.length % 32 != 0 + ) { + revert OrderUnavailable(); + } + + // Set the offer and consideration that were supplied during deployment. + offer = new SpentItem[](1 + extraAvailable); + consideration = new ReceivedItem[](1 + extraRequired); + + for (uint256 i = 0; i < 1 + extraAvailable; ++i) { + offer[i] = _available; + } + + for (uint256 i = 0; i < 1 + extraRequired; ++i) { + consideration[i] = ReceivedItem({ + itemType: _required.itemType, + token: _required.token, + identifier: _required.identifier, + amount: _required.amount, + recipient: payable(address(this)) + }); + } + + // Update storage to reflect that the order has been fulfilled. + fulfilled = true; + } + + function previewOrder( + address caller, + address, + SpentItem[] calldata, + SpentItem[] calldata, + bytes calldata context + ) + external + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Ensure the caller is Seaport & the order has not yet been fulfilled. + if ( + !ready || + fulfilled || + caller != _SEAPORT || + context.length % 32 != 0 + ) { + revert OrderUnavailable(); + } + + // Set the offer and consideration that were supplied during deployment. + offer = new SpentItem[](1 + extraAvailable); + consideration = new ReceivedItem[](1 + extraRequired); + + for (uint256 i = 0; i < 1 + extraAvailable; ++i) { + offer[i] = _available; + } + + for (uint256 i = 0; i < 1 + extraRequired; ++i) { + consideration[i] = ReceivedItem({ + itemType: _required.itemType, + token: _required.token, + identifier: _required.identifier, + amount: _required.amount, + recipient: payable(address(this)) + }); + } + } + + function getInventory() + external + view + returns (SpentItem[] memory offerable, SpentItem[] memory receivable) + { + // Set offerable and receivable supplied at deployment if unfulfilled. + if (!ready || fulfilled) { + offerable = new SpentItem[](0); + + receivable = new SpentItem[](0); + } else { + offerable = new SpentItem[](1 + extraAvailable); + for (uint256 i = 0; i < 1 + extraAvailable; ++i) { + offerable[i] = _available; + } + + receivable = new SpentItem[](1 + extraRequired); + for (uint256 i = 0; i < 1 + extraRequired; ++i) { + receivable[i] = _required; + } + } + } + + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata context, + bytes32[] calldata orderHashes, + uint256 /* contractNonce */ + ) + external + pure + virtual + override + returns (bytes4 /* ratifyOrderMagicValue */) + { + if (context.length > 32 && context.length % 32 == 0) { + bytes32[] memory expectedOrderHashes = abi.decode( + context, + (bytes32[]) + ); + + uint256 expectedLength = expectedOrderHashes.length; + + if (expectedLength != orderHashes.length) { + revert("Revert on unexpected order hashes length"); + } + + for (uint256 i = 0; i < expectedLength; ++i) { + if (expectedOrderHashes[i] != orderHashes[i]) { + revert("Revert on unexpected order hash"); + } + } + } + + return ContractOffererInterface.ratifyOrder.selector; + } + + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external pure returns (bytes4) { + return bytes4(0xf23a6e61); + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("TestContractOfferer", schemas); + } +} diff --git a/contracts/test/TestContractOffererNativeToken.sol b/contracts/test/TestContractOffererNativeToken.sol new file mode 100644 index 000000000..b1955fc6b --- /dev/null +++ b/contracts/test/TestContractOffererNativeToken.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ERC721Interface, + ERC1155Interface +} from "../interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../interfaces/ContractOffererInterface.sol"; + +import { ItemType } from "../lib/ConsiderationEnums.sol"; + +import { + ReceivedItem, + Schema, + SpentItem +} from "../lib/ConsiderationStructs.sol"; + +/** + * @title TestContractOffererNativeToken + */ +contract TestContractOffererNativeToken is ContractOffererInterface { + error OrderUnavailable(); + + address private immutable _SEAPORT; + + SpentItem private _available; + SpentItem private _required; + + bool public ready; + bool public fulfilled; + + uint256 public extraAvailable; + uint256 public extraRequired; + + constructor(address seaport) { + // Set immutable values and storage variables. + _SEAPORT = seaport; + fulfilled = false; + ready = false; + extraAvailable = 0; + extraRequired = 0; + } + + receive() external payable {} + + function activate( + SpentItem memory available, + SpentItem memory required + ) public payable { + if (ready || fulfilled) { + revert OrderUnavailable(); + } + + // Set storage variables. + _available = available; + _required = required; + ready = true; + } + + /// In case of criteria based orders and non-wildcard items, the member + /// `available.identifier` would correspond to the `identifierOrCriteria` + /// i.e., the merkle-root. + /// @param identifier corresponds to the actual token-id that gets transferred. + function activateWithCriteria( + SpentItem memory available, + SpentItem memory required, + uint256 identifier + ) public { + if (ready || fulfilled) { + revert OrderUnavailable(); + } + + if (available.itemType == ItemType.ERC721_WITH_CRITERIA) { + ERC721Interface token = ERC721Interface(available.token); + + token.transferFrom(msg.sender, address(this), identifier); + + token.setApprovalForAll(_SEAPORT, true); + } else if (available.itemType == ItemType.ERC1155_WITH_CRITERIA) { + ERC1155Interface token = ERC1155Interface(available.token); + + token.safeTransferFrom( + msg.sender, + address(this), + identifier, + available.amount, + "" + ); + + token.setApprovalForAll(_SEAPORT, true); + } + + // Set storage variables. + _available = available; + _required = required; + ready = true; + } + + function extendAvailable() public { + if (!ready || fulfilled) { + revert OrderUnavailable(); + } + + extraAvailable++; + + _available.amount /= 2; + } + + function extendRequired() public { + if (!ready || fulfilled) { + revert OrderUnavailable(); + } + + extraRequired++; + } + + function generateOrder( + address, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata /* context */ + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Set the offer and consideration that were supplied during deployment. + offer = new SpentItem[](1); + consideration = new ReceivedItem[](1); + + // Send eth to Seaport. + (bool success, ) = _SEAPORT.call{ value: minimumReceived[0].amount }( + "" + ); + + // Revert if transaction fails. + if (!success) { + assembly { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + } + + // Set the offer item as the _available item in storage. + offer[0] = minimumReceived[0]; + + // Set the erc721 consideration item. + consideration[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: maximumSpent[0].token, + identifier: maximumSpent[0].identifier, + amount: maximumSpent[0].amount, + recipient: payable(address(this)) + }); + + // Update storage to reflect that the order has been fulfilled. + fulfilled = true; + } + + function previewOrder( + address caller, + address, + SpentItem[] calldata, + SpentItem[] calldata, + bytes calldata context + ) + external + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Ensure the caller is Seaport & the order has not yet been fulfilled. + if (!ready || fulfilled || caller != _SEAPORT || context.length != 0) { + revert OrderUnavailable(); + } + + // Set the offer and consideration that were supplied during deployment. + offer = new SpentItem[](1 + extraAvailable); + consideration = new ReceivedItem[](1 + extraRequired); + + for (uint256 i = 0; i < 1 + extraAvailable; ++i) { + offer[i] = _available; + } + + for (uint256 i = 0; i < 1 + extraRequired; ++i) { + consideration[i] = ReceivedItem({ + itemType: _required.itemType, + token: _required.token, + identifier: _required.identifier, + amount: _required.amount, + recipient: payable(address(this)) + }); + } + } + + function getInventory() + external + view + returns (SpentItem[] memory offerable, SpentItem[] memory receivable) + { + // Set offerable and receivable supplied at deployment if unfulfilled. + if (!ready || fulfilled) { + offerable = new SpentItem[](0); + + receivable = new SpentItem[](0); + } else { + offerable = new SpentItem[](1 + extraAvailable); + for (uint256 i = 0; i < 1 + extraAvailable; ++i) { + offerable[i] = _available; + } + + receivable = new SpentItem[](1 + extraRequired); + for (uint256 i = 0; i < 1 + extraRequired; ++i) { + receivable[i] = _required; + } + } + } + + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata /* context */, + bytes32[] calldata /* orderHashes */, + uint256 /* contractNonce */ + ) + external + pure + virtual + override + returns (bytes4 /* ratifyOrderMagicValue */) + { + return ContractOffererInterface.ratifyOrder.selector; + } + + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external pure returns (bytes4) { + return bytes4(0xf23a6e61); + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("TestContractOffererNativeToken", schemas); + } +} diff --git a/contracts/test/TestERC1155.sol b/contracts/test/TestERC1155.sol index 959dad790..ff967bec2 100644 --- a/contracts/test/TestERC1155.sol +++ b/contracts/test/TestERC1155.sol @@ -1,7 +1,7 @@ -//SPDX-License-Identifier: Unlicense -pragma solidity >=0.8.7; +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; -import "@rari-capital/solmate/src/tokens/ERC1155.sol"; +import { ERC1155 } from "@rari-capital/solmate/src/tokens/ERC1155.sol"; // Used for minting test ERC1155s in our tests contract TestERC1155 is ERC1155 { diff --git a/contracts/test/TestERC1155Revert.sol b/contracts/test/TestERC1155Revert.sol new file mode 100644 index 000000000..ac5742489 --- /dev/null +++ b/contracts/test/TestERC1155Revert.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +contract TestERC1155Revert { + function safeTransferFrom( + address /* from */, + address /* to */, + uint256 /* id */, + uint256 /* amount */, + bytes calldata /* data */ + ) public pure { + revert( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + } + + function safeBatchTransferFrom( + address /* from */, + address /* to */, + uint256[] memory /* ids */, + uint256[] memory /* values */, + bytes memory /* data */ + ) public pure { + revert("Some ERC1155 revert message for batch transfers"); + } + + function getRevertData() public pure returns (bytes memory) { + assembly { + mstore(0x40, 0) + mstore(0, shl(20, 1)) + mstore(add(0x20, shl(20, 1)), 1) + return(0, add(0x20, shl(20, 1))) + } + } +} diff --git a/contracts/test/TestERC20.sol b/contracts/test/TestERC20.sol index 9113ecf68..ea4abd901 100644 --- a/contracts/test/TestERC20.sol +++ b/contracts/test/TestERC20.sol @@ -1,7 +1,7 @@ -//SPDX-License-Identifier: Unlicense -pragma solidity >=0.8.7; +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; -import "@rari-capital/solmate/src/tokens/ERC20.sol"; +import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; // Used for minting test ERC20s in our tests contract TestERC20 is ERC20("Test20", "TST20", 18) { diff --git a/contracts/test/TestERC20NotOk.sol b/contracts/test/TestERC20NotOk.sol new file mode 100644 index 000000000..5d794f4df --- /dev/null +++ b/contracts/test/TestERC20NotOk.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; + +// Used for minting test ERC20s in our tests. +contract TestERC20NotOk is ERC20("Test20NotOk", "TST20NO", 18) { + bool public notOk; + + function mint(address to, uint256 amount) external returns (bool) { + _mint(to, amount); + return true; + } + + function transferFrom( + address /* from */, + address /* to */, + uint256 /* amount */ + ) public pure override returns (bool) { + return false; + } +} diff --git a/contracts/test/TestERC20Panic.sol b/contracts/test/TestERC20Panic.sol new file mode 100644 index 000000000..1b204b256 --- /dev/null +++ b/contracts/test/TestERC20Panic.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; + +contract TestERC20Panic is ERC20("TestPanic", "PANIC", 18) { + function mint(address to, uint256 amount) external returns (bool) { + _mint(to, amount); + return true; + } + + function transferFrom( + address /* from */, + address /* to */, + uint256 /* amount */ + ) public pure override returns (bool) { + uint256 a = uint256(0) / uint256(0); + a; + + return true; + } +} diff --git a/contracts/test/TestERC20Revert.sol b/contracts/test/TestERC20Revert.sol new file mode 100644 index 000000000..37a01b137 --- /dev/null +++ b/contracts/test/TestERC20Revert.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; + +contract TestERC20Revert is ERC20("TestRevert", "REVERT", 18) { + function mint(address to, uint256 amount) external { + _mint(to, amount); + } + + function transferFrom( + address /* from */, + address /* to */, + uint256 /* amount */ + ) public pure override returns (bool) { + revert( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + } +} diff --git a/contracts/test/TestERC721.sol b/contracts/test/TestERC721.sol index 5e9e143ee..e207d9ae1 100644 --- a/contracts/test/TestERC721.sol +++ b/contracts/test/TestERC721.sol @@ -1,7 +1,7 @@ -//SPDX-License-Identifier: Unlicense -pragma solidity >=0.8.7; +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; -import "@rari-capital/solmate/src/tokens/ERC721.sol"; +import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol"; // Used for minting test ERC721s in our tests contract TestERC721 is ERC721("Test721", "TST721") { diff --git a/contracts/test/TestERC721Revert.sol b/contracts/test/TestERC721Revert.sol new file mode 100644 index 000000000..8d820122f --- /dev/null +++ b/contracts/test/TestERC721Revert.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +contract TestERC721Revert { + function transferFrom( + address /* from */, + address /* to */, + uint256 /* amount */ + ) public pure { + revert( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + } +} diff --git a/contracts/test/TestInvalidContractOfferer.sol b/contracts/test/TestInvalidContractOfferer.sol new file mode 100644 index 000000000..627849945 --- /dev/null +++ b/contracts/test/TestInvalidContractOfferer.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ReceivedItem, SpentItem } from "../lib/ConsiderationStructs.sol"; + +import { TestContractOfferer } from "./TestContractOfferer.sol"; + +contract TestInvalidContractOfferer is TestContractOfferer { + error RevertWithData(bytes revertData); + + constructor(address seaport) TestContractOfferer(seaport) {} + + function generateOrder( + address, + SpentItem[] calldata, + SpentItem[] calldata, + bytes calldata context + ) + external + pure + override + returns (SpentItem[] memory, ReceivedItem[] memory) + { + revert RevertWithData(context); + } +} diff --git a/contracts/test/TestInvalidContractOffererRatifyOrder.sol b/contracts/test/TestInvalidContractOffererRatifyOrder.sol new file mode 100644 index 000000000..c9a81c84e --- /dev/null +++ b/contracts/test/TestInvalidContractOffererRatifyOrder.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ReceivedItem, SpentItem } from "../lib/ConsiderationStructs.sol"; + +import { TestContractOfferer } from "./TestContractOfferer.sol"; + +contract TestInvalidContractOffererRatifyOrder is TestContractOfferer { + constructor(address seaport) TestContractOfferer(seaport) {} + + function ratifyOrder( + SpentItem[] calldata, + ReceivedItem[] calldata, + bytes calldata, + bytes32[] calldata, + uint256 + ) external pure override returns (bytes4) { + return bytes4(keccak256("throw")); + } +} diff --git a/contracts/test/TestPostExecution.sol b/contracts/test/TestPostExecution.sol new file mode 100644 index 000000000..34dd469ce --- /dev/null +++ b/contracts/test/TestPostExecution.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ZoneInterface } from "../interfaces/ZoneInterface.sol"; + +import { ERC721Interface } from "../interfaces/AbridgedTokenInterfaces.sol"; + +import { ItemType } from "../lib/ConsiderationEnums.sol"; + +import { + ReceivedItem, + Schema, + ZoneParameters +} from "../lib/ConsiderationStructs.sol"; + +contract TestPostExecution is ZoneInterface { + function validateOrder( + ZoneParameters calldata zoneParameters + ) external view override returns (bytes4 validOrderMagicValue) { + if (zoneParameters.consideration.length == 0) { + revert("No consideration items supplied"); + } + + ReceivedItem memory receivedItem = zoneParameters.consideration[0]; + + address currentOwner; + try + ERC721Interface(receivedItem.token).ownerOf(receivedItem.identifier) + returns (address owner) { + currentOwner = owner; + } catch { + revert("Unsupported consideration token type (must implement 721)"); + } + + if (receivedItem.itemType != ItemType.ERC721) { + revert("Validity check performed with unsupported item type"); + } + + // Note that endAmount has been repurposed as recipient; this interface + // still needs to be modified to return spent / received items. + if (receivedItem.amount != 1) { + // Note that this is currently failing in the matchOrder case. + revert("Returned item amount incorrectly modified"); + } + + if (currentOwner != receivedItem.recipient) { + revert("Validity check performed prior to execution"); + } + + validOrderMagicValue = ZoneInterface.validateOrder.selector; + } + + /** + * @dev Returns the metadata for this zone. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 3003; + schemas[0].metadata = new bytes(0); + + return ("TestPostExecution", schemas); + } +} diff --git a/contracts/test/TestZone.sol b/contracts/test/TestZone.sol index 45f520be8..ca78521fc 100644 --- a/contracts/test/TestZone.sol +++ b/contracts/test/TestZone.sol @@ -1,61 +1,71 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; import { ZoneInterface } from "../interfaces/ZoneInterface.sol"; -// prettier-ignore -import { - AdvancedOrder, - CriteriaResolver -} from "../lib/ConsiderationStructs.sol"; +import { Schema, ZoneParameters } from "../lib/ConsiderationStructs.sol"; contract TestZone is ZoneInterface { - function isValidOrder( - bytes32 orderHash, - address caller, - address offerer, - bytes32 zoneHash + function validateOrder( + ZoneParameters calldata zoneParameters ) external pure override returns (bytes4 validOrderMagicValue) { - orderHash; - caller; - offerer; - - if (zoneHash == bytes32(uint256(1))) { - revert("Revert on zone hash 1"); - } else if (zoneHash == bytes32(uint256(2))) { + if (zoneParameters.extraData.length == 0) { + if (zoneParameters.zoneHash == bytes32(uint256(1))) { + revert("Revert on zone hash 1"); + } else if (zoneParameters.zoneHash == bytes32(uint256(2))) { + assembly { + revert(0, 0) + } + } + } else if (zoneParameters.extraData.length == 4) { + revert("Revert on extraData length 4"); + } else if (zoneParameters.extraData.length == 5) { assembly { revert(0, 0) } - } + } else if ( + zoneParameters.extraData.length > 32 && + zoneParameters.extraData.length % 32 == 0 + ) { + bytes32[] memory expectedOrderHashes = abi.decode( + zoneParameters.extraData, + (bytes32[]) + ); - validOrderMagicValue = zoneHash != bytes32(uint256(3)) - ? ZoneInterface.isValidOrder.selector - : bytes4(0xffffffff); - } + uint256 expectedLength = expectedOrderHashes.length; - function isValidOrderIncludingExtraData( - bytes32 orderHash, - address caller, - AdvancedOrder calldata order, - bytes32[] calldata priorOrderHashes, - CriteriaResolver[] calldata criteriaResolvers - ) external pure override returns (bytes4 validOrderMagicValue) { - orderHash; - caller; - order; - priorOrderHashes; - criteriaResolvers; + if (expectedLength != zoneParameters.orderHashes.length) { + revert("Revert on unexpected order hashes length"); + } - if (order.extraData.length == 4) { - revert("Revert on extraData length 4"); - } else if (order.extraData.length == 5) { - assembly { - revert(0, 0) + for (uint256 i = 0; i < expectedLength; ++i) { + if (expectedOrderHashes[i] != zoneParameters.orderHashes[i]) { + revert("Revert on unexpected order hash"); + } } } - validOrderMagicValue = order.parameters.zoneHash != bytes32(uint256(3)) - ? ZoneInterface.isValidOrder.selector + validOrderMagicValue = zoneParameters.zoneHash != bytes32(uint256(3)) + ? ZoneInterface.validateOrder.selector : bytes4(0xffffffff); } + + /** + * @dev Returns the metadata for this zone. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 3003; + schemas[0].metadata = new bytes(0); + + return ("TestZone", schemas); + } } diff --git a/contracts/zones/PausableZone.sol b/contracts/zones/PausableZone.sol new file mode 100644 index 000000000..6abf08d1d --- /dev/null +++ b/contracts/zones/PausableZone.sol @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { ZoneInterface } from "../interfaces/ZoneInterface.sol"; + +import { + PausableZoneEventsAndErrors +} from "./interfaces/PausableZoneEventsAndErrors.sol"; + +import { SeaportInterface } from "../interfaces/SeaportInterface.sol"; + +import { + AdvancedOrder, + CriteriaResolver, + Execution, + Fulfillment, + Order, + OrderComponents, + Schema, + ZoneParameters +} from "../lib/ConsiderationStructs.sol"; + +import { PausableZoneInterface } from "./interfaces/PausableZoneInterface.sol"; + +/** + * @title PausableZone + * @author cupOJoseph, BCLeFevre, ryanio + * @notice PausableZone is a simple zone implementation that approves every + * order. It can be self-destructed by its controller to pause + * restricted orders that have it set as their zone. Note that this zone + * cannot execute orders that return native tokens to the fulfiller. + */ +contract PausableZone is + PausableZoneEventsAndErrors, + ZoneInterface, + PausableZoneInterface +{ + // Set an immutable controller that can pause the zone & update an operator. + address internal immutable _controller; + + // Set an operator that can instruct the zone to cancel or execute orders. + address public operator; + + /** + * @dev Ensure that the caller is either the operator or controller. + */ + modifier isOperator() { + // Ensure that the caller is either the operator or the controller. + if (msg.sender != operator && msg.sender != _controller) { + revert InvalidOperator(); + } + + // Continue with function execution. + _; + } + + /** + * @dev Ensure that the caller is the controller. + */ + modifier isController() { + // Ensure that the caller is the controller. + if (msg.sender != _controller) { + revert InvalidController(); + } + + // Continue with function execution. + _; + } + + /** + * @notice Set the deployer as the controller of the zone. + */ + constructor() { + // Set the controller to the deployer. + _controller = msg.sender; + + // Emit an event signifying that the zone is unpaused. + emit Unpaused(); + } + + /** + * @notice Cancel an arbitrary number of orders that have agreed to use the + * contract as their zone. + * + * @param seaport The Seaport address. + * @param orders The orders to cancel. + * + * @return cancelled A boolean indicating whether the supplied orders have + * been successfully cancelled. + */ + function cancelOrders( + SeaportInterface seaport, + OrderComponents[] calldata orders + ) external override isOperator returns (bool cancelled) { + // Call cancel on Seaport and return its boolean value. + cancelled = seaport.cancel(orders); + } + + /** + * @notice Pause this contract, safely stopping orders from using + * the contract as a zone. Restricted orders with this address as a + * zone will not be fulfillable unless the zone is redeployed to the + * same address. + */ + function pause(address payee) external override isController { + // Emit an event signifying that the zone is paused. + emit Paused(); + + // Destroy the zone, sending any native tokens to the transaction + // submitter. + selfdestruct(payable(payee)); + } + + /** + * @notice Assign the given address with the ability to operate the zone. + * + * @param operatorToAssign The address to assign as the operator. + */ + function assignOperator( + address operatorToAssign + ) external override isController { + // Ensure the operator being assigned is not the null address. + if (operatorToAssign == address(0)) { + revert PauserCanNotBeSetAsZero(); + } + + // Set the given address as the new operator. + operator = operatorToAssign; + + // Emit an event indicating the operator has been updated. + emit OperatorUpdated(operatorToAssign); + } + + /** + * @notice Execute an arbitrary number of matched orders, each with + * an arbitrary number of items for offer and consideration + * along with a set of fulfillments allocating offer components + * to consideration components. Note that this call will revert if + * excess native tokens are returned by Seaport. + * + * @param seaport The Seaport address. + * @param orders The orders to match. + * @param fulfillments An array of elements allocating offer components + * to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchOrders( + SeaportInterface seaport, + Order[] calldata orders, + Fulfillment[] calldata fulfillments + ) + external + payable + override + isOperator + returns (Execution[] memory executions) + { + // Call matchOrders on Seaport and return the sequence of transfers + // performed as part of matching the given orders. + executions = seaport.matchOrders{ value: msg.value }( + orders, + fulfillments + ); + } + + /** + * @notice Execute an arbitrary number of matched advanced orders, + * each with an arbitrary number of items for offer and + * consideration along with a set of fulfillments allocating + * offer components to consideration components. Note that this call + * will revert if excess native tokens are returned by Seaport. + * + * @param seaport The Seaport address. + * @param orders The orders to match. + * @param criteriaResolvers An array where each element contains a reference + * to a specific order as well as that order's + * offer or consideration, a token identifier, and + * a proof that the supplied token identifier is + * contained in the order's merkle root. + * @param fulfillments An array of elements allocating offer components + * to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchAdvancedOrders( + SeaportInterface seaport, + AdvancedOrder[] calldata orders, + CriteriaResolver[] calldata criteriaResolvers, + Fulfillment[] calldata fulfillments + ) + external + payable + override + isOperator + returns (Execution[] memory executions) + { + // Call matchAdvancedOrders on Seaport and return the sequence of + // transfers performed as part of matching the given orders. + executions = seaport.matchAdvancedOrders{ value: msg.value }( + orders, + criteriaResolvers, + fulfillments, + msg.sender + ); + } + + /** + * @notice Check if a given order including extraData is currently valid. + * + * @dev This function is called by Seaport whenever any extraData is + * provided by the caller. + * + * @custom:param zoneParameters A struct that provides context about the + * order fulfillment and any supplied + * extraData, as well as all order hashes + * fulfilled in a call to a match or + * fulfillAvailable method. + * + * @return validOrderMagicValue A magic value indicating if the order is + * currently valid. + */ + function validateOrder( + /** + * @custom:name zoneParameters + */ + ZoneParameters calldata + ) external pure override returns (bytes4 validOrderMagicValue) { + // Return the selector of isValidOrder as the magic value. + validOrderMagicValue = ZoneInterface.validateOrder.selector; + } + + /** + * @dev Returns the metadata for this zone. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 3003; + schemas[0].metadata = new bytes(0); + + return ("PausableZone", schemas); + } +} diff --git a/contracts/zones/PausableZoneController.sol b/contracts/zones/PausableZoneController.sol new file mode 100644 index 000000000..14662e518 --- /dev/null +++ b/contracts/zones/PausableZoneController.sol @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { PausableZone } from "./PausableZone.sol"; + +import { + PausableZoneControllerInterface +} from "./interfaces/PausableZoneControllerInterface.sol"; + +import { + PausableZoneEventsAndErrors +} from "./interfaces/PausableZoneEventsAndErrors.sol"; + +import { + AdvancedOrder, + CriteriaResolver, + Execution, + Fulfillment, + Order, + OrderComponents +} from "../lib/ConsiderationStructs.sol"; + +import { SeaportInterface } from "../interfaces/SeaportInterface.sol"; + +/** + * @title PausableZoneController + * @author cupOJoseph, BCLeFevre, stuckinaboot, stephankmin + * @notice PausableZoneController enables deploying, pausing and executing + * orders on PausableZones. This deployer is designed to be owned + * by a gnosis safe, DAO, or trusted party. + */ +contract PausableZoneController is + PausableZoneControllerInterface, + PausableZoneEventsAndErrors +{ + // Set the owner that can deploy, pause and execute orders on PausableZones. + address internal _owner; + + // Set the address of the new potential owner of the zone. + address private _potentialOwner; + + // Set the address with the ability to pause the zone. + address internal _pauser; + + // Set the immutable zone creation code hash. + bytes32 public immutable zoneCreationCode; + + /** + * @dev Throws if called by any account other than the owner or pauser. + */ + modifier isPauser() { + if (msg.sender != _pauser && msg.sender != _owner) { + revert InvalidPauser(); + } + _; + } + + /** + * @notice Set the owner of the controller and store + * the zone creation code. + * + * @param ownerAddress The deployer to be set as the owner. + */ + constructor(address ownerAddress) { + // Set the owner address as the owner. + _owner = ownerAddress; + + // Hash and store the zone creation code. + zoneCreationCode = keccak256(type(PausableZone).creationCode); + } + + /** + * @notice Deploy a PausableZone to a precomputed address. + * + * @param salt The salt to be used to derive the zone address + * + * @return derivedAddress The derived address for the zone. + */ + function createZone( + bytes32 salt + ) external override returns (address derivedAddress) { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + + // Derive the PausableZone address. + // This expression demonstrates address computation but is not required. + derivedAddress = address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + salt, + zoneCreationCode + ) + ) + ) + ) + ); + + // Revert if a zone is currently deployed to the derived address. + if (derivedAddress.code.length != 0) { + revert ZoneAlreadyExists(derivedAddress); + } + + // Deploy the zone using the supplied salt. + new PausableZone{ salt: salt }(); + + // Emit an event signifying that the zone was created. + emit ZoneCreated(derivedAddress, salt); + } + + /** + * @notice Pause orders on a given zone. + * + * @param zone The address of the zone to be paused. + * + * @return success A boolean indicating the zone has been paused. + */ + function pause( + address zone + ) external override isPauser returns (bool success) { + // Call pause on the given zone. + PausableZone(zone).pause(msg.sender); + + // Return a boolean indicating the pause was successful. + success = true; + } + + /** + * @notice Cancel Seaport orders on a given zone. + * + * @param pausableZoneAddress The zone that manages the + * orders to be cancelled. + * @param seaportAddress The Seaport address. + * @param orders The orders to cancel. + */ + function cancelOrders( + address pausableZoneAddress, + SeaportInterface seaportAddress, + OrderComponents[] calldata orders + ) external override { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + + // Create a zone object from the zone address. + PausableZone zone = PausableZone(pausableZoneAddress); + + // Call cancelOrders on the given zone. + zone.cancelOrders(seaportAddress, orders); + } + + /** + * @notice Execute an arbitrary number of matched orders on a given zone. + * + * @param pausableZoneAddress The zone that manages the orders + * to be cancelled. + * @param seaportAddress The Seaport address. + * @param orders The orders to match. + * @param fulfillments An array of elements allocating offer + * components to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchOrders( + address pausableZoneAddress, + SeaportInterface seaportAddress, + Order[] calldata orders, + Fulfillment[] calldata fulfillments + ) external payable override returns (Execution[] memory executions) { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + + // Create a zone object from the zone address. + PausableZone zone = PausableZone(pausableZoneAddress); + + // Call executeMatchOrders on the given zone and return the sequence + // of transfers performed as part of matching the given orders. + executions = zone.executeMatchOrders{ value: msg.value }( + seaportAddress, + orders, + fulfillments + ); + } + + /** + * @notice Execute an arbitrary number of matched advanced orders on a given + * zone. + * + * @param pausableZoneAddress The zone that manages the orders to be + * cancelled. + * @param seaportAddress The Seaport address. + * @param orders The orders to match. + * @param criteriaResolvers An array where each element contains a + * reference to a specific order as well as that + * order's offer or consideration, a token + * identifier, and a proof that the supplied + * token identifier is contained in the + * order's merkle root. + * @param fulfillments An array of elements allocating offer + * components to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchAdvancedOrders( + address pausableZoneAddress, + SeaportInterface seaportAddress, + AdvancedOrder[] calldata orders, + CriteriaResolver[] calldata criteriaResolvers, + Fulfillment[] calldata fulfillments + ) external payable override returns (Execution[] memory executions) { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + + // Create a zone object from the zone address. + PausableZone zone = PausableZone(pausableZoneAddress); + + // Call executeMatchOrders on the given zone and return the sequence + // of transfers performed as part of matching the given orders. + executions = zone.executeMatchAdvancedOrders{ value: msg.value }( + seaportAddress, + orders, + criteriaResolvers, + fulfillments + ); + } + + /** + * @notice Initiate Zone ownership transfer by assigning a new potential + * owner this contract. Once set, the new potential owner + * may call `acceptOwnership` to claim ownership. + * Only the owner in question may call this function. + * + * @param newPotentialOwner The address for which to initiate ownership + * transfer to. + */ + function transferOwnership(address newPotentialOwner) external override { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + // Ensure the new potential owner is not an invalid address. + if (newPotentialOwner == address(0)) { + revert OwnerCanNotBeSetAsZero(); + } + + // Emit an event indicating that the potential owner has been updated. + emit PotentialOwnerUpdated(newPotentialOwner); + + // Set the new potential owner as the potential owner. + _potentialOwner = newPotentialOwner; + } + + /** + * @notice Clear the currently set potential owner, if any. + * Only the owner of this contract may call this function. + */ + function cancelOwnershipTransfer() external override { + // Ensure the caller is the current owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + + // Emit an event indicating that the potential owner has been cleared. + emit PotentialOwnerUpdated(address(0)); + + // Clear the current new potential owner. + delete _potentialOwner; + } + + /** + * @notice Accept ownership of this contract. Only the account that the + * current owner has set as the new potential owner may call this + * function. + */ + function acceptOwnership() external override { + // Ensure the caller is the potential owner. + if (msg.sender != _potentialOwner) { + revert CallerIsNotPotentialOwner(); + } + + // Emit an event indicating that the potential owner has been cleared. + emit PotentialOwnerUpdated(address(0)); + + // Clear the current new potential owner + delete _potentialOwner; + + // Emit an event indicating ownership has been transferred. + emit OwnershipTransferred(_owner, msg.sender); + + // Set the caller as the owner of this contract. + _owner = msg.sender; + } + + /** + * @notice Assign the given address with the ability to pause the zone. + * + * @param pauserToAssign The address to assign the pauser role. + */ + function assignPauser(address pauserToAssign) external override { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + // Ensure the pauser to assign is not an invalid address. + if (pauserToAssign == address(0)) { + revert PauserCanNotBeSetAsZero(); + } + + // Set the given account as the pauser. + _pauser = pauserToAssign; + + // Emit an event indicating the pauser has been assigned. + emit PauserUpdated(pauserToAssign); + } + + /** + * @notice Assign the given address with the ability to operate the + * given zone. + * + * @param pausableZoneAddress The zone address to assign operator role. + * @param operatorToAssign The address to assign as operator. + */ + function assignOperator( + address pausableZoneAddress, + address operatorToAssign + ) external override { + // Ensure the caller is the owner. + if (msg.sender != _owner) { + revert CallerIsNotOwner(); + } + // Create a zone object from the zone address. + PausableZone zone = PausableZone(pausableZoneAddress); + + // Call assignOperator on the zone by passing in the given + // operator address. + zone.assignOperator(operatorToAssign); + } + + /** + * @notice An external view function that returns the owner. + * + * @return The address of the owner. + */ + function owner() external view override returns (address) { + return _owner; + } + + /** + * @notice An external view function that return the potential owner. + * + * @return The address of the potential owner. + */ + function potentialOwner() external view override returns (address) { + return _potentialOwner; + } + + /** + * @notice An external view function that returns the pauser. + * + * @return The address of the pauser. + */ + function pauser() external view override returns (address) { + return _pauser; + } +} diff --git a/contracts/zones/interfaces/PausableZoneControllerInterface.sol b/contracts/zones/interfaces/PausableZoneControllerInterface.sol new file mode 100644 index 000000000..8c115556c --- /dev/null +++ b/contracts/zones/interfaces/PausableZoneControllerInterface.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + AdvancedOrder, + CriteriaResolver, + Execution, + Fulfillment, + Order, + OrderComponents +} from "../../lib/ConsiderationStructs.sol"; + +import { SeaportInterface } from "../../interfaces/SeaportInterface.sol"; + +/** + * @title PausableZoneController + * @author cupOJoseph, BCLeFevre, stuckinaboot + * @notice PausableZoneController enables deploying, pausing and executing + * orders on PausableZones. This deployer is designed to be owned + * by a gnosis safe, DAO, or trusted party. + */ +interface PausableZoneControllerInterface { + /** + * @notice Deploy a PausableZone to a precomputed address. + * + * @param salt The salt to be used to derive the zone address + * + * @return derivedAddress The derived address for the zone. + */ + function createZone(bytes32 salt) external returns (address derivedAddress); + + /** + * @notice Pause orders on a given zone. + * + * @param zone The address of the zone to be paused. + * + * @return success A boolean indicating the zone has been paused. + */ + function pause(address zone) external returns (bool success); + + /** + * @notice Cancel Seaport offers on a given zone. + * + * @param pausableZoneAddress The zone that manages the orders to be + * cancelled. + * @param seaportAddress The Seaport address. + * @param orders The orders to cancel. + */ + function cancelOrders( + address pausableZoneAddress, + SeaportInterface seaportAddress, + OrderComponents[] calldata orders + ) external; + + /** + * @notice Execute an arbitrary number of matched orders on a given zone. + * + * @param pausableZoneAddress The zone that manages the orders to be + * cancelled. + * @param seaportAddress The Seaport address. + * @param orders The orders to match. + * @param fulfillments An array of elements allocating offer + * components to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchOrders( + address pausableZoneAddress, + SeaportInterface seaportAddress, + Order[] calldata orders, + Fulfillment[] calldata fulfillments + ) external payable returns (Execution[] memory executions); + + /** + * @notice Execute an arbitrary number of matched advanced orders on a + * given zone. + * + * @param pausableZoneAddress The zone that manages the orders to be + * cancelled. + * @param seaportAddress The Seaport address. + * @param orders The orders to match. + * @param criteriaResolvers An array where each element contains a + * reference to a specific order as well as + * that order's offer or consideration, + * a token identifier, and a proof that + * the supplied token identifier is + * contained in the order's merkle root. + * @param fulfillments An array of elements allocating offer + * components to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchAdvancedOrders( + address pausableZoneAddress, + SeaportInterface seaportAddress, + AdvancedOrder[] calldata orders, + CriteriaResolver[] calldata criteriaResolvers, + Fulfillment[] calldata fulfillments + ) external payable returns (Execution[] memory executions); + + /** + * @notice Initiate Zone ownership transfer by assigning a new potential + * owner this contract. Once set, the new potential owner + * may call `acceptOwnership` to claim ownership. + * Only the owner in question may call this function. + * + * @param newPotentialOwner The address for which to initiate ownership + * transfer to. + */ + function transferOwnership(address newPotentialOwner) external; + + /** + * @notice Clear the currently set potential owner, if any. + * Only the owner of this contract may call this function. + */ + function cancelOwnershipTransfer() external; + + /** + * @notice Accept ownership of this contract. Only the account that the + * current owner has set as the new potential owner may call this + * function. + */ + function acceptOwnership() external; + + /** + * @notice Assign the given address with the ability to pause the zone. + * + * @param pauserToAssign The address to assign the pauser role. + */ + function assignPauser(address pauserToAssign) external; + + /** + * @notice Assign the given address with the ability to operate the + * given zone. + * + * @param pausableZoneAddress The zone address to assign operator role. + * @param operatorToAssign The address to assign as operator. + */ + function assignOperator( + address pausableZoneAddress, + address operatorToAssign + ) external; + + /** + * @notice An external view function that returns the owner. + * + * @return The address of the owner. + */ + function owner() external view returns (address); + + /** + * @notice An external view function that return the potential owner. + * + * @return The address of the potential owner. + */ + function potentialOwner() external view returns (address); + + /** + * @notice An external view function that returns the pauser. + * + * @return The address of the pauser. + */ + function pauser() external view returns (address); +} diff --git a/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol b/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol new file mode 100644 index 000000000..459343631 --- /dev/null +++ b/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/** + * @notice PausableZoneEventsAndErrors contains errors and events + * related to zone interaction. + */ +interface PausableZoneEventsAndErrors { + /** + * @dev Emit an event whenever a zone is successfully paused. + */ + event Paused(); + + /** + * @dev Emit an event whenever a zone is successfully unpaused (created). + */ + event Unpaused(); + + /** + * @dev Emit an event whenever a zone owner registers a new potential + * owner for that zone. + * + * @param newPotentialOwner The new potential owner of the zone. + */ + event PotentialOwnerUpdated(address newPotentialOwner); + + /** + * @dev Emit an event whenever zone ownership is transferred. + * + * @param previousOwner The previous owner of the zone. + * @param newOwner The new owner of the zone. + */ + event OwnershipTransferred(address previousOwner, address newOwner); + + /** + * @dev Emit an event whenever a new zone is created. + * + * @param zone The address of the zone. + * @param salt The salt used to deploy the zone. + */ + event ZoneCreated(address zone, bytes32 salt); + + /** + * @dev Emit an event whenever a zone owner assigns a new pauser + * + * @param newPauser The new pausear of the zone. + */ + event PauserUpdated(address newPauser); + + /** + * @dev Emit an event whenever a zone owner assigns a new operator + * + * @param newOperator The new operator of the zone. + */ + event OperatorUpdated(address newOperator); + + /** + * @dev Revert with an error when attempting to pause the zone + * while the caller is not the owner or pauser of the zone. + */ + error InvalidPauser(); + + /** + * @dev Revert with an error when attempting to call an operation + * while the caller is not the controller or operator of the zone. + */ + error InvalidOperator(); + + /** + * @dev Revert with an error when attempting to pause the zone or update the + * operator while the caller is not the controller of the zone. + */ + error InvalidController(); + /** + * @dev Revert with an error when attempting to deploy a zone that is + * currently deployed. + */ + error ZoneAlreadyExists(address zone); + + /** + * @dev Revert with an error when the caller does not have the _owner role + * + */ + error CallerIsNotOwner(); + + /** + * @dev Revert with an error when the caller does not have the operator role + * + */ + error CallerIsNotOperator(); + + /** + * @dev Revert with an error when attempting to set the new potential owner + * as the 0 address. + * + */ + error OwnerCanNotBeSetAsZero(); + + /** + * @dev Revert with an error when attempting to set the new potential pauser + * as the 0 address. + * + */ + error PauserCanNotBeSetAsZero(); + + /** + * @dev Revert with an error when the caller does not have + * the potentialOwner role. + */ + error CallerIsNotPotentialOwner(); +} diff --git a/contracts/zones/interfaces/PausableZoneInterface.sol b/contracts/zones/interfaces/PausableZoneInterface.sol new file mode 100644 index 000000000..42b5dfc0e --- /dev/null +++ b/contracts/zones/interfaces/PausableZoneInterface.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { SeaportInterface } from "../../interfaces/SeaportInterface.sol"; + +import { + AdvancedOrder, + CriteriaResolver, + Execution, + Fulfillment, + Order, + OrderComponents +} from "../../lib/ConsiderationStructs.sol"; + +/** + * @title PausableZone + * @author cupOJoseph, BCLeFevre, ryanio + * @notice PausableZone is a simple zone implementation that approves every + * order. It can be self-destructed by its controller to pause + * restricted orders that have it set as their zone. + */ +interface PausableZoneInterface { + /** + * @notice Cancel an arbitrary number of orders that have agreed to use the + * contract as their zone. + * + * @param seaport The Seaport address. + * @param orders The orders to cancel. + * + * @return cancelled A boolean indicating whether the supplied orders have + * been successfully cancelled. + */ + function cancelOrders( + SeaportInterface seaport, + OrderComponents[] calldata orders + ) external returns (bool cancelled); + + /** + * @notice Execute an arbitrary number of matched orders, each with + * an arbitrary number of items for offer and consideration + * along with a set of fulfillments allocating offer components + * to consideration components. + * + * @param seaport The Seaport address. + * @param orders The orders to match. + * @param fulfillments An array of elements allocating offer components + * to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchOrders( + SeaportInterface seaport, + Order[] calldata orders, + Fulfillment[] calldata fulfillments + ) external payable returns (Execution[] memory executions); + + /** + * @notice Execute an arbitrary number of matched advanced orders, + * each with an arbitrary number of items for offer and + * consideration along with a set of fulfillments allocating + * offer components to consideration components. + * + * @param seaport The Seaport address. + * @param orders The orders to match. + * @param criteriaResolvers An array where each element contains a reference + * to a specific order as well as that order's + * offer or consideration, a token identifier, and + * a proof that the supplied token identifier is + * contained in the order's merkle root. + * @param fulfillments An array of elements allocating offer components + * to consideration components. + * + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. + */ + function executeMatchAdvancedOrders( + SeaportInterface seaport, + AdvancedOrder[] calldata orders, + CriteriaResolver[] calldata criteriaResolvers, + Fulfillment[] calldata fulfillments + ) external payable returns (Execution[] memory executions); + + /** + * @notice Pause this contract, safely stopping orders from using + * the contract as a zone. Restricted orders with this address as a + * zone will not be fulfillable unless the zone is redeployed to the + * same address. + */ + function pause(address payee) external; + + /** + * @notice Assign the given address with the ability to operate the zone. + * + * @param operatorToAssign The address to assign as the operator. + */ + function assignOperator(address operatorToAssign) external; +} diff --git a/diagrams/Seaport.drawio.svg b/diagrams/Seaport.drawio.svg index f9c8a13e0..95de479fc 100644 --- a/diagrams/Seaport.drawio.svg +++ b/diagrams/Seaport.drawio.svg @@ -1,4 +1,4 @@ -OfferItem
ItemType
TokenAddress
IdentifierOrCriteria
StartAmount
EndAmount
ItemType...
ConsiderationItem
ItemType
TokenAddress
IdentifierOrCriteria
StartAmount
EndAmount

Recipient
ItemType...
Order
Offer (array)
Consideration (array)
Offer (array)...
Offerer
Signature

OrderType
StartTime
EndTime
Counter
Salt

ConduitKey
Zone
ZoneHash
Offerer...

Seaport

Seaport
Flowchart
Flowchart
OrderType
FullOpen
PartialOpen
FullRestricted
PartialRestricted
FullOpen...
ItemType
Native (ETH)
ERC20
ERC721
ERC1155
Native (ETH)...
Executor
Executor
Verifiers
VerifyTime
VerifySignature
VerifyOrderStatus
VerifyTime...
SeaportInterface
FulfillOrder
FulfillOrder
MatchOrder
MatchOrder
ValidateOrder
ValidateOrder
CancelOrder
CancelOrder
IncrementCounter
IncrementCounter

GetOrderHash GetOrderStatus GetCounter
Information Name

GetOrderHash GetOrderStatus GetCounter...
ConduitTransfer
ItemType
Token
From
To
Identifier
Amount
ItemType...
ConsiderationErrors
OrderAlreadyFilled
InvalidTime
InvalidConduit
MissingOriginalConsiderationItems
InvalidCallToConduit
ConsiderationNotMet
InsufficientEtherSupplied
EtherTransferGenericFailure
PartialFillsNotEnabledForOrder
OrderIsCancelled
OrderPartiallyFilled
InvalidCanceller
BadFraction
InvalidMsgValue
InvalidBasicOrderParameterEncoding
NoSpecifiedOrdersAvailable
OrderAlreadyFilled...
ZoneInteractionErrors
InvalidRestrictedOrder
InvalidRestrictedOrder
AdvancedOrder

FulfillerConduitKey

Numerator
Denominator
ExtraData

FulfillerConduitKey...
TokenTransferrerErrors
InvalidERC721TransferAmount
MissingItemAmount
UnusedItemParameters
TokenTransferGenericFailure
ERC1155BatchTransferGenericFailure
BadReturnValueFromERC20OnTransfer
NoContract
InvalidERC721TransferAmount...
CriteriaResolutionErrors
OrderCriteriaResolverOutOfRange
UnresolvedOfferCriteria
UnresolvedConsiderationCriteria
OfferCriteriaResolverOutOfRange
ConsiderationCriteriaResolverOutOfRange
CriteriaNotEnabledForItem
InvalidProof
OrderCriteriaResolverOutOfRange...
BasicOrder

FulfillerConduitKey

AdditionalRecipients

FulfillerConduitKey...
OrderCombiner
OrderCombiner
OrderFulfiller
OrderFulfiller
FulfillmentApplicationErrors
MissingFulfillmentComponentOnAggregation
OfferAndConsiderationRequiredOnFulfillment
MismatchedFulfillmentOfferAndConsiderationComponents
InvalidFulfillmentComponentData
MissingFulfillmentComponentOnAggregation...
SignatureVerificationErrors
BadSignatureV
InvalidSigner
InvalidSignature
BadContractSignature
BadSignatureV...
OrderValidator
OrderValidator
AmountDeriver
AmountDeriver
TokenTransferrer
TokenTransferrer
Conduit
Conduit
Zone.isValidOrder
Zone.isValidOrder
FulfillmentApplier
FulfillmentApplier
CriteriaResolution
CriteriaResolution
ConsiderationEvents
OrderFulfilled
OrderCancelled
OrderValidated
CounterIncremented
OrderFulfilled...
AmountDerivationError
InexactFraction
InexactFraction
ConduitErrors
ChannelClosed
InvalidItemType
Invalid1155BatchTransferEncoding
ChannelClosed...
Restricted means zone
must give approval.
Restricted means zone...
Text is not SVG - cannot display
\ No newline at end of file +OfferItem
ItemType
TokenAddress
IdentifierOrCriteria
StartAmount
EndAmount
ItemType...
ConsiderationItem
ItemType
TokenAddress
IdentifierOrCriteria
StartAmount
EndAmount

Recipient
ItemType...
Order
Offer (array)
Consideration (array)
Offer (array)...
Offerer
Signature

OrderType
StartTime
EndTime
Counter
Salt

ConduitKey
Zone
ZoneHash
Offerer...

Seaport

Seaport
Flowchart
Flowchart
OrderType
FullOpen
PartialOpen
FullRestricted
PartialRestricted
FullOpen...
ItemType
Native (ETH)
ERC20
ERC721
ERC1155
Native (ETH)...
Executor
Executor
Verifiers
VerifyTime
VerifySignature
VerifyOrderStatus
VerifyTime...
SeaportInterface
FulfillOrder
FulfillOrder
MatchOrder
MatchOrder
ValidateOrder
ValidateOrder
CancelOrder
CancelOrder
IncrementCounter
IncrementCounter

GetOrderHash GetOrderStatus GetCounter
Information Name

GetOrderHash GetOrderStatus GetCounter...
ConduitTransfer
ItemType
Token
From
To
Identifier
Amount
ItemType...
ConsiderationErrors
OrderAlreadyFilled
InvalidTime
InvalidConduit
MissingOriginalConsiderationItems
InvalidCallToConduit
ConsiderationNotMet
InsufficientNativeTokensSupplied
NativeTokenTransferGenericFailure
PartialFillsNotEnabledForOrder
OrderIsCancelled
OrderPartiallyFilled
InvalidCanceller
BadFraction
InvalidMsgValue
InvalidBasicOrderParameterEncoding
NoSpecifiedOrdersAvailable
OrderAlreadyFilled...
ZoneInteractionErrors
InvalidRestrictedOrder
InvalidRestrictedOrder
AdvancedOrder

FulfillerConduitKey

Numerator
Denominator
ExtraData

FulfillerConduitKey...
TokenTransferrerErrors
InvalidERC721TransferAmount
MissingItemAmount
UnusedItemParameters
TokenTransferGenericFailure
ERC1155BatchTransferGenericFailure
BadReturnValueFromERC20OnTransfer
NoContract
InvalidERC721TransferAmount...
CriteriaResolutionErrors
OrderCriteriaResolverOutOfRange
UnresolvedOfferCriteria
UnresolvedConsiderationCriteria
OfferCriteriaResolverOutOfRange
ConsiderationCriteriaResolverOutOfRange
CriteriaNotEnabledForItem
InvalidProof
OrderCriteriaResolverOutOfRange...
BasicOrder

FulfillerConduitKey

AdditionalRecipients

FulfillerConduitKey...
OrderCombiner
OrderCombiner
OrderFulfiller
OrderFulfiller
FulfillmentApplicationErrors
MissingFulfillmentComponentOnAggregation
OfferAndConsiderationRequiredOnFulfillment
MismatchedFulfillmentOfferAndConsiderationComponents
InvalidFulfillmentComponentData
MissingFulfillmentComponentOnAggregation...
SignatureVerificationErrors
BadSignatureV
InvalidSigner
InvalidSignature
BadContractSignature
BadSignatureV...
OrderValidator
OrderValidator
AmountDeriver
AmountDeriver
TokenTransferrer
TokenTransferrer
Conduit
Conduit
Zone.isValidOrder
Zone.isValidOrder
FulfillmentApplier
FulfillmentApplier
CriteriaResolution
CriteriaResolution
ConsiderationEvents
OrderFulfilled
OrderCancelled
OrderValidated
CounterIncremented
OrderFulfilled...
AmountDerivationError
InexactFraction
InexactFraction
ConduitErrors
ChannelClosed
InvalidItemType
Invalid1155BatchTransferEncoding
ChannelClosed...
Restricted means zone
must give approval.
Restricted means zone...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/Deployment.md b/docs/Deployment.md new file mode 100644 index 000000000..4226d2711 --- /dev/null +++ b/docs/Deployment.md @@ -0,0 +1,120 @@ +# Deploying Seaport + +Seaport 1.1, 1.2, and the ConduitController can each be deployed to their respective canonical deployment address on all EVM chains using the CREATE2 Factory. + +## Relevant Addresses + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAddress
KEYLESS_CREATE2_DEPLOYER_ADDRESS0x4c8D290a1B368ac4728d83a9e8321fC3af2b39b1
KEYLESS_CREATE2_ADDRESS0x7A0D94F55792C434d74a40883C6ed8545E406D12
INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS0xcfA3A7637547094fF06246817a35B8333C315196
IMMUTABLE_CREATE2_FACTORY_ADDRESS0x0000000000ffe8b47b3e2130213b802212439497
ConduitController0x00000000F9490004C11Cef243f5400493c00Ad63
Seaport 1.10x00000000006c3852cbEf3e08E8dF289169EdE581
Seaport 1.20x00000000000006c7676171937C444f6BDe3D6282
+ +--- + +## Setting up Factory on a New Chain + +If there is no `IMMUTABLE_CREATE2_FACTORY_ADDRESS` on the chain, deploy this first with foundry. + +1. Send 0.01 Ether to the `KEYLESS_CREATE2_DEPLOYER_ADDRESS` +2. Create the `KEYLESS_CREATE2_ADDRESS` by submitting this pre-signed transaction: + +``` +cast publish --rpc-url ${RPC_URL} 0xf87e8085174876e800830186a08080ad601f80600e600039806000f350fe60003681823780368234f58015156014578182fd5b80825250506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222 +``` + +3. Create the `INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS` by submitting: + +``` +cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x7a0d94f55792c434d74a40883c6ed8545e406d12 0x608060405234801561001057600080fd5b50610833806100206000396000f3fe60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a0032 +``` + +4. Create the `IMMUTABLE_CREATE2_FACTORY_ADDRESS` contract at `0x0000000000ffe8b47b3e2130213b802212439497` by submitting: + +``` +cast send --rpc-url ${RPC_URL} --private-key ${PK} 0xcfa3a7637547094ff06246817a35b8333c315196 0x64e030870000000000000000000000000000000000000000f4b0218f13a6440a6f02000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000853608060405234801561001057600080fd5b50610833806100206000396000f3fe60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a003200000000000000000000000000 +``` + +## Deploying Seaport and ConduitController + +Once the `IMMUTABLE_CREATE2_FACTORY_ADDRESS` exists, begin to deploy the contracts: + +1. Deploy the `ConduitController` contract by submitting: + +``` +cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000dc0ef3c792976604960400000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000302760c08060405234620000ea57600090610c9f906001600160401b03603f8301601f1916820181811183821017620000da575b604052828252620023889160208101908484833951902060805260405192839281840192831184841017620000ca575b8339039082f58015620000ba575b6001600160a01b03163f60a05260405161227490816200011482396080518181816102b101528181610bcc0152610d06015260a0518181816102d401528181610c620152610da90152f35b620000c462000106565b6200006f565b620000d4620000ef565b62000061565b620000e4620000ef565b62000031565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b506040513d6000823e3d90fdfe60806040526004361015610013575b600080fd5b60003560e01c8063027cc7641461012b5780630a96ad391461012257806313ad9cab1461011957806314afd79e1461011057806333bc8572146101075780634e3f9580146100fe57806351710e45146100f55780636d435421146100ec5780636e9bfd9f146100e3578063794593bc146100da5780637b37e561146100d15780638b9e028b146100c8578063906c87cc146100bf576393790f44146100b757600080fd5b61000e61126e565b5061000e6111fa565b5061000e61113c565b5061000e610fc8565b5061000e610c8a565b5061000e610b3c565b5061000e6109bf565b5061000e610765565b5061000e6106f3565b5061000e61064f565b5061000e6105db565b5061000e6102fa565b5061000e61027b565b5061000e61017a565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576101b2610134565b602435906101bf81611574565b73ffffffffffffffffffffffffffffffffffffffff80911691600083815280602052600360408220015482101561023f5790600360408361023b9661020a9552806020522001611400565b90549060031b1c166040519182918291909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0390f35b602484604051907f6ceb340b0000000000000000000000000000000000000000000000000000000082526004820152fd5b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57604080517f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020820152f35b503461000e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57610332610134565b61033a610157565b90604435918215918215840361000e5761035381611505565b73ffffffffffffffffffffffffffffffffffffffff811690813b1561000e576040517fc4e8fcb500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528515156024820152610401926000908290604490829084905af180156105ce575b6105b5575b5073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b92600484019261043183859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5491821590806105ae575b1561048157505050600361047d92930161045682826114ce565b54929073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b555b005b91949391816105a5575b5061049257005b6104df61047d938560037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600098019201916104ce83546113a4565b90808203610504575b505050611447565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b6105766105449161053b61051b61059c9588611400565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b92839187611400565b90919082549060031b9173ffffffffffffffffffffffffffffffffffffffff9283811b93849216901b16911916179055565b859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b553880806104d7565b9050153861048b565b508061043c565b806105c26105c892611335565b80610270565b386103da565b6105d6611397565b6103d5565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610615610134565b61061e81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260016040600020015416604051908152f35b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206106e861068c610134565b73ffffffffffffffffffffffffffffffffffffffff6106a9610157565b916106b381611574565b166000526000835260046040600020019073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b541515604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff610740610134565b61074981611574565b1660005260006020526020600360406000200154604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5761079d610134565b6107a681611574565b61080c6107f360026107d88473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b33036109765761047f9060007f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a2610896600261086d8373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b73ffffffffffffffffffffffffffffffffffffffff3390806108dd60016107d88673ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b169083167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec6000604051a46001610935339273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6040517f88c3a11500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576109f7610134565b6109ff610157565b90610a0981611505565b73ffffffffffffffffffffffffffffffffffffffff808316908115610b095750610a5b6107f360026107d88573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b8114610ab95761093561047f93926002927f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da6000604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b506040517fcbc080ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b82602491604051917fa388d263000000000000000000000000000000000000000000000000000000008352166004820152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576040517fff00000000000000000000000000000000000000000000000000000000000000602082019081523060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260043560358301527f0000000000000000000000000000000000000000000000000000000000000000605583015273ffffffffffffffffffffffffffffffffffffffff91610c3b81607581015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611356565b519020604080519290911673ffffffffffffffffffffffffffffffffffffffff811683523f7f000000000000000000000000000000000000000000000000000000000000000014602083015290f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610cc6610157565b73ffffffffffffffffffffffffffffffffffffffff91828216908115610f9f57338160601c03610f7657610da46107f386516020810190610d8881610c0f7f0000000000000000000000000000000000000000000000000000000000000000883087917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b51902073ffffffffffffffffffffffffffffffffffffffff1690565b92833f7f000000000000000000000000000000000000000000000000000000000000000014610f3057947f4397af6128d529b8ae0442f99db1296d5136062597a15bbc61c1b2a6431a7d15610eca838060009961023b989796865180610c9f8082019082821067ffffffffffffffff831117610f23575b6115a0833903908df515610f16575b610e9c610e578973ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b91600183019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b55835173ffffffffffffffffffffffffffffffffffffffff8716815260208101919091529081906040820190565b0390a15194859483167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec8287a473ffffffffffffffffffffffffffffffffffffffff1682526020820190565b610f1e611397565b610e2a565b610f2b611305565b610e1b565b85517f6328ccb200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b600485517fcb6e5344000000000000000000000000000000000000000000000000000000008152fd5b600485517f99faaa04000000000000000000000000000000000000000000000000000000008152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611000610134565b61100981611505565b73ffffffffffffffffffffffffffffffffffffffff9081811660009281845283602052600260408520015416156110ba575061108e600291837f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055604051f35b602490604051907f6b0136160000000000000000000000000000000000000000000000000000000082526004820152fd5b6020908160408183019282815285518094520193019160005b828110611112575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611104565b503461000e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611175610134565b9061117f82611574565b73ffffffffffffffffffffffffffffffffffffffff91826000911681528082526003604082200192604051908193808654938481520195845280842093915b8383106111e15761023b866111d5818a0382611356565b604051918291826110eb565b84548116875295810195600194850194909201916111be565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020611234610134565b61123d81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260026040600020015416604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff6112bb610134565b16600052600060205260406000205480156112db57602090604051908152f35b60046040517f4ca82090000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161134957604052565b611351611305565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761134957604052565b506040513d6000823e3d90fd5b600181106113d1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80548210156114185760005260206000200190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8054801561149f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019061147c8282611400565b73ffffffffffffffffffffffffffffffffffffffff82549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b906105446114f692805490680100000000000000008210156114f8575b600182018155611400565b565b611500611305565b6114eb565b61150e81611574565b73ffffffffffffffffffffffffffffffffffffffff809116908160005260006020526001604060002001541633036115435750565b602490604051907fd4ed9a170000000000000000000000000000000000000000000000000000000082526004820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002054156112db5756fe60a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e0033a264697066735822122031e2de61a9e35e9e87d5eef6a36b045a0bab54c4031fd01a0f8138afce3cec3164736f6c634300080e003360a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e003300000000000000000000000000000000000000000000000000 +``` + +2. Deploy the `Seaport 1.1` contract by submitting: + +``` +cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000a39d1860ddeb0e016b0900000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000670b6101c060405234620000b9576200001f6200001962000114565b62000151565b604051615f7e90816200076d82396080518161282c015260a05181612852015260c05181612809015260e051818181611758015261269701526101005181818161162401526126e60152610120518181816117f40152612734015261014051816127b7015261016051816127dd015261018051818181611003015281816122f4015261246a01526101a05181818161233201526124a80152f35b600080fd5b604081019081106001600160401b03821117620000da57604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620000da57604052565b620066eb60208138039182604051938492620001318285620000f0565b833981010312620000b957516001600160a01b0381168103620000b95790565b604060049162000160620002e3565b610120526101005260e05260c05260a05260805246610140526200018362000237565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa90811562000203575b600091620001cd575b506101a052620001cb6001600055565b565b620001f3915060403d8111620001fb575b620001ea8183620000f0565b81019062000213565b5038620001bb565b503d620001de565b6200020d6200022a565b620001b2565b9190826040910312620000b9576020825192015190565b506040513d6000823e3d90fd5b60c05160805160a0516040519160208301938452604083015260608201524660808201523060a082015260a0815260c0810181811060018060401b03821117620000da5760405251902090565b604051906200029382620000be565b6003825262312e3160e81b6020830152565b90815180926000905b828210620002cb575011620002c1570190565b6000828201520190565b915080602080928401015181850152018391620002ae565b620002ed62000747565b8051602080920120916200030062000284565b8281519101209160405181810192816200032b85600a906909ecccccae492e8cada560b31b81520190565b6e1d5a5b9d0e081a5d195b551e5c194b608a1b8152600f016d1859191c995cdcc81d1bdad95b8b60921b8152600e017f75696e74323536206964656e7469666965724f7243726974657269612c0000008152601d017f75696e74323536207374617274416d6f756e742c0000000000000000000000008152601401701d5a5b9d0c8d4d88195b99105b5bdd5b9d607a1b8152601101602960f81b81526001010392601f19938481018452620003e19084620000f0565b60405171086dedce6d2c8cae4c2e8d2dedc92e8cada560731b8282019081529481601287016e1d5a5b9d0e081a5d195b551e5c194b608a1b8152600f016d1859191c995cdcc81d1bdad95b8b60921b8152600e017f75696e74323536206964656e7469666965724f7243726974657269612c0000008152601d017f75696e74323536207374617274416d6f756e742c0000000000000000000000008152601401711d5a5b9d0c8d4d88195b99105b5bdd5b9d0b60721b8152601201701859191c995cdcc81c9958da5c1a595b9d607a1b8152601101602960f81b8152600101038181018352620004d29083620000f0565b6040519283818101620004fc906010906f09ee4c8cae486dedae0dedccadce8e6560831b81520190565b6f1859191c995cdcc81bd999995c995c8b60821b81526010016c1859191c995cdcc81e9bdb994b609a1b8152600d017113d999995c925d195b56d7481bd999995c8b60721b81526012017f436f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f8152611b8b60f21b60208201526022016f1d5a5b9d0e081bdc99195c951e5c194b60821b8152601001711d5a5b9d0c8d4d881cdd185c9d151a5b594b60721b81526012016f1d5a5b9d0c8d4d88195b99151a5b594b60821b815260100170189e5d195ccccc881e9bdb9952185cda0b607a1b81526011016c1d5a5b9d0c8d4d881cd85b1d0b609a1b8152600d017f6279746573333220636f6e647569744b65792c0000000000000000000000000081526013016e3ab4b73a191a9b1031b7bab73a32b960891b8152600f01602960f81b81526001010382810185526200064e9085620000f0565b6040516c08a92a06e626488dedac2d2dc5609b1b8282019081529080600d83016b1cdd1c9a5b99c81b985b594b60a21b8152600c016e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b8152600f016f1d5a5b9d0c8d4d8818da185a5b92590b60821b81526010017f6164647265737320766572696679696e67436f6e7472616374000000000000008152601901602960f81b8152600101038481018252620006f69082620000f0565b5190209786519020968351902095604051938492830195866200071991620002a5565b6200072491620002a5565b6200072f91620002a5565b039081018252620007419082620000f0565b51902090565b604051906200075682620000be565b600782526614d9585c1bdc9d60ca1b602083015256fe60806040526004361015610013575b600080fd5b60003560e01c806306fdde031461013f57806346423aa71461013657806355944a421461012d5780635b34b9661461012457806379df72bd1461011b57806387201b41146101125780638814773214610109578063a817440414610100578063b3a34c4c146100f7578063e7acab24146100ee578063ed98a574146100e5578063f07ec373146100dc578063f47b7740146100d3578063fb0f3ee1146100ca5763fd9f1e10146100c257600080fd5b61000e61132d565b5061000e61102c565b5061000e610f8b565b5061000e610f46565b5061000e610eb5565b5061000e610e07565b5061000e610da3565b5061000e610d32565b5061000e610be3565b5061000e610b0f565b5061000e610994565b5061000e61092f565b5061000e61089e565b5061000e6101c1565b5061000e610199565b91908251928382526000905b8482106101815750601f8460209495601f199311610174575b0116010190565b600085828601015261016d565b90602090818082850101519082860101520190610154565b503461000e57600060031936011261000e57602080526707536561706f727460475260606020f35b503461000e57602060031936011261000e57600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761027057604052565b610278610224565b604052565b60c0810190811067ffffffffffffffff82111761027057604052565b6020810190811067ffffffffffffffff82111761027057604052565b6040810190811067ffffffffffffffff82111761027057604052565b90601f601f19910116810190811067ffffffffffffffff82111761027057604052565b60405190610160820182811067ffffffffffffffff82111761027057604052565b6040519061032282610254565b565b60209067ffffffffffffffff811161033e575b60051b0190565b610346610224565b610337565b6001600160a01b0381160361000e57565b60a435906103228261034b565b35906103228261034b565b3590600682101561000e57565b92919261038d82610324565b60409461039c865192836102d1565b819584835260208093019160a080960285019481861161000e57925b8584106103c85750505050505050565b868483031261000e5784879184516103df81610254565b6103e887610374565b8152828701356103f78161034b565b83820152858701358682015260608088013590820152608080880135908201528152019301926103b8565b9080601f8301121561000e5781602061043d93359101610381565b90565b92919261044c82610324565b60409461045b865192836102d1565b819584835260208093019160c080960285019481861161000e57925b8584106104875750505050505050565b868483031261000e57848791845161049e8161027d565b6104a787610374565b8152828701356104b68161034b565b838201528587013586820152606080880135908201526080808801359082015260a080880135906104e68261034b565b820152815201930192610477565b9080601f8301121561000e5781602061043d93359101610440565b6004111561000e57565b35906103228261050f565b9190916101608184031261000e5761053a6102f4565b9261054482610369565b845261055260208301610369565b602085015267ffffffffffffffff90604083013582811161000e5781610579918501610422565b6040860152606083013591821161000e576105959183016104f4565b60608401526105a660808201610519565b608084015260a081013560a084015260c081013560c084015260e081013560e0840152610100808201359084015261012080820135908401526101408091013590830152565b35906effffffffffffffffffffffffffffff8216820361000e57565b92919267ffffffffffffffff8211610650575b604051916106336020601f19601f84011601846102d1565b82948184528183011161000e578281602093846000960137010152565b610658610224565b61061b565b9080601f8301121561000e5781602061043d93359101610608565b91909160a08184031261000e5761068d610315565b9267ffffffffffffffff823581811161000e57826106ac918501610524565b85526106ba602084016105ec565b60208601526106cb604084016105ec565b6040860152606083013581811161000e57826106e891850161065d565b6060860152608083013590811161000e57610703920161065d565b6080830152565b9080601f8301121561000e5781359061072282610324565b9261073060405194856102d1565b828452602092838086019160051b8301019280841161000e57848301915b84831061075e5750505050505090565b823567ffffffffffffffff811161000e57869161078084848094890101610678565b81520192019161074e565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156107f657565b6103226107bc565b608090805161080c816107ec565b8352816001600160a01b03918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610860575050505090565b909192938260e0600192604088516108798382516107fe565b808501516001600160a01b031660a0840152015160c082015201950193929101610852565b50606060031936011261000e5767ffffffffffffffff60043581811161000e576108cc90369060040161070a565b9060243581811161000e576108e590369060040161078b565b60443592831161000e5761092b9361091161090761091795369060040161078b565b9490933691611bff565b90613e21565b604051918291602083526020830190610840565b0390f35b503461000e57600060031936011261000e57610949615017565b3360005260016020526020604060002060018154018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b503461000e5760031960208136011261000e5760043567ffffffffffffffff811161000e576101608160040192823603011261000e576109d38261152d565b916109e06024830161152d565b906109ee6044840182611cfc565b6064850192916109fe8484611d50565b92909360848801610a0e90611dae565b95610a1891611d50565b969050610a236102f4565b6001600160a01b0390991689526001600160a01b031660208901523690610a4992610381565b60408701523690610a5992610440565b6060850152610a6b9060808501611db8565b60a482013560a084015260c482013560c084015260e482013560e08401526101048201356101008401526101248201356101208401526101408301526101440135610ab59161268a565b604051908152602090f35b9092916040820191604081528451809352606081019260208096019060005b818110610af95750505061043d9394818403910152610840565b8251151586529487019491870191600101610adf565b5060e060031936011261000e5767ffffffffffffffff60043581811161000e57610b3d90369060040161070a565b60243582811161000e57610b5590369060040161078b565b909160443584811161000e57610b6f90369060040161078b565b9060643595861161000e57610b8b610ba496369060040161078b565b929091610b9661035c565b9560c4359760843596611cc2565b9061092b60405192839283610ac0565b602060031982011261000e576004359067ffffffffffffffff821161000e57610bdf9160040161078b565b9091565b503461000e57610bf236610bb4565b610bfa615017565b60005b818110610c105760405160018152602090f35b80610c1e6001928486613f13565b610c2881806146ae565b610c318161152d565b91610c44610c3f3684610524565b614fa9565b91610c59836000526002602052604060002090565b610c6381856155a2565b50610c76610c72825460ff1690565b1590565b610c86575b505050505001610bfd565b7ffde361574a066b44b3b5fe98a87108b7565e327327954c4faeea56a4e6491a0a92610d2592610d01610d0793610cd6610ccf610cc86020968781019061158b565b3691610608565b898b615303565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b0161152d565b6040519384526001600160a01b039081169416929081906020820190565b0390a33880808080610c7b565b50604060031936011261000e5767ffffffffffffffff60043581811161000e57610d6090369060040161078b565b60249291923591821161000e5761092b92610d8d610d8561091794369060040161078b565b939092614750565b60405190610d9a82610299565b60008252613e21565b5060031960408136011261000e576004359067ffffffffffffffff821161000e57604090823603011261000e57610dfd610de16020926004016146e1565b60405190610dee82610299565b600082523391602435916141fd565b6040519015158152f35b5060031960808136011261000e576004359067ffffffffffffffff9081831161000e5760a090833603011261000e5760243590811161000e5761092b91610e55610e9692369060040161078b565b90606435610e628161034b565b6001600160a01b038116610ea85750610e90610e8433945b3690600401610678565b91604435933691611bff565b906141fd565b60405190151581529081906020820190565b610e84610e909194610e7a565b5060a060031936011261000e5767ffffffffffffffff60043581811161000e57610ee390369060040161078b565b9060243583811161000e57610efc90369060040161078b565b91909260443594851161000e57610f25610f1d610ba496369060040161078b565b929093614750565b9160405193610f3385610299565b6000855260843595339560643595612a0b565b503461000e57602060031936011261000e576020610f83600435610f698161034b565b6001600160a01b0316600052600160205260406000205490565b604051908152f35b503461000e57600060031936011261000e57610ff3610fa86127b4565b60405190610fb5826102b5565b600382527f312e3100000000000000000000000000000000000000000000000000000000006020830152604051928392606084526060840190610148565b9060208301526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660408301520390f35b5060031960208136011261000e5760043567ffffffffffffffff811161000e576102408160040192823603011261000e5761012435908160021c926001841193341585036112f85784936003821160028314916110d183600286117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe870102018815926001820185028460011b880103998a92600360a088026024013593168a6115dc565b6110e38260051b6101c40135986107ec565b156111b5575050506111036110f78261152d565b6001600160a01b031690565b6001600160a01b0390811660248401351761118b5761115f60449461115a6111759761116b9461113560a4890161152d565b9060648901946111448661152d565b9060e48b01359360c48c01359333931691611dcf565b61152d565b91610204840190611537565b93909201356119df565b61117f6001600055565b60405160018152602090f35b60046040517f6ab37ce7000000000000000000000000000000000000000000000000000000008152fd5b9194509161121e6110f7606461122396611228996111d1611514565b8a819b996111df839b6107ec565b1561122d5750610d01916111f560a4850161152d565b61120086860161152d565b9060e48601359160c4870135916001600160a01b03339216906120c8565b611ac5565b6122c4565b611175565b611236816107ec565b6003810361127d57506112789161124f60a4850161152d565b61125a86860161152d565b9060e48601359160c4870135916001600160a01b03339216906121be565b610d01565b806112896004926107ec565b036112c3576112789161129b8861152d565b6112a686860161152d565b6044860135916001600160a01b03602488013592169033906120c8565b611278916112d08861152d565b6112db86860161152d565b6044860135916001600160a01b03602488013592169033906121be565b6040517fa61be9f0000000000000000000000000000000000000000000000000000000008152346004820152602490fd5b0390fd5b503461000e5761133c36610bb4565b611344615017565b60005b81811061135a5760405160018152602090f35b611365818385614fe2565b61136e8161152d565b60209061137c82840161152d565b6001600160a01b0391828116938433141580611508575b6114de576040956113a681880182611cfc565b6060808401926113b68486611d50565b90916080948a8689016113c890611dae565b976113d3908a611d50565b9a90506113de6102f4565b6001600160a01b03909c168c526001600160a01b03909116908b0152369061140592610381565b8c890152369061141492610440565b9086015284019061142491611db8565b60a0808201359084015260c0808201359084015260e08082013590840152610100808201359084015261012080820135908401526101409182840152013561146b9161268a565b93611480856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000166101001790555193845216917f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d90602090a3600101611347565b60046040517f80ec7374000000000000000000000000000000000000000000000000000000008152fd5b50838316331415611393565b60405190611521826102b5565b60208083523683820137565b3561043d8161034b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e57602001918160061b3603831361000e57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e5760200191813603831361000e57565b9591906115e7615008565b6115fb610140880135610120890135615296565b50611604611927565b611622611615610200890189611537565b6101e08a013591506118f6565b7f00000000000000000000000000000000000000000000000000000000000000006080528160a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019384526024906102e00137610160928460a0528560c052600060e05260005b8394610204358210156116fb5790604060a0600193602090818560061b6102840161010037838560061b6102840161012037019660e0608020885201968888528960c08201526101008360061b610284019101370193929361169e565b5090929350969590966001610204350160051b610160206060525b83610264358210156117495790604060a060019301958787528860c08201526101008360061b6102840191013701611716565b505093509490506103229391507f00000000000000000000000000000000000000000000000000000000000000006080528260a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022092836102643560051b0152606060c46102406102643560051b01376118ee610cc8608435936117f1856001600160a01b03166000526001602052604060002090565b547f00000000000000000000000000000000000000000000000000000000000000006080526040608460a03760605161010052846101205260a0610144610140376101e0526101809485608020956102643560051b0190868252336101a06102643560051b015260806101c06102643560051b01526101206101e06102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a4359260a061026435026101e00190a360006060526118e56060820161115a6118bf8261152d565b966118cc6080860161152d565b906001600160a01b03809916906101608701358b61569d565b9581019061158b565b9216906147dc565b106118fd57565b60046040517f466aa616000000000000000000000000000000000000000000000000000000008152fd5b601861012435106102643560061b61026001610244351461024061022435146020600435141616161561195657565b60046040517f39f3e3fd000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90156119b95790565b61043d611980565b91908110156119d2575b60061b0190565b6119da611980565b6119cb565b919234936000915b808310611a4257505050828211611a185781611a0291611e97565b808211611a0d575050565b610322910333611e97565b60046040517f1a783b8d000000000000000000000000000000000000000000000000000000008152fd5b909194611a508683856119c1565b90813590808211611a1857611a748260206001950135611a6f8161034b565b611e97565b03950191906119e7565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818110611ab9570390565b611ac1611a7e565b0390565b90939291908115611b85579333611ade60a0830161152d565b60e08301359260c08101355b61118b578460051b6101e40335946102008201611b078184611537565b93905060005b848110611b24575050505050956103229596611f2c565b8989858e611b3c85611b368989611537565b906119c1565b803592611b6a575b91611b649391611b5d6110f7602060019998960161152d565b908c611f2c565b01611b0d565b92909493919b8c611b7a91611aae565b9b9193949092611b44565b933394611b918261152d565b6040830135926020810135611aea565b81601f8201121561000e57803591611bb883610324565b92611bc660405194856102d1565b808452602092838086019260051b82010192831161000e578301905b828210611bf0575050505090565b81358152908301908301611be2565b909291611c0b84610324565b91604094611c1b865194856102d1565b839581855260208095019160051b83019380851161000e5783925b858410611c465750505050505050565b67ffffffffffffffff90843582811161000e5786019060a08285031261000e578451611c7181610254565b8235815289830135600281101561000e578a82015285830135868201526060808401359082015260808084013594851161000e57611cb3868c96879601611ba1565b90820152815201930192611c36565b90611cf090610bdf9a99989796959493986001600160a01b03811615600014611cf6575033985b3691611bff565b90612a0b565b98611ce9565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e576020019160a082023603831361000e57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e576020019160c082023603831361000e57565b600411156107f657565b3561043d8161050f565b6004821015611dc45752565b611dcc6107bc565b52565b949290959391841515600014611e3b5761032296604051967f4ce34aa2000000000000000000000000000000000000000000000000000000008852602060048901526001602489015260448801526064870152608486015260a485015260c484015260e4830152612451565b9291946002919450611e4c816107ec565b03611e8b57600103611e61576103229361504d565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b9291906103229461515b565b90611ea181611efb565b600080808084865af115611eb3575050565b60449250611ebf612895565b6001600160a01b03604051927f470c7c1d0000000000000000000000000000000000000000000000000000000084521660048301526024820152fd5b15611f0257565b60046040517f91b3e514000000000000000000000000000000000000000000000000000000008152fd5b929193949094611f3b83611efb565b611f4581836122b1565b806120ba575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611fb4575b505050505050604052606052565b80863b151516611fa657908795969115611ff457602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b1561202e57506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d61206d575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c9080600302918082116120a1575b505060205a9101106120985785612034565b833d81803e3d90fd5b8080600392028380020360091c92030201018680612086565b9061032295929493916125c0565b959092949391936120d981836122b1565b806120f0575050600103611e61576103229361504d565b9060649593916000979593975060208251146000146121ab5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261214b565b9590919293946121cd86611efb565b6121d781836122b1565b806121e75750506103229461515b565b906064959694939291602082511460001461229e5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261223e565b906020820151036122bf5750565b610322905b60408082510361244d57602082015160c06064840151026044019180519260206001600160a01b036000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528684527f000000000000000000000000000000000000000000000000000000000000000086526055600b201696855281805284880182885af190519015612402577fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa2000000000000000000000000000000000000000000000000000000009116036123c05750505060209052565b517f1cf99b2600000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03919091166024820152604490fd5b611329848361240f612895565b517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201529081906024820190565b5050565b6040519160206001600160a01b036101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528685527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169660405282805282875af190519015612574577fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa200000000000000000000000000000000000000000000000000000000911603612530575050565b6040517f1cf99b2600000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03919091166024820152604490fd5b61132983612580612895565b6040517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201529081906024820190565b9060649492939160208251146000146126775760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c08582016001815101809152612615565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106127915750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a831061276d575050505050601f198660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b838082601f19600194510180519089815260e0812087525201920192019190612715565b8082601f19600194510180519088815260c08120875252019201920191906126cd565b467f0000000000000000000000000000000000000000000000000000000000000000036127ff577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f000000000000000000000000000000000000000000000000000000000000000082527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a0815261288f8161027d565b51902090565b3d61289c57565b601f3d0160051c60405160051c9080600302918082116128cf575b505060205a9101106128c557565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806128b7565b919082604091031261000e576040516040810181811067ffffffffffffffff821117612922575b6040526020808294803584520135910152565b61292a610224565b61290f565b92919261293b82610324565b60409261294a845192836102d1565b819581835260208093019160061b84019381851161000e57915b84831061297357505050505050565b83869161298084866128e8565b815201920191612964565b9291909261299884610324565b916129a660405193846102d1565b829480845260208094019060051b83019282841161000e5780915b8483106129d057505050505050565b823567ffffffffffffffff811161000e57820184601f8201121561000e578691612a00868385809535910161292f565b8152019201916129c1565b96989792612a268a612a359695612a2d95949998998b612c40565b369161298b565b93369161298b565b908251825191612a4d612a48848461314b565b61366d565b9760009586915b848310612b47575050506000935b838510612abf57505050505080612ab4575b50825115612a8a5782612a8691613b15565b9190565b60046040517fd5da9a1b000000000000000000000000000000000000000000000000000000008152fd5b835103835238612a74565b909192939488612ada84612ad38986612c1e565b518a613745565b8051608001516001600160a01b03166001600160a01b03612b086110f760208501516001600160a01b031690565b911603612b225750506001809101955b0193929190612a62565b8791612b4191612b3a85896001979c01038093612c1e565b528b612c1e565b50612b18565b9091968a612b6583612b5e8b879b98999a9b612c1e565b518c6136c9565b8051608001516001600160a01b03166001600160a01b03612b936110f760208501516001600160a01b031690565b911603612bb05750506001809101975b0191909594939295612a54565b8991612bcd91612bc6856001969d038093612c1e565b528d612c1e565b50612ba3565b90612bdd82610324565b612bea60405191826102d1565b828152601f19612bfa8294610324565b0190602036910137565b602090805115612c12570190565b612c1a611980565b0190565b6020918151811015612c33575b60051b010190565b612c3b611980565b612c2b565b93929091612c4c615008565b845192612c5884612bd3565b9160008352601d604560003560e01c061160011b9060005b868110612d30575050600314612d0657612c8a9086613266565b60005b838110612c9c57505050509050565b80612ca960019284612c1e565b5115612d0157612cfb612cbc8289612c1e565b5151612cc88386612c1e565b519086612cdc82516001600160a01b031690565b60208301516001600160a01b03169060606040850151940151946145e5565b01612c8d565b612cfb565b60046040517f12d3f5a3000000000000000000000000000000000000000000000000000000008152fd5b612d3a818a612c1e565b51918015612ebf57612d4d868685614cb3565b9290916001850189528215612eab57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91612d89868b612c1e565b52019380519260a084015193604060c08201519101518051908560005b838110612e405750505050606080935101519485519560005b878110612dd85750505050505050506001905b01612c70565b808760a0612de860019486612c1e565b5188612e2489898d6080860197612e01895187836131fa565b918701958651908a518214600014612e30575050508085525b80885284516131a0565b90520151905201612dbf565b612e39926131fa565b8552612e1a565b612e4a8184612c1e565b519b8c5115179b86868b60808401938451612e669085896131fa565b60608192019586519881518a1460001499612e919760019b612e9b575050508187525b52845161315f565b9052018690612da6565b612ea4926131fa565b8752612e89565b509360019392506000915060200152612dd2565b91906000602060019301528181018652612dd2565b612edc615008565b805192612ee884612bd3565b92600091828552601d6045843560e01c061160011b90835b878110612f90575050600314612d0657612f1a9083613266565b838110612f275750505050565b80612f3460019285612c1e565b5115612f8b57612f85612f478285612c1e565b5151612f538387612c1e565b5190612f6681516001600160a01b031690565b60208201516001600160a01b0316906060604084015193015193614513565b01612f1a565b612f85565b612f9a8187612c1e565b51918581156130fb5750612faf888685614ee0565b929091600185018b528883156130e95750907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91612fed868d612c1e565b52019380519260a084015191604060c0860151950151805190858c5b83811061308f5750505050606090510151938451948a5b86811061303857505050505050506001905b01612f00565b8061304560019284612c1e565b5160a0608082019189613083888b61305f87518d866131fa565b60608601948d8651908a518214600014612e305750505080855280885284516131a0565b90520151905201613020565b6130998184612c1e565b519b8c5115179b868a89608084019384516130b59085896131fa565b60608192019586519881518a14600014996130df9760019b612e9b5750505081875252845161315f565b9052018690613009565b92505093600193925060200152613032565b6020600193929401528181018852613032565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482118115151661313f570290565b613147611a7e565b0290565b81198111613157570190565b612c1a611a7e565b909283820361316e5750505090565b82939161318a613196946131909303954203918287039061310e565b9261310e565b9061314b565b9081049015150290565b90928382036131af5750505090565b926131906131cd9261318a856001969703964203918288039061310e565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b9190918281146132435782818309613219576132159161310e565b0490565b7fc63cf0890000000000000000000000000000000000000000000000000000000060005260046000fd5b50905090565b600211156107f657565b5161043d816107ec565b611dcc826107ec565b815181519260005b8281106133a45750505060005b82811061328757505050565b6132918183612c1e565b516132c56132b160208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b1561339b5751606081018051519060005b828110613354575050506040809101908151519160005b83811061330257505050506001905b0161327b565b61331f613319613313838551612c1e565b51613253565b60031090565b61332b576001016132ed565b600483517fa6cfc673000000000000000000000000000000000000000000000000000000008152fd5b613365613319613313838551612c1e565b613371576001016132d6565b60046040517fff75a340000000000000000000000000000000000000000000000000000000008152fd5b506001906132fc565b6133ae8183612c1e565b5180519086821015613565576020916133e56132b1846133ce848b612c1e565b5101516effffffffffffffffffffffffffffff1690565b1561355a576133f49087612c1e565b515191604092838301519183015161340b81613249565b61341481613249565b6134e55783015180518210156134bc579061342e91612c1e565b5191600383519361343e856107ec565b84906134558482019160048351981485039061325d565b606085015190525b11156134935750906001929181613478575b50505b0161326e565b61348c91608060608301519201519161358f565b388061346f565b600490517f94eb6af6000000000000000000000000000000000000000000000000000000008152fd5b600484517fbfb3f8ce000000000000000000000000000000000000000000000000000000008152fd5b929060608094015180518210156135315760039161350291612c1e565b5193845194613510866107ec565b85916135278583019260048451991486039061325d565b850151905261345d565b600483517f6088d7de000000000000000000000000000000000000000000000000000000008152fd5b505050600190613472565b60046040517f869586c4000000000000000000000000000000000000000000000000000000008152fd5b91909160009081526020808220928181019282825192600593841b0101915b8285106135eb575050505050036135c157565b60046040517f09bde339000000000000000000000000000000000000000000000000000000008152fd5b8451808711821b968752958418959095526040812094938301936135ae565b604051906060820182811067ffffffffffffffff821117613660575b8060405260408361363683610254565b6000928381528360808301528360a08301528360c08301528360e083015281528260208201520152565b613668610224565b613626565b9061367782610324565b61368460405191826102d1565b828152601f196136948294610324565b019060005b8281106136a557505050565b6020906136b061360a565b82828501015201613699565b906002821015611dc45752565b9092916136d461360a565b93805115613714576136f6926001600160a01b038693166080845101526137e9565b81516060810151156137055750565b60806000918260208601520152565b60246040517f375c24c100000000000000000000000000000000000000000000000000000000815260006004820152fd5b92919061375061360a565b9381511561378d576137639185916139aa565b60208301903382526040840152825190606082015115613781575050565b60009182608092520152565b60246040517f375c24c100000000000000000000000000000000000000000000000000000000815260016004820152fd5b507f7fda72790000000000000000000000000000000000000000000000000000000060005260046000fd5b92919260208201906020825151825181101561399d575b60051b82010151928351926020604085015181835101518151811015613990575b60051b01015160009460208697015161397a575b9061012060609260408b5193805185526020810151602086015201516040840152805160208c0152015160408a01522091805160051b01905b8181106138c1575050505060608293945101526138885750565b60011461389757610322611a7e565b7f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b60209095949501906020825151855181101561396d575b60051b85010151602081015115613964575160606020604083015181865101518151811015613957575b60051b01015196818801519081158a8381011060011b17179801966000828201522084149060408a0151610120820151149060208b015190511416161561394a575b9061386e565b6139526137be565b613944565b61395f6137be565b613902565b50949394613944565b6139756137be565b6138d8565b6060820180516000909152801597509550613835565b6139986137be565b613821565b6139a56137be565b613800565b9291602080830194855151918151831015613b08575b80600593841b8301015194606093828588510151818b5101518151811015613afb575b831b010151926000968188990151613ae6575b51948451865281850151828701526040850151604087015260a0809501519a608087019b8c52878720948051851b01905b818110613a4257505050505050508394955001526138885750565b83909a999a01908c848351518551811015613ad9575b871b850101518581015115613acf578a869151015181855101518151811015613ac2575b881b0101518a81019b8d8d518091019e8f9115911060011b17179c9b60009052888b822089149251910151141615613ab5575b90613a27565b613abd6137be565b613aaf565b613aca6137be565b613a7c565b5050999899613aaf565b613ae16137be565b613a58565b848701805160009091528015995097506139f6565b613b036137be565b6139e3565b613b106137be565b6139c0565b908151613b2181612bd3565b9260005b828110613be5575050503490613b39611514565b9080519060005b828110613b7457505050613b53906122c4565b80613b64575b5061043d6001600055565b613b6e9033611e97565b38613b59565b613b7e8183612c1e565b518051908151613b8d816107ec565b613b96816107ec565b15613bca575b8560019392826040613bbb6020613bc49601516001600160a01b031690565b91015191613cae565b01613b40565b9560608293920181815111611a185751900395909190613b9c565b613bef8183612c1e565b51613c0f6132b160208301516effffffffffffffffffffffffffffff1690565b15613ca557613c27613c218388612c1e565b60019052565b606080915101519081519160005b838110613c4a57505050506001905b01613b25565b82613c558284612c1e565b51015180613c665750600101613c35565b6040517fa5f542080000000000000000000000000000000000000000000000000000000081526004810187905260248101929092526044820152606490fd5b50600190613c44565b9290918351613cbc816107ec565b613cc5816107ec565b613d1a57505050613ce36110f760208301516001600160a01b031690565b6001600160a01b03604083015191161761118b57806060613d1160806103229401516001600160a01b031690565b91015190611e97565b90919260018151613d2a816107ec565b613d33816107ec565b03613d8357604081015161118b5761032293613d5960208301516001600160a01b031690565b906001600160a01b036060613d7860808601516001600160a01b031690565b940151931691611f2c565b9260028451613d91816107ec565b613d9a816107ec565b03613de05783613db760206103229601516001600160a01b031690565b60808201516001600160a01b0316926001600160a01b03606060408501519401519416916120c8565b83613df860206103229601516001600160a01b031690565b60808201516001600160a01b0316926001600160a01b03606060408501519401519416916121be565b90613e33909493929482519083612ed4565b613e3c8261366d565b9160009485915b808310613e705750505090613e619184829495613e65575b50613b15565b5090565b825103825238613e5b565b909195613e7e878385613f13565b613ea4613e8b8280611537565b90613e9b60209485810190611537565b92909189613f6c565b906001600160a01b03613ed96110f7613ec960808651016001600160a01b0390511690565b938501516001600160a01b031690565b911603613ef057506001809101965b019190613e43565b96613f0d8298600193830390613f06828a612c1e565b5287612c1e565b50613ee8565b9190811015613f54575b60051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18136030182121561000e570190565b613f5c611980565b613f1d565b61043d9036906128e8565b92909391613f7861360a565b948115801561415e575b61413457613f8e61360a565b613fa381613f9d36888861292f565b886139aa565b5191613fba87613fb436848661292f565b886137e9565b613fc48751613253565b835190613fd0826107ec565b613fd9826107ec565b613fe2816107ec565b148015906140fc575b80156140e9575b6140bf5761043d9561406f95608095896060948588019687518784510151106000146140825750505061403161402c8593614057936119b0565b613f61565b60208361404a8d828a5191510151900396845190612c1e565b5151015191015190612c1e565b5101528651015190525b01516001600160a01b031690565b6080835101906001600160a01b03169052565b86979694506140b1935061404a856140a161402c6020956040956119b0565b9451015188518551910397612c1e565b510152519086510152614061565b60046040517f09cfb455000000000000000000000000000000000000000000000000000000008152fd5b5060408751015160408401511415613ff2565b508651602001516001600160a01b03166001600160a01b0361412b6110f760208701516001600160a01b031690565b91161415613feb565b60046040517f98e9db6e000000000000000000000000000000000000000000000000000000008152fd5b508315613f82565b6040519061417382610254565b604051608083610160830167ffffffffffffffff8111848210176141f0575b6040526000808452806020850152606093846040820152848082015281848201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528252806020830152604082015282808201520152565b6141f8610224565b614192565b909291614208615017565b600260005561421784836148c0565b9490919260405195614228876102b5565b6001875260005b6020808210156142515790602091614245614166565b90828b0101520161422f565b505061428583959761428061429e9a61428e97998351156142ba575b60208401528251156142ad575b82613266565b612c04565b515195866142c7565b81516001600160a01b0316612cdc565b6142a86001600055565b600190565b6142b5611980565b61427a565b6142c2611980565b61426d565b939192909360a093848201519360c0830151966142e2611514565b96604092838601908151519160005b8381106143d7575050505034986060809601978851519860005b8a8110614338575050505050505050505050614326906122c4565b8061432e5750565b6103229033611e97565b614343818351612c1e565b51898101805161435d87878d8c60808801958651906144a1565b8092528783015190528151614371816107ec565b61437a816107ec565b15614397575b50906143918d8c6001943390613cae565b0161430b565b90919e9d8082116143ae579d9e9d039c908a614380565b600489517f1a783b8d000000000000000000000000000000000000000000000000000000008152fd5b6143e2818351612c1e565b5180516143ee816107ec565b6143f7816107ec565b15614441579061443b8d8f93868f8d6144236001988e936060870193845195608089019687519061446a565b9052528c610120613bbb82516001600160a01b031690565b016142f1565b600488517f12d3f5a3000000000000000000000000000000000000000000000000000000008152fd5b90939084810361448057505061043d93506131fa565b938361449561043d979661449b9496866131fa565b936131fa565b9061315f565b9093908481036144b757505061043d93506131fa565b938361449561043d97966144cc9496866131fa565b906131a0565b90815180825260208080930193019160005b8281106144f2575050505090565b909192938260a08261450760019489516107fe565b019501939291016144e4565b91939290936040805193608091828601918652602090600082880152838188015285518093528160a088019601936000915b84831061459a5750505050505091614595827f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31948380950360608501526001600160a01b038091169716956144d2565b0390a3565b90919293949684836001928a5180516145b2816107ec565b8252808401516001600160a01b031684830152858101518683015260609081015190820152019801959493019190614545565b92909493916040918251946080918287019187526001600160a01b0394856020921682890152838189015286518093528160a089019701936000915b84831061466a57505050505050828285949361459593867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f319896036060870152169716956144d2565b90919293949784836001928b518051614682816107ec565b8252808401518c1684830152858101518683015260609081015190820152019901959493019190614621565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea18136030182121561000e570190565b6146e9614166565b506147336147056146fa83806146ae565b92602081019061158b565b61471c6040519461471586610254565b3690610524565b845260016020850152600160408501523691610608565b606082015260405161474481610299565b60008152608082015290565b61475982610324565b9161476760405193846102d1565b808352601f1961477682610324565b0160005b8181106147c557505060005b8181106147935750505090565b806147a96147a46001938587613f13565b6146e1565b6147b38287612c1e565b526147be8186612c1e565b5001614786565b6020906147d0614166565b8282880101520161477a565b929190836000526002602052604060002091825460ff8160081c1661487b576effffffffffffffffffffffffffffff8160101c1661484a579460ff7101000000000000000000000000000001000195961615614839575b50505055565b61484292615303565b388080614833565b602486604051907fee9e0e630000000000000000000000000000000000000000000000000000000082526004820152fd5b602486604051907f1a5155740000000000000000000000000000000000000000000000000000000082526004820152fd5b90805b6148b7575090565b809106806148af565b90918151926148db610c7260a086015160c087015190615296565b614ca7576148fe6132b160208501516effffffffffffffffffffffffffffff1690565b9361491e6132b160408601516effffffffffffffffffffffffffffff1690565b948581118015614c9f575b614c755785811080614c5d575b614c335761498261494683614fa9565b9360e0840151608085015161495a81611da4565b85516001600160a01b0316918761497b60208901516001600160a01b031690565b948b615cc1565b614996836000526002602052604060002090565b916149a4610c7284866155a2565b614c23578254958460ff881615614bfc575b5050506effffffffffffffffffffffffffffff90818660101c169560881c96871515600014614b7f5760018103614b4757505085945b856149f7888361314b565b11614b3d575b86614a079161314b565b8082871183831117614ad6575b5090614a8f818493614a4e614ad19660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b84547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff16911660101b70ffffffffffffffffffffffffffffff000016178355565b815470ffffffffffffffffffffffffffffffffff1690861660881b7fffffffffffffffffffffffffffffff000000000000000000000000000000000016179055565b929190565b9690614ae987614aef92989594986148ac565b826148ac565b80150180809204970492049480861181841117614b0e57909138614a14565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80860396506149fd565b959096868103614b58575b506149ec565b614b7281614b6c89614b78959b9a9b61310e565b9861310e565b9761310e565b9438614b52565b9550955090614ad191614bb78260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b81547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff1687821660101b70ffffffffffffffffffffffffffffff000016178255614a8f565b6060614c12614c1b94516001600160a01b031690565b92015191615303565b3880846149b6565b5050509150915090600090600090565b60046040517fa11b63ff000000000000000000000000000000000000000000000000000000008152fd5b5060016080830151614c6e81611da4565b1615614936565b60046040517f5a052b32000000000000000000000000000000000000000000000000000000008152fd5b508015614929565b50600092508291508190565b919290928251614ccf610c7260a083015160c0840151906152df565b614ed057614cf26132b160208601516effffffffffffffffffffffffffffff1690565b614d116132b160408701516effffffffffffffffffffffffffffff1690565b958682118015614ec8575b614c755786821080614eb0575b614c3357614d7d90614d3a84614fa9565b9460e0850151608086015190614d4f82611da4565b87614d6188516001600160a01b031690565b93614d7660208a01516001600160a01b031690565b958c615da2565b614d91836000526002602052604060002090565b91614d9f610c728486615645565b614c23578254958460ff881615614e92575b5050506effffffffffffffffffffffffffffff90818660101c169560881c96871515600014614b7f5760018103614e6657505085945b85614df2888361314b565b11614e5c575b86614e029161314b565b8082871183821117614e48575090614a8f818493614a4e614ad19660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b969050614aef614ae98789989594986148ac565b8086039650614df8565b959096868103614e77575b50614de7565b614b7281614b6c89614e8b959b9a9b61310e565b9438614e71565b6060614c12614ea894516001600160a01b031690565b388084614db1565b5060016080840151614ec181611da4565b1615614d29565b508115614d1c565b5050915050600090600090600090565b919290928251614efc610c7260a083015160c084015190615296565b614ed057614f1f6132b160208601516effffffffffffffffffffffffffffff1690565b614f3e6132b160408701516effffffffffffffffffffffffffffff1690565b958682118015614fa1575b614c755786821080614f89575b614c3357614f6790614d3a84614fa9565b614f7b836000526002602052604060002090565b91614d9f610c7284866155a2565b5060016080840151614f9a81611da4565b1615614f56565b508115614f49565b61043d90614fc2606082015151610140830151906118f6565b80516001600160a01b03166000908152600160205260409020549061268a565b909161043d92811015614ffb575b60051b8101906146ae565b615003611980565b614ff0565b615010615017565b6002600055565b60016000540361502357565b60046040517f7fa8a987000000000000000000000000000000000000000000000000000000008152fd5b9092813b1561512d57604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156150a65750505050604052606052565b8593943d6150e9575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211615114575b505060205a91011061209857856150af565b8080600392028380020360091c92030201018680615102565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b1561526857604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af1156151d857505050505060805260a05260c052604052606052565b89949550883d61521b575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c90806003029180821161524f575b505060205a91011061524657866151e3565b843d81803e3d90fd5b8080600392028380020360091c92030201018780615234565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b42109081156152d4575b506152aa57600190565b60046040517f6f7eac26000000000000000000000000000000000000000000000000000000008152fd5b9050421015386152a0565b42109081156152f8575b506152f357600190565b600090565b9050421015386152e9565b9091336001600160a01b0383161461559d5761531d6127b4565b926000937f190100000000000000000000000000000000000000000000000000000000000085526002526022526042832090836022528380528392815191601f198101805184604103918860018411938415615532575b508514851515169788156153c3575b5050505050505050156153935750565b60049061539e612895565b7f4f7fb80d000000000000000000000000000000000000000000000000000000008152fd5b909192939495969750604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8501937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0855196019660208b60648a519b7f1626ba7e000000000000000000000000000000000000000000000000000000009d8e8b528c520188845afa998a615469575b505050505252523880808080808080615383565b8b51036154765780615455565b908a913b61550a576154e257640101000000821a156154b757807f815e1d640000000000000000000000000000000000000000000000000000000060049252fd5b6024917f1f003d0a000000000000000000000000000000000000000000000000000000008252600452fd5b807f8baa579f0000000000000000000000000000000000000000000000000000000060049252fd5b6004827f4f7fb80d000000000000000000000000000000000000000000000000000000008152fd5b9850506040840180519060608601518b1a99615569575b89865288835260208b60808560015afa5083835287865252885138615374565b9850601b8160ff1c01987f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152615549565b505050565b905460ff8160081c16615614576effffffffffffffffffffffffffffff8160101c1690816155d3575b505050600190565b60881c11156155e35780806155cb565b602490604051907f10fda3e10000000000000000000000000000000000000000000000000000000082526004820152fd5b602482604051907f1a5155740000000000000000000000000000000000000000000000000000000082526004820152fd5b906000905460ff8160081c16615694576effffffffffffffffffffffffffffff8160101c16908161567a575b50505050600190565b60881c111561568a578080615671565b6155e35750600090565b50905050600090565b90929160019060048110156156fd575b11806156ea575b806156d7575b6156c5575b50505050565b6156ce9361570a565b388080806156bf565b506001600160a01b0382163314156156ba565b506001600160a01b0384163314156156b4565b6157056107bc565b6156ad565b6000919290829161032295604051906001600160a01b0360208301937f0e1d31dc00000000000000000000000000000000000000000000000000000000855288602485015233604485015216606483015260848201526084815261576d8161027d565b51915afa615e78565b90815180825260208080930193019160005b828110615796575050505090565b909192938260a0600192875180516157ad816107ec565b8252808401516001600160a01b03168483015260408082015190830152606080820151908301526080908101519082015201950193929101615788565b90815180825260208080930193019160005b82811061580a575050505090565b909192938260c060019287518051615821816107ec565b8252808401516001600160a01b039081168584015260408083015190840152606080830151908401526080808301519084015260a0918201511690820152019501939291016157fc565b906004821015611dc45752565b6060519081815260208091019160809160005b828110615899575050505090565b83518552938101939281019260010161588b565b90815180825260208080930193019160005b8281106158cd575050505090565b8351855293810193928101926001016158bf565b90815180825260208092019182818360051b85019501936000915b84831061590c5750505050505090565b909192939495848061595e83856001950387528a518051825261593584820151858401906136bc565b60408082015190830152606080820151908301526080809101519160a0809282015201906158ad565b98019301930191949392906158fc565b92615b02906001600160a01b0361043d9694615b0f94875216602086015260a06040860152805160a080870152610140906159b482880182516001600160a01b03169052565b6080615af1615a286159f38a6159dc6020870151610160809301906001600160a01b03169052565b6040860151906101808d01526102a08c0190615776565b60608501517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec08c8303016101a08d01526157ea565b615a3a838501516101c08c019061586b565b60a08401516101e08b015260c08401516102008b015260e08401516102208b015261010094858501516102408c015261012094858101516102608d015201516102808b0152615aa1602087015160c08c01906effffffffffffffffffffffffffffff169052565b60408601516effffffffffffffffffffffffffffff1660e08b015260608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6095868c840301908c0152610148565b930151918784030190870152610148565b8381036060850152615878565b9160808184039101526158e1565b939061043d95936001600160a01b03615b0f94615cb393885216602087015260a06040870152805160a08088015261014090615b6482890182516001600160a01b03169052565b6080615ca2615bd8615ba38b6020860151615b8d61016091828401906001600160a01b03169052565b61018060408801519201526102a08d0190615776565b60608501518c82037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0016101a08e01526157ea565b615bea838501516101c08d019061586b565b60a08401516101e08c015260c08401516102008c015260e08401516102208c015261010094858501516102408d0152610120948c6102608783015191015201516102808c0152615c52602087015160c08d01906effffffffffffffffffffffffffffff169052565b60408601516effffffffffffffffffffffffffffff1660e08c015260608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6095868d840301908d0152610148565b930151918884030190880152610148565b9084820360608601526158ad565b909591929493600190615cd381611da4565b1180615d8f575b80615d7c575b615ced575b505050505050565b6080810151511580615d73575b15615d155750615d0a945061570a565b388080808080615ce5565b6000935083929450615d6061576d615d6e9760405192839160208301957f33131570000000000000000000000000000000000000000000000000000000008752338b6024860161596e565b03601f1981018352826102d1565b615d0a565b50855115615cfa565b506001600160a01b038416331415615ce0565b506001600160a01b038216331415615cda565b919692939594600190615db481611da4565b1180615e65575b80615e52575b615dcf575b50505050505050565b6080820151511580615e49575b15615df9575050615ded945061570a565b38808080808080615dc6565b600094508493955061576d615e4497615d6060405193849260208401967f33131570000000000000000000000000000000000000000000000000000000008852338c60248701615b1d565b615ded565b50805115615ddc565b506001600160a01b038516331415615dc1565b506001600160a01b038316331415615dbb565b15615f0f577f0e1d31dc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000600060203d14615f04575b1603615ed35750565b602490604051907ffb5014fc0000000000000000000000000000000000000000000000000000000082526004820152fd5b602081803e51615eca565b602490615f1a612895565b604051907ffb5014fc0000000000000000000000000000000000000000000000000000000082526004820152fdfea26469706673582212200d53e9d4f26a00cc6af37b012c26f8d770777dfea74c99c52ea7d855f909a12a64736f6c634300080e003300000000000000000000000000000000f9490004c11cef243f5400493c00ad63000000000000000000000000000000000000000000 +``` + +3. Deploy the `Seaport 1.2` contract by submitting: + +``` +cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e03087000000000000000000000000000000000000000007cfc129aa3aa9006db365ff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000068786101e060405234620000c3576200001f620000196200012e565b6200016b565b604051615c0190816200076e823960805181612622015260a05181612646015260c051816125ff015260e0518181816113b80152612430015261010051818181611257015261247f01526101205181818161145e01526124eb015261014051816125ac015261016051816125d3015261018051818181610f1f01528181612144015261226501526101a0518161559c01526101c05181818161218201526122a30152f35b600080fd5b50634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b03821117620000fb57604052565b62000105620000c8565b604052565b601f909101601f19168101906001600160401b03821190821017620000fb57604052565b62006858602081380391826040519384926200014b82856200010a565b833981010312620000c357516001600160a01b0381168103620000c35790565b60406004916200017a62000638565b610120526101005260e05260c05260a0526080528151806104e980820190828210600180881b038311176200029d575b6200636f833903906000f080156200028d575b6101a0524661014052620001fd60c0519060805160a0516040519360005281602052604052466060523060805260a0600020926040526000606052608052565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa9081156200027d575b60009162000247575b506101c052620002456001600055565b565b6200026d915060403d811162000275575b6200026481836200010a565b810190620002ba565b503862000235565b503d62000258565b62000287620002ad565b6200022c565b62000297620002ad565b620001bd565b620002a7620000c8565b620001aa565b506040513d6000823e3d90fd5b9190826040910312620000c3576020825192015190565b60405190620002e082620000df565b60038252565b6040519060a082016001600160401b038111838210176200030b575b604052606a8252565b62000315620000c8565b62000302565b6040519060c082016001600160401b03811183821017620003e5575b6040526084825263656e742960e01b60a0837f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e746966696560408201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c616464726573732072656369706960808201520152565b620003ef620000c8565b62000337565b6040519061010082016001600160401b0381118382101762000525575b60405260d482527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0837f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c4360408201527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608201527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808201527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08201527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08201520152565b6200052f620000c8565b62000412565b60405190608082016001600160401b03811183821017620005c1575b6040526052825271766572696679696e67436f6e74726163742960701b6060837f454950373132446f6d61696e28737472696e67206e616d652c737472696e672060208201527f76657273696f6e2c75696e7432353620636861696e49642c616464726573732060408201520152565b620005cb620000c8565b62000551565b9081519160005b838110620005ea575050016000815290565b8060208092840101518185015201620005d8565b620006296200062294936200062262000245946040519788956020870190620005d1565b90620005d1565b03601f1981018452836200010a565b6040516200064681620000df565b600781526614d9585c1bdc9d60ca1b6020918201527f32b5c112df393a49218d7552f96b2eeb829dfb4272f4f24eef510a586b85feef9162000687620002d1565b828101906218971960e91b825251902091620006a2620002e6565b818101927f4f666665724974656d2875696e7438206974656d547970652c6164647265737384527f20746f6b656e2c75696e74323536206964656e7469666965724f72437269746560408301527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606083015269656e64416d6f756e742960b01b6080830152620007326200031b565b926200076562000741620003f5565b936200074c62000535565b83815191012096815190209580518482012095620005fe565b80519101209056fe60806040526004361015610023575b361561001957600080fd5b610021614fef565b005b60003560e01c80156100eb57806306fdde031461016957806346423aa7146101605780635b34b9661461015757806379df72bd1461014e57806387201b4114610145578063881477321461013c578063a817440414610133578063a900866b1461012a578063b3a34c4c14610121578063e7acab2414610118578063ed98a5741461010f578063f07ec37314610106578063f2d12b12146100fd578063f47b7740146100f4578063fb0f3ee1146100eb5763fd9f1e100361000e576100e6610f50565b61000e565b506100e66101c8565b506100e6610ec8565b506100e6610df2565b506100e6610d8a565b506100e6610cc2565b506100e6610c05565b506100e6610b81565b506100e6610b17565b506100e6610a60565b506100e66108d6565b506100e66107c6565b506100e661059d565b506100e66104f5565b506100e6610474565b506100e661042e565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc906020828201126101c3576004359167ffffffffffffffff83116101c35782610240920301126101c35760040190565b600080fd5b506101d236610172565b6101243590600382169160021c91600183119234158403610420575b60038111907f0203020301010000000000000000000000000000000000000000000000000000811a9061024c8260a0850260240135887d010102030000000000000000000000000000000000000000000000000000851a888a61121a565b928060051b6101c4013596610260816106a8565b6102b3575050604435602435176102a55761028b9461027e916115b5565b61028661166d565b61569e565b6102956001600055565b60405160018152602090f35b0390f35b636ab37ce76000526004601cfd5b610286925061028b969161032a916102c96111a8565b9384836102d682956106a8565b6002810361032f5750610325918a6102f060a082016111bf565b6102fc606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611efe565b611738565b612105565b610338816106a8565b600381036103875750610325918a61035260a082016111bf565b61035e606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611fff565b806103936004926106a8565b036103dc57610325918a6103a6816111bf565b6103b2606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611efe565b610325918a6103ea816111bf565b6103f6606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611fff565b61042934611d42565b6101ee565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357602080526707536561706f727460475260606020f35b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35761052d614fd5565b3360005260016020526020604060002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43014060801c018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b50346101c3577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020813601126101c3576004359067ffffffffffffffff82116101c3576101609082360301126101c35761061263ffffffff6020921661014461060982600401611cd6565b91013590612423565b604051908152f35b9181601f840112156101c35782359167ffffffffffffffff83116101c3576020808501948460051b0101116101c357565b73ffffffffffffffffffffffffffffffffffffffff8116036101c357565b60a435906106768261064b565b565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156106b257565b610676610678565b60809080516106c8816106a8565b83528173ffffffffffffffffffffffffffffffffffffffff918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610729575050505090565b909192938260e0600192604088516107428382516106ba565b8085015173ffffffffffffffffffffffffffffffffffffffff1660a0840152015160c08201520195019392910161071b565b9092916040820191604081528451809352606081019260208096019060005b8181106107b0575050506107ad9394818403910152610709565b90565b8251151586529487019491870191600101610793565b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff6004358181116101c35761081290369060040161061a565b50506024358181116101c35761082c90369060040161061a565b50506044358181116101c35761084690369060040161061a565b50506064359081116101c35761086090369060040161061a565b505061087961086d610669565b60c43590608435611813565b906102a160405192839283610774565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101c3576004359067ffffffffffffffff82116101c3576108d29160040161061a565b9091565b50346101c3576108e536610889565b505060046108fb63ffffffff8235168201611aba565b90610904614fd5565b81519060005b82811061091d5760405160018152602090f35b8061092a600192866129f9565b51805184608082015161093c816129a5565b610945816129a5565b14610a4857805173ffffffffffffffffffffffffffffffffffffffff1661096b826147c6565b90610980826000526002602052604060002090565b61098a81846155c2565b5061099d610999825460ff1690565b1590565b6109ae575b50505050505b0161090a565b6109f4610a1f928460207ff280791efe782edcf06ce15c8f4dff17601db3b88eb3805a0db7d77faf757f04986060890151516101408a015103610a3b575b0151916151f2565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b610a2e60405192839283614e9e565b0390a138808080806109a2565b610a43614cb0565b6109ec565b50506109a8565b9060206107ad928181520190610709565b5060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35760043567ffffffffffffffff8082116101c357610aab368360040161061a565b50506024359081116101c3576102a191610b0391610acc368260040161061a565b5050610afb610ae463ffffffff809416600401615b97565b92610aed6110db565b926000845216600401611c52565b903392613bcc565b604051918291602083526020830190610709565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35773ffffffffffffffffffffffffffffffffffffffff600435610b688161064b565b1660005260036020526020604060002054604051908152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6040813601126101c3576004359067ffffffffffffffff82116101c35760409082360301126101c357610bfb610be363ffffffff602093166004016119cd565b610beb6110db565b9060008252339160243591613f74565b6040519015158152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6080813601126101c3576004359067ffffffffffffffff908183116101c35760a09083360301126101c3576024359081116101c3576102a191610cb091610c71368260040161061a565b5050610ca060643592610c838461064b565b610c9663ffffffff80921660040161186c565b9216600401611a2d565b9133811502019160443591613f74565b60405190151581529081906020820190565b5060a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600467ffffffffffffffff81358181116101c357610d0d3682850161061a565b5050602435908282116101c357610d263683860161061a565b50506044359283116101c357610d7b61087994610d453686830161061a565b5050610d5963ffffffff8094168201615b97565b92610d7381610d666110db565b9660008852168301611b44565b951601611b44565b608435933393606435936126d4565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c3576020610612600435610dcb8161064b565b73ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff600480358281116101c357610e3d3682840161061a565b5050602435908382116101c357610e563683850161061a565b50506044359384116101c3576102a193610eb0610ebc94610e793684830161061a565b5050610e9f610ea860643595610e8e8761064b565b63ffffffff92838092168501611bf5565b97168301611a2d565b931601611c52565b91338115020192613bcc565b60405191829182610a4f565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357610f006125a7565b606060005260205273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166040526303312e3260635260a06000f35b50346101c357610f5f36610889565b90610f68614fd5565b600091825b818110610f925783610f855760405160018152602090f35b610f8d614d6b565b610295565b80610fa06001928486614d10565b94610faa866111bf565b907f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d611075611006610fde60208b016111bf565b93610feb60808c01614d5e565b60048633148833141715911417179961014061060982611cd6565b9261104a61101e856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016610100179055565b60405193845273ffffffffffffffffffffffffffffffffffffffff9081169416929081906020820190565b0390a301610f6d565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519060a0820182811067ffffffffffffffff8211176110ce57604052565b6110d661107e565b604052565b604051906020820182811067ffffffffffffffff8211176110ce57604052565b604051906040820182811067ffffffffffffffff8211176110ce57604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176110ce57604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff811161119b575b01160190565b6111a361107e565b611195565b6111b06110fb565b90602082526020828136910137565b356107ad8161064b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101c3570180359067ffffffffffffffff82116101c3576020019181360383136101c357565b959392919094611228614fa7565b61123061155f565b6101643561014435428211154282111761154b57505061020435610264351061153d5793907f00000000000000000000000000000000000000000000000000000000000000006080528060a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019283526024906102e00137610160948360a0528460c052600060e05260009260005b83610204358210156113315790604060019261010060a060208560061b9a818c610284018537858c61028401610120376102a48c0135179d019860e06080208a5201988a8a528b60c08401526102840191013701969392966112d4565b5096509192979690976001610204350160051b610160206060525b836102643588101561138957906102a460a060019301958787528860c082015260408a60061b91610100836102840191013701351796019561134c565b50925095945095925073ffffffffffffffffffffffffffffffffffffffff91501161152f576107ad91611528917f00000000000000000000000000000000000000000000000000000000000000006080528060a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022090816102643560051b0152606060c46102406102643560051b013761036060843561145a8173ffffffffffffffffffffffffffffffffffffffff166000526001602052604060002090565b54967f00000000000000000000000000000000000000000000000000000000000000006080526040608460a037606051610100526101205260a0610144610140376101e09687526101809687608020976102643560051b0191888352336101a06102643560051b015260806101c06102643560051b0152610120826102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a06102643502938460a435940190a360006060526102643560051b01016040528101906111c9565b9083614371565b6339f3e3fd6000526004601cfd5b63466aa6166000526004601cfd5b6321ccfeb76000526020526040526044601cfd5b7401000000000000000000000000000000000000000060243560c4351760a43560843517171060186101243510166102643560061b61026001610244351461024061022435146020600435141616161561152f57565b608435916101043560e43560c4358315611627579461067695604051957f4ce34aa200000000000000000000000000000000000000000000000000000000875260206004880152600160248801526044870152606486015260848501523360a485015260c484015260e483015261223e565b925092806116366002926106a8565b0361166057928360016106769503611651575b503391615004565b61165a90611d31565b38611649565b91906106769333916150e3565b3460643560006102643560061b815b8181106116bd575050508181116116b0575b61169a81608435611d62565b8082116116a5575050565b610676910333611d62565b6116b8611d22565b61168e565b806102840135948086116116e657906116e08660409303966102a4830135611d62565b0161167c565b638ffff98084526004601cfd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190820391821161173057565b6106766116f3565b919082156117d95760843592610104353360c43560e4355b6117cc575b8360051b6101e40335936102643560061b9060005b82811061177f57505050956106769596611dae565b87876102848301358c856117ab575b918493916117a5936102a46040970135908a611dae565b0161176a565b9891816117bf60409695936117a595611723565b9a9193509193945061178e565b6117d4611d53565b611755565b3392606435608435602435604435611750565b60209067ffffffffffffffff8111611806575b60051b0190565b61180e61107e565b6117ff565b906108d2929163ffffffff9161182f8360043516600401611bf5565b926118408160243516600401611a2d565b6118606118538360443516600401611b44565b9260643516600401611b44565b923381150201946126d4565b90604051610200810160405260806118c68294604060208201602086013760a084018085526118a563ffffffff918284351684016118f5565b6118b68160608401351683016118cb565b60608601528382013516016118cb565b910152565b9060206040519263ffffffff813563ffffffe0601f82011692848401908737168452830101604052565b6118c660609161016081853763ffffffff611917816040840135168301611927565b604086015283820135160161197a565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160a0809402910185378086015b83811061196c5750505050604052565b84815293820193810161195c565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160c0809402910185378086015b8381106119bf5750505050604052565b8481529382019381016119af565b906040516102008101604052611a13819360a083018084526119f963ffffffff918284351684016118f5565b6001602085015260016040850152602082013516016118cb565b606082015260806040519160208301604052600083520152565b803591600592641fffffffe081851b16604080519060209384848401018252829663ffffffff809216845260005b858110611a6e5750505050505050909150565b8083888093850101351683018551908360a091828401895287608093848484018737820135160101908d60018884351601901b8851928184018a52833782015282828801015201611a5b565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611af55750505050505050565b808388809385010135168301611b34838851928984016101a085018b52611b2581848b81860135168501016118f5565b8452878a8201351601016118cb565b8382015282828701015201611ae5565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611b7f57505050505050565b80611b9587848180958801013516860101611ba1565b82828701015201611b70565b90813591604080519363ffffffff81168552602080641fffffffe08360051b168701019381643fffffffc0869460061b16910185378086015b828110611be75750505052565b848152938301938101611bda565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611c3057505050505050565b80611c468784818095880101351686010161186c565b82828701015201611c21565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611c8d5750505050505050565b808388809385010135168301611cc6838851928984018a52611cb782898184013516830101611ba1565b8452878a820135160101611ba1565b8382015282828701015201611c7d565b9060405161016081016040528092611d16610140918281853763ffffffff611d05816040840135168301611927565b60408601526060820135160161197a565b80606084015251910152565b50638ffff9806000526004601cfd5b6369f958276000526020526024601cfd5b63a61be9f06000526020526024601cfd5b50636ab37ce76000526004601cfd5b611d6b82611d99565b600080808085855af115611d7d575050565b611d85612681565b63bc806b966000526020526040526044601cfd5b15611da057565b6391b3e5146000526004601cfd5b929193949094611dbd83611d99565b611dc781836120f2565b80611ef0575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611e36575b505050505050604052606052565b80863b151516611e2857908795969115611e5b5786635f15d67287526020526024601cfd5b959192939515611e80575063988919238594526020526040526060526080526084601cfd5b3d611ea3575b5063f486bc87845260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c908060030291808211611ed7575b505060205a910110611ece5785611e86565b833d81803e3d90fd5b8080600392028380020360091c92030201018680611ebc565b906106769592949391612359565b919395909294611f0e81836120f2565b80611f375750508460016106769603611f28575b50615004565b611f3190611d31565b38611f22565b815160649693959394929190602003611fec5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c08682016001815101809152611f8c565b95909192939461200e86611d99565b61201881836120f2565b80612028575050610676946150e3565b90606495969493929160208251146000146120df5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261207f565b906020820151036121005750565b610676905b90604082510361223a5760208201519160c06064820151026044019260405193602073ffffffffffffffffffffffffffffffffffffffff6000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528584527f00000000000000000000000000000000000000000000000000000000000000006040526055600b2016976040528180526040860182895af190805191156122215750937f4ce34aa2000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000060209596160361221557505052565b61221e91612345565b52565b63d13d53d48691612230612681565b526020526024601cfd5b9050565b9060405190602073ffffffffffffffffffffffffffffffffffffffff6101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528785527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169560405282805282865af1908051911561233657507fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa20000000000000000000000000000000000000000000000000000000091160361232d575050565b61067691612345565b63d13d53d49150612230612681565b631cf99b266000526020526040526044601cfd5b9060649492939160208251146000146124105760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c085820160018151018091526123ae565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106125665750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a83106125245750505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b8380827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519089815260e08120875252019201920191906124ae565b80827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519088815260c0812087525201920192019190612466565b6000467f0000000000000000000000000000000000000000000000000000000000000000036125f557507f000000000000000000000000000000000000000000000000000000000000000090565b60405190608051907f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020527f0000000000000000000000000000000000000000000000000000000000000000604052466060523060805260a081209260405260605260805290565b3d61268857565b601f3d0160051c60405160051c9080600302918082116126bb575b505060205a9101106126b157565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806126a3565b9196948094966126e49284612a1b565b9186519581516126fc6126f7828a612efa565b61339b565b976000998a905b8282106128015750506000925b828410612753575050505050936127379394868297612748575b5081511561273b57613748565b9190565b6127436133fe565b613748565b82510382523861272a565b90919293998961276e83612767888f6129f9565b5189613478565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff6127c0602084015173ffffffffffffffffffffffffffffffffffffffff1690565b9116036127dc5750506001809101945b01929190999399612710565b86916127fb916127f485886001979b010380936129f9565b528c6129f9565b506127d0565b90949a8a61281e8a6128178986999798996129f9565b518a61340d565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff612870602084015173ffffffffffffffffffffffffffffffffffffffff1690565b91160361288e5750506001809101955b01909a949a93929193612703565b87916128ab916128a4856001969b0380936129f9565b528d6129f9565b50612880565b6128b96110ae565b90604051610160810181811067ffffffffffffffff821117612938575b604052600080825280602083015260609182604082015282808201528160808201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528452806020850152604084015280808401526080830152565b61294061107e565b6128d6565b61294d6110fb565b600181529060203681840137565b9061296d612968836117ec565b61111b565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061299b82946117ec565b0190602036910137565b600511156106b257565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020908051156129ed570190565b6129f56129af565b0190565b6020918151811015612a0e575b60051b010190565b612a166129af565b612a06565b90919392612a27614fb6565b6000357c40000000000000000000000000000000000000000000000000000000001692612a526128b1565b50825193612a5f8561295b565b96600180960160051b9560205b878110612b2f57505050907c4000000000000000000000000000000000000000000000000000000001612aa59214612b22575b8361301b565b60205b838110612ab55750505050565b806020918701518015612b1c57612b1690828601515185612aea825173ffffffffffffffffffffffffffffffffffffffff1690565b8287015173ffffffffffffffffffffffffffffffffffffffff165b906060604085015194015194614296565b01612aa8565b50612b16565b612b2a612ff8565b612a9f565b80870151928015612cbf57612b43846146cc565b95918d82949215612cab5790857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93920152019481519260a08401519360c081015160408201518051906004608080950151612b9e816129a5565b106000528960005b838110612c42575050505060608095510151958651958960005b888110612bdb57505050505050505050506020905b01612a6c565b87612be6828c6129f9565b5160a088820191612c2089898d612bff87518983612fc5565b908b86019889519089518214600014612c32575050508088525b8751612f53565b80945201908151905252018a90612bc0565b612c3b92612fc5565b8852612c19565b87612c4d82856129f9565b519e8f600051905110179e612c8f878d8a8401938c6060612c7087518984612fc5565b92019687519087518214600014612c9b575050508086525b8551612f07565b80925252018a90612ba6565b612ca492612fc5565b8652612c88565b505094506020809392506000910152612bd5565b92906000602080930152612bd5565b90919392612cda614fb6565b6000357c40000000000000000000000000000000000000000000000000000000001692612d056128b1565b50825193612d128561295b565b96600180960160051b9560205b878110612da857505050907c4000000000000000000000000000000000000000000000000000000001612d579214612b22578361301b565b60205b838110612d675750505050565b806020918701518015612da257612d9c90828601515185612aea825173ffffffffffffffffffffffffffffffffffffffff1690565b01612d5a565b50612d9c565b80870151928015612ed857612dbc84614478565b95918d82949215612ec45790857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93920152019481519260a08401519360c081015160408201518051906004608080950151612e17816129a5565b106000528960005b838110612e8a575050505060608095510151958651958960005b888110612e5457505050505050505050506020905b01612d1f565b87612e5f828c6129f9565b5160a088820191612e7889898d612bff87518983612fc5565b80945201908151905252018a90612e39565b87612e9582856129f9565b519e8f600051905110179e612eb8878d8a8401938c6060612c7087518984612fc5565b80925252018a90612e1f565b505094506020809392506000910152612e4e565b92906000602080930152612e4e565b8181029291811591840414171561173057565b9190820180921161173057565b929092838103612f175750505090565b612f2d83612f3393039342039182850390612ee7565b93612ee7565b8201809211612f46575b81049015150290565b612f4e6116f3565b612f3d565b919092838303612f635750505090565b600192612f7c83612f8293039342039182850390612ee7565b94612ee7565b8301809311612fb8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b612fc06116f3565b612f8c565b919091828114612ff25782818309612fe457612fe091612ee7565b0490565b63c63cf0896000526004601cfd5b50905090565b506312d3f5a36000526004601cfd5b600211156106b257565b516107ad816106a8565b815181519260005b8281106131245750505060005b82811061303c57505050565b61304681836129f9565b5161307a61306660208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b1561311b5751606081018051519060005b8281106130ed575050506040018051519060005b8281106130b3575050506001905b01613030565b806130d36130cd6130c760019486516129f9565b51613011565b60031090565b6130de575b0161309f565b6130e8818661321e565b6130d8565b806131016130cd6130c760019486516129f9565b61310c575b0161308b565b613116818761320a565b613106565b506001906130ad565b61312e81836129f9565b516131438151878110156131de575b866129f9565b51602090613165613066838301516effffffffffffffffffffffffffffff1690565b156131d357519060409081830151918401519263bfb3f8ce9185015161318a81613007565b61319381613007565b6131c0575b5081518310156131b75750916131b1916001949361323b565b01613023565b6000526004601cfd5b9050606091500151636088d7de38613198565b5050506001906131b1565b6131f460208401516131ef81613007565b6131f9565b61313d565b63133c37c66000526020526024601cfd5b63a8930e9a6000526020526040526044601cfd5b63d69293326000526020526040526044601cfd5b61221e826106a8565b90613245916129f9565b51805191613252836106a8565b60038311156132b157613292826004604060609501958651801515600014613298576132889087870151906080880151916132ce565b1460030390613232565b01519052565b5060808501515115613288576132ac6132bf565b613288565b6394eb6af66000526004601cfd5b506309bde3396000526004601cfd5b916000928352602090818420918082019181815191600592831b0101905b81841061330c5750505050036132ff5750565b6309bde33990526004601cfd5b8351808611821b958652948318949094526040862093928201926132ec565b6133336110ae565b906000825260006020830152600060408301526000606083015260006080830152565b604051906060820182811067ffffffffffffffff82111761338e575b604052600060408361338261332b565b81528260208201520152565b61339661107e565b613372565b906133a8612968836117ec565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06133d682946117ec565b019060005b8281106133e757505050565b6020906133f2613356565b828285010152016133db565b5063d5da9a1b6000526004601cfd5b909291613418613356565b93805115613465576134479273ffffffffffffffffffffffffffffffffffffffff8693166080845101526134f6565b81516060810151156134565750565b60806000918260208601520152565b63375c24c160005260006020526024601cfd5b929190613483613356565b938151156134c05761349691859161363d565b602083019033825260408401528251906060820151156134b4575050565b60009182608092520152565b63375c24c160005260016020526024601cfd5b50637fda72796000526004601cfd5b50634e487b7160005260116020526024601cfd5b9092919260009081928290828351905b8160051b8501811061353557505050505060608293945101526135265750565b600114611da0576106766134e2565b602090969596019060208251518451811015613630575b60051b840101518051906020845101516020604084015192015115825182101517613625579060209160051b0101519660609081890151998a81019a15908b1060011b171798976000828201528b5187156001146135d857502085189060408b0151610120820151189060208c015190511817176135cb575b90613506565b6135d36134d3565b6135c5565b929061012092949750806040915185526020810151602086015201516040840152805160208d0152015160408b015220926020850182811861361b575b506135c5565b8251905238613615565b5050509594956135c5565b6136386134d3565b61354c565b9092919260009081928291808051600590811b82015b80841061366f5750505050505060608293945101526135265750565b60209796978094019380855151875181101561373b575b841b87010151908086510151916060928284835101519201511582518210151761372f576000918391871b010151928301998a519b8c81019c15908d1060011b17179a99528b5188156001146136ef57505060a090208614613653576136ea6134d3565b613653565b8251815281830151818301526040808401519082015260808084015191015260a090912096508301848118613725575b50613653565b845190523861371f565b50505050969596613653565b6137436134d3565b613686565b9290918351916137578361295b565b946137606111a8565b9480519060005b8281106138ee5750505060005b8481106137ab57505050505061378990612105565b478061379b575b506107ad6001600055565b6137a59033611d62565b38613790565b6137b581836129f9565b516137d561306660208301516effffffffffffffffffffffffffffff1690565b156138d8576137ed6137e7838a6129f9565b60019052565b8051604081015180519060005b828110613873575050506060809101519081519160005b83811061383b5750505050906138356001928661382e84826129f9565b519161577d565b01613774565b80613848600192846129f9565b5160a085820191825180613862575b500151905201613811565b61386d90858c61397a565b38613857565b808b613881600193856129f9565b518a6080820151926060830192835161389f575b50505052016137fa565b6138d0926138ac91613968565b895173ffffffffffffffffffffffffffffffffffffffff166101208b015191613991565b8a8e38613895565b508060006138e86001938a6129f9565b52613835565b80613949896138ff600194866129f9565b51805190815161390e816106a8565b613917816106a8565b1561394f575b6040613940602083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015191613991565b01613767565b476060830151111561391d57613963611d22565b61391d565b919061397261332b565b506080830152565b63a5f542086000526020526040526060526064601cfd5b929190835161399f816106a8565b6139a8816106a8565b613a4b57505050806139f06139d7602061067694015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6040830151911617613a3e575b6060613a35608083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015190611d62565b613a46611d53565b613a12565b60018451613a58816106a8565b613a61816106a8565b03613ae15792610676936040820151613ad4575b602082015173ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff6060613ac9608086015173ffffffffffffffffffffffffffffffffffffffff1690565b940151931691611dae565b613adc611d53565b613a75565b60028451613aee816106a8565b613af7816106a8565b03613b645783613b21602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611efe565b83613b89602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611fff565b9193929081613bde9184519085612cce565b805160051b604001927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051907f4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e760209687835282a152855195613c438761339b565b9460009788915b818310613c78575050505092613c699386829697613c6d575b50613748565b5090565b825103825238613c63565b90919298613c9884613c8a818d6129f9565b518481519101519088613d26565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff613ce98584015173ffffffffffffffffffffffffffffffffffffffff1690565b911603613d0357506001809101935b019190989298613c4a565b93613d208295600193830390613d19828d6129f9565b528a6129f9565b50613cf8565b909192613d31613356565b938351158015613f13575b613f06575b613d49613356565b90613d5582828661363d565b81519460609384870193845115613eee575092859288836107ad9996613d828360809a97613e859c6134f6565b613d8c8351613011565b613d95816106a8565b885190613da1826106a8565b613daa826106a8565b60ff85519273ffffffffffffffffffffffffffffffffffffffff8c604080613dec6139d760208a015173ffffffffffffffffffffffffffffffffffffffff1690565b613e106139d7602086015173ffffffffffffffffffffffffffffffffffffffff1690565b189701519101511894169218161717613edf575b50835182518601511015613ea557505090602083613e59613e47613e66956129df565b5193518c5183015185519103976129f9565b51510151910151906129f9565b5101525b015173ffffffffffffffffffffffffffffffffffffffff1690565b60808351019073ffffffffffffffffffffffffffffffffffffffff169052565b8495939492509060206040613e5985613ec0613ed1966129df565b5194510151885185519103976129f9565b510152519086510152613e6a565b613ee890613f1c565b38613e24565b97505050505050506080600091826020850152015290565b613f0e613f2d565b613d41565b50805115613d3c565b63bced929d6000526020526024601cfd5b506398e9db6e6000526004601cfd5b613f446110fb565b90600182528160005b60209081811015613f6f57602091613f636128b1565b90828501015201613f4d565b505050565b9261400e613fda9261404695613fa460046080835101516005811015614055575b613f9e816129a5565b14614fc5565b613fec84613fb183614478565b9098829a9296613fbf613f3c565b96613fc9886129df565b52613fd3876129df565b508661301b565b613fe3856129df565b51519889614062565b614008613ff7612945565b9183614002846129df565b526129df565b5161577d565b815173ffffffffffffffffffffffffffffffffffffffff16602083015173ffffffffffffffffffffffffffffffffffffffff16612b05565b6140506001600055565b600190565b61405d610678565b613f95565b60a08082015160c083015197969095939161407b6111a8565b9689604086019384515190600095865b8c898d86841061417b5750505050505050506080926004848701516140af816129a5565b101661416e575b6060809501968751519760005b8981106140f257505050505050505050506140df919250612105565b47806140e85750565b6106769033611d62565b8061414e8c8f8b8b8b8f93614123908c8c61411060019c8e516129f9565b5196870195865195880195865190614224565b8092528b83015190528151614137816106a8565b614140816106a8565b15614154575b503390613991565b016140c3565b4710614161575b38614146565b614169611d22565b61415b565b614176612ff8565b6140b6565b99856141e29392869798999c6141bd6141978860019a516129f9565b519485516141a4816106a8565b15179e8d606087019384519560808901968751906141ed565b9052528c610120613940825173ffffffffffffffffffffffffffffffffffffffff1690565b01908d93929161408b565b9093908481036142035750506107ad9350612fc5565b93836142186107ad979661421e949686612fc5565b93612fc5565b90612f07565b90939084810361423a5750506107ad9350612fc5565b93836142186107ad979661424f949686612fc5565b90612f53565b90815180825260208080930193019160005b828110614275575050505090565b909192938260a08261428a60019489516106ba565b01950193929101614267565b929094939160409182519460809182870191875273ffffffffffffffffffffffffffffffffffffffff94856020921682890152838189015286518093528160a089019701936000915b84831061432d57505050505050828285949361432893867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31989603606087015216971695614255565b0390a3565b90919293949784836001928b518051614345816106a8565b8252808401518c16848301528581015186830152606090810151908201520199019594930191906142df565b9092916000938285526002602052604085209283549260ff8460081c16614453576effffffffffffffffffffffffffffff8460101c166144425760ff8416156143d8575b505071010000000000000000000000000000010001909255509091506106769050565b6143e46129688261115f565b92818452368282011161443e57926201000194926106769798602084614436957fffffffffffffffffffffffffffffff00000000000000000000000000000000009883870137840101526084356151f2565b9185946143b5565b8780fd5b5063ee9e0e6386526020526024601cfd5b50631a51557486526020526024601cfd5b90805b61446f575090565b80910680614467565b80519061449161099960a084015160c0850151906151dd565b6146bf576effffffffffffffffffffffffffffff9260209284848401511693856040850151169360808301600481516144c9816129a5565b6144d2816129a5565b1461468c5786158688111761467f575b51916144ed836129a5565b60018093161586881016614672575b614505846147c6565b9761451a896000526002602052604060002090565b94614528610999878c6155c2565b614663578554938a60ff86161561462f575b5050508260881c8481159061455c575b505050508460881b9060101b17179055565b989798939091929361461f5760101c821688851461460b578189146145ed5788829102970297029587019686881187890302809103970391818711828411176145a7575b808061454a565b909591966145be6145b8848a614464565b82614464565b801501808092049804920495808711908311176145db57806145a0565b601190634e487b71600052526024601cfd5b9250505084959401948486118587030280910395033880808061454a565b93975095505050830393833880808061454a565b505050508394933880808061454a565b606061465261465b945173ffffffffffffffffffffffffffffffffffffffff1690565b920151916151f2565b38808a61453a565b50600097508796505050505050565b61467a614832565b6144fc565b614687614823565b6144e2565b5091936080939650600191506146ab9502186146b2575b015190614841565b9192909190565b6146ba614823565b6146a3565b5050600090600090600090565b8051906146e961099960a084015160c08501514210904210151690565b6146bf576effffffffffffffffffffffffffffff926020928484840151169385604085015116936080830160048151614721816129a5565b61472a816129a5565b1461479a5786158688111761478d575b5191614745836129a5565b60018093161586881016614780575b61475d846147c6565b97614772896000526002602052604060002090565b94614528610999878c615625565b614788614832565b614754565b614795614823565b61473a565b5091936080939650600191506146ab9502186147b9575b015190614a00565b6147c1614823565b6147b1565b6060810151516101408201511161153d578061481d73ffffffffffffffffffffffffffffffffffffffff6107ad93511673ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b90612423565b50635a052b326000526004601cfd5b5063a11b63ff6000526004601cfd5b6060906040828201805151610140840151036149f3575b60008061488361487c865173ffffffffffffffffffffffffffffffffffffffff1690565b9786614bc4565b9082895af1936148b38673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055821b1894156149e5575b6148d1615a9f565b94909195866149d7575b01805151825181116149c9575b6000905b89818310614993575050505281519083519180518311614985575b91906000925b888385106149325750505050505261492457918190565b61492d81614f96565b918190565b909192939661494188846129f9565b5161497961494f8a8a6129f9565b518681015187840151106149638285614cbf565b179260a080910151910151908091149015171590565b1717960192919061490d565b61498e87614f96565b614907565b9091976149a18985516129f9565b516149bf6149af8b886129f9565b5188830151898201511092614cbf565b17179701906148ec565b6149d288614f96565b6148e8565b6149e088614f96565b6148db565b6149ee85614f96565b6148c9565b6149fb614cb0565b614858565b60608082019182515161014082015103614b5c575b614a3d614a36825173ffffffffffffffffffffffffffffffffffffffff1690565b9482614bc4565b929060008094819282895af193614a748673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055831b189415614b5257614a91615a9f565b93919485614b115760400180515182518111614b445787905b8a818310614b1e575050505281519083519180518311614b1157919086925b89838510614ae857505050505052614ae15750918190565b9150918190565b9091929396614af788846129f9565b51614b0561494f8a8a6129f9565b17179601929190614ac9565b5050505050509150918190565b909197614b2c8985516129f9565b51614b3a6149af8b886129f9565b1717970190614aaa565b505050505050509150918190565b5050509150918190565b614b64614cb0565b614a15565b91909160408051936020928360e083028701018352818652839160010160051b92838701915b848410614b9e57505050505050565b60c060a0879285878c01528460808083893e606083019088013e01930193019291614b8f565b9190608490614c2b604051916398919765835260a0601c84019633602086015260806040860152614c176060614c01604084015185890190614c55565b9283608001828901520151838388010190614c55565b018094608082016080820152010190614c30565b010190565b8051603f0163ffffffe0169291610676918491905b829060045afa153d15176101c357565b9081519081815260209283808083019301918460051b0101915b84838210614c82575050505060071b0190565b8160809251805185528281015183860152604080820151908601526060809101519085015201910190614c6f565b50632165628a6000526004601cfd5b90815191604081015180156003851116614cfc575b6020809160608401516080850151149060408601511416948451149301519101511416161590565b506040820151600490931460030392614cd4565b9190811015614d51575b60051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea1813603018212156101c3570190565b614d596129af565b614d1a565b3560058110156101c35790565b5063fed398fc6000526004601cfd5b90815180825260208080930193019160005b828110614d9a575050505090565b909192938260a060019287518051614db1816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff168483015260408082015190830152606080820151908301526080908101519082015201950193929101614d8c565b90815180825260208080930193019160005b828110614e1b575050505090565b909192938260c060019287518051614e32816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff9081168584015260408083015190840152606080830151908401526080808301519084015260a091820151169082015201950193929101614e0d565b906005821015614e965752565b61221e610678565b90815260406020820152614ecb60408201835173ffffffffffffffffffffffffffffffffffffffff169052565b602082015173ffffffffffffffffffffffffffffffffffffffff1660608201526101806040830151614f42614f0e610160928360808701526101a0860190614d7a565b60608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08683030160a0870152614dfb565b93614f55608082015160c0860190614e89565b60a081015160e085015260c081015191610100928386015260e082015192610120938487015282015192610140938487015282015190850152015191015290565b63939792856000526020526024601cfd5b614faf614fd5565b6002600055565b614fbe614fd5565b6003600055565b614fcd614fd5565b600201600055565b600160005403614fe157565b637fa8a9876000526004601cfd5b600360005403614ffb57565b61067634611d42565b929091833b156150d157604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652816004528260245283604452858060648180855af11561505e5750505050604052606052565b85853d615085575b5063f486bc879052602052604052606052608052600160a05260a4601cfd5b601f3d0160051c9060051c9080600302918082116150b8575b505060205a9101106150b05785615066565b3d81803e3d90fd5b8080600392028380020360091c9203020101868061509e565b83635f15d6726000526020526024601cfd5b9392919091843b156151cb57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528160045282602452836044528460645260a06084528960a452898060c48180855af11561516257505050505060805260a05260c052604052606052565b89893d615187575b5063f486bc87905260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c9080600302918082116151b2575b505060205a9101106150b0578661516a565b8080600392028380020360091c920302010187806151a0565b84635f15d6726000526020526024601cfd5b9190428111428411151692831561154b575050565b929190338414615373576152046125a7565b9361524182867f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b908351926002601f601d860116106102e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9d860110166000146153655760018085169081604103927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf600593880101831c93808952880160209384820151928560238560e81c94019460e31c1690815285845191185283925b8684106153455750505050509661533f9161067697986152fe60406000209261556d565b600052526040600020907f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b90615379565b85859101938684821c841b166040600020815287865191185201926152da565b506106769495508190615379565b50509050565b909291926000948580528051957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051918860410390809160018111968715615503575b5050508514851515169788156153f5575b5050505050505050156153df57565b6153e7612681565b634f7fb80d6000526004601cfd5b909192939495809798508452604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8401938451957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08201976020600060648b519c7f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8c528d520189845afa9a8b6154a1575b505050505052525238808080808080806153d0565b600051036154af578061548c565b3b6153e7576154f557606001906041640101000000835160001a1a159114166154e05763815e1d646000526004601cfd5b631f003d0a6000525160001a6020526024601cfd5b638baa579f6000526004601cfd5b9091925060408601908151926060880151851a9061553b575b8752845260208360808660015afa508484528a865252513880806153bf565b50601b8360ff1c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416835261551c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020910160051b60010160007f00000000000000000000000000000000000000000000000000000000000000003c60005190565b905460ff8160081c16615613576effffffffffffffffffffffffffffff8160101c1690816155f3575b505050600190565b60881c1115615604575b80806155eb565b61560d9061568d565b386155fd565b50631a5155746000526020526024601cfd5b906000905460ff8160081c16615684576effffffffffffffffffffffffffffff8160101c16908161565a575b50505050600190565b60881c111561566a578080615651565b615675575b50600090565b61567e9061568d565b3861566f565b50905050600090565b6310fda3e16000526020526024601cfd5b9190608082019081356156b08161064b565b33141590600460018211911016166156c757505050565b610676926156f56139d76060604051956317b1f94287526020808801528460408801523382880152016111bf565b6080840152606061014461012085013761014060a08401526101e060c0840152615778601c6103246102643561574160a08202918261016001906101808a019060051b61020001614c45565b6102a0810160e08801528461032082890160006102e08201526102c084016101008b015260016103008201520152019401926111bf565b6159e3565b919082519060808201918251926005841015615838575b6157c5602083019473ffffffffffffffffffffffffffffffffffffffff865116331415906004600182119110161690565b156157ed5750906157df91608061067696015190856158c9565b91519263fb5014fc93615a4b565b600491949350516157fd816129a5565b615806816129a5565b0361583257610676936158269184519460808660601b9301519085615845565b91639397928593615a4b565b50505050565b615840610678565b615794565b9493919260c060a4946158b5614c2b946040519663f4dd92ce8852601c88019a1860a088015260a0602088015261589f60606158886040840151878b0190614c55565b928360a00160408b0152015185838a01019061599b565b019160a083016060880152838388010190614c30565b01809460a08201608082015201019061597f565b9392614c2b906101649392604051936317b1f9428552601c85019760208087015260408601523360608601528151608086015260a082015161012086015260c082015190610140918287015260e08301516101608701528160a087015261596f60408401519361595a606061594461018097888c0190614c55565b9283870160c08c0152015186838b01019061599b565b019183830160e0890152848389010190614c30565b0194859182016101008201520101905b6129f5602092839283815180845260051b948593019101614c45565b8051908183526020928380808401938560051b01019101915b8181106159c55750505060a0020190565b60a090818481835160045afa153d15176101c35785019201916159b4565b6020909391937fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615a3c5715615a2c575050565b63fb5014fc90526020526024601cfd5b5063fb5014fc90612230612681565b602090949391947fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615a96571561223057505050565b50612230612681565b60009081906080803d109060009081908280918515615b42575b8515615aca575b5050505050929190565b91939750919550602094939480920196604051918360c08302840101604052818352839160010160051b98898401905b8a8410615b1f5750505050615b1493949596509501614b69565b913880808080615ac0565b60a083879284878901528181863e60608501518286015201920193019290615afa565b9450909150604081803e5190602051913d81113d8411179485615ab95794508093506020915060003e60005191602082813e602051903d8260a0028560071b0186011161ffff83861711179460008052615ab9565b908135641fffffffe08160051b169060405191602091828285010160405263ffffffff809116845260005b828110615bd25750929450505050565b80615be885848180958c010135168a01016119cd565b82828801015201615bc256fea164736f6c6343000811000a60406080815234610081576100156103a0604052565b6018806080526103003660a03761002a6101fe565b61003261027d565b9280519160005b84811061004c5760fe608052610301609ff35b8061005860019261011f565b6003028352610067878461016e565b60208151910120610077826101d5565b5284845201610039565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b038211176100b857604052565b6100c0610086565b604052565b60c081019081106001600160401b038211176100b857604052565b61010081019081106001600160401b038211176100b857604052565b601f909101601f19168101906001600160401b038211908210176100b857604052565b906001820180921161012d57565b634e487b7160e01b600052601160045260246000fd5b9081519160005b83811061015b575050016000815290565b806020809284010151818501520161014a565b6101d3906101c56101b2949360066040519687947f42756c6b4f72646572284f72646572436f6d706f6e656e74730000000000000060208701526039860190610143565b6520747265652960d01b81520190610143565b03601f1981018452836100fc565b565b6080518110156101e85760051b60a00190565b634e487b7160e01b600052603260045260246000fd5b604051608081016001600160401b0381118282101761024b575b6040526048815260208101606036823760688201905b81811061023a57505090565b625b325d60e81b815260030161022e565b610253610086565b610218565b6101c561027794936102776101d3946040519788956020870190610143565b90610143565b6104e56040805161028d8161009d565b606a81527f4f666665724974656d2875696e7438206974656d547970652c6164647265737360208201527f20746f6b656e2c75696e74323536206964656e7469666965724f724372697465828201527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606082015269656e64416d6f756e742960b01b60808201528151610320816100c5565b608481527f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e7469666965838201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c6164647265737320726563697069608082015263656e742960e01b60a08201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c438351936103f5856100e0565b60d485527f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208601528401527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608401527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808401527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08401527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08401527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0840152610258565b9056fe00000000000000000000000000000000f9490004c11cef243f5400493c00ad630000000000000000 +``` + +4. Validate deployments were successful by checking that `Seaport` is returned: + +``` +cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000006c3852cbEf3e08E8dF289169EdE581 'name()') +cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000000006c7676171937C444f6BDe3D6282 'name()') +``` + +## Verifying Seaport and ConduitController +After `Seaport` and `ConduitController` are deployed, they are verified as follows: + +1. Ensure that `EXPLORER_API_KEY` and `NETWORK_RPC` are set in `.env` appropriately. +2. Navigate to the `1.1` release tag. + +3. Verify `ConduitController` by calling: + +``` +npx hardhat verify --network verificationNetwork "0x00000000F9490004C11Cef243f5400493c00Ad63" +``` + +4. Verify `Seaport 1.1` by calling: + +``` +npx hardhat verify --network verificationNetwork "0x00000000006c3852cbEf3e08E8dF289169EdE581" "0x00000000F9490004C11Cef243f5400493c00Ad63" +``` + +5. Navigate to the `1.2` release tag. + +6. Verify `Seaport 1.2` by calling: + +``` +npx hardhat verify --network verificationNetwork "0x00000000000006c7676171937C444f6BDe3D6282" "0x00000000F9490004C11Cef243f5400493c00Ad63" +``` \ No newline at end of file diff --git a/docs/FunctionSignatures.md b/docs/FunctionSignatures.md new file mode 100644 index 000000000..cc7e950c9 --- /dev/null +++ b/docs/FunctionSignatures.md @@ -0,0 +1,64 @@ +# Seaport Function Signatures + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function NameSignature (1.2)Signature (1.1)
fulfillOrder0xb3a34c4c0xb3a34c4c
fulfillAdvancedOrder0xe7acab240xe7acab24
matchOrders0xa81744040xa8174404
matchAdvancedOrders0xf2d12b120x55944a42
fulfillAvailableOrders0xed98a5740xed98a574
fulfillAvailableAdvancedOrders0x87201b410x87201b41
fulfillBasicOrder0xfb0f3ee10xfb0f3ee1
fulfillBasicOrder_efficient_6GL6yc0x00000000n/a
cancel0xfd9f1e100xfd9f1e10
validate0x881477320x88147732
incrementCounter0x5b34b9660x5b34b966
\ No newline at end of file diff --git a/docs/SeaportDocumentation.md b/docs/SeaportDocumentation.md index 73c66a4de..93b7f144f 100644 --- a/docs/SeaportDocumentation.md +++ b/docs/SeaportDocumentation.md @@ -16,17 +16,24 @@ Each order contains eleven key components: - The `offerer` of the order supplies all offered items and must either fulfill the order personally (i.e. `msg.sender == offerer`) or approve the order via signature (either standard 65-byte EDCSA, 64-byte EIP-2098, or an EIP-1271 `isValidSignature` check) or by listing the order on-chain (i.e. calling `validate`). - The `zone` of the order is an optional secondary account attached to the order with two additional privileges: - The zone may cancel orders where it is named as the zone by calling `cancel`. (Note that offerers can also cancel their own orders, either individually or for all orders signed with their current counter at once by calling `incrementCounter`). - - "Restricted" orders (as specified by the order type) must either be executed by the zone or the offerer, or must be approved as indicated by a call to an `isValidOrder` or `isValidOrderIncludingExtraData` view function on the zone. + - "Restricted" orders (as specified by the order type) must either be executed by the zone or the offerer, or must be approved as indicated by a call to `validateOrder` when the caller is not the zone. - The `offer` contains an array of items that may be transferred from the offerer's account, where each item consists of the following components: - - The `itemType` designates the type of item, with valid types being Ether (or other native token for the given chain), ERC20, ERC721, ERC1155, ERC721 with "criteria" (explained below), and ERC1155 with criteria. + - The `itemType` designates the type of item, with valid types being: + * Ether (or other native token for the given chain) enum value: `NATIVE = 0` + * ERC20: enum value: `ERC20 = 1` + * ERC721: enum value: `ERC721 = 2` + * ERC1155: enum value: `ERC1155 = 3` + * ERC721 with "criteria" (explained below): enum value: `ERC721_WITH_CRITERIA = 4` + * ERC1155 with "criteria" (explained below): enum value: `ERC1155_WITH_CRITERIA = 5` - The `token` designates the account of the item's token contract (with the null address used for Ether or other native tokens). - The `identifierOrCriteria` represents either the ERC721 or ERC1155 token identifier or, in the case of a criteria-based item type, a merkle root composed of the valid set of token identifiers for the item. This value will be ignored for Ether and ERC20 item types, and can optionally be zero for criteria-based item types to allow for any identifier. - The `startAmount` represents the amount of the item in question that will be required should the order be fulfilled at the moment the order becomes active. - The `endAmount` represents the amount of the item in question that will be required should the order be fulfilled at the moment the order expires. If this value differs from the item's `startAmount`, the realized amount is calculated linearly based on the time elapsed since the order became active. - The `consideration` contains an array of items that must be received in order to fulfill the order. It contains all of the same components as an offered item, and additionally includes a `recipient` that will receive each item. This array may be extended by the fulfiller on order fulfillment so as to support "tipping" (e.g. relayer or referral payments). -- The `orderType` designates one of four types for the order depending on two distinct preferences: +- The `orderType` designates one of four types for the order depending on three distinct preferences: - `FULL` indicates that the order does not support partial fills, whereas `PARTIAL` enables filling some fraction of the order, with the important caveat that each item must be cleanly divisible by the supplied fraction (i.e. no remainder after division). - - `OPEN` indicates that the call to execute the order can be submitted by any account, whereas `RESTRICTED` requires that the order either be executed by the offerer or the zone of the order, or that a magic value indicating that the order is approved is returned upon calling an `isValidOrder` or `isValidOrderIncludingExtraData` view function on the zone. + - `OPEN` indicates that the call to execute the order can be submitted by any account, whereas `RESTRICTED` requires that the order either be executed by the offerer or the zone of the order, or that a magic value indicating that the order is approved is returned upon calling `validateOrder` when the caller is not the zone. + - `CONTRACT` indicates that the order will be generated by the offerer upon a call from Seaport to `generateOrder`, then verified after execution with a follow-on call to `ratifyOrder` on the offerer. - The `startTime` indicates the block timestamp at which the order becomes active. - The `endTime` indicates the block timestamp at which the order expires. This value and the `startTime` are used in conjunction with the `startAmount` and `endAmount` of each item to derive their current amount. - The `zoneHash` represents an arbitrary 32-byte value that will be supplied to the zone when fulfilling restricted orders that the zone can utilize when making a determination on whether to authorize the order. @@ -50,7 +57,7 @@ Orders are fulfilled via one of four methods: - If the order has an ERC721 item, that item has an amount of `1`. - If the order has multiple consideration items and all consideration items other than the first consideration item have the same item type as the offered item, the offered item amount is not less than the sum of all consideration item amounts excluding the first consideration item amount. - Calling one of two "fulfill available" functions, `fulfillAvailableOrders` and `fulfillAvailableAdvancedOrders`, where a group of orders are supplied alongside a group of fulfillments specifying which offer items can be aggregated into distinct transfers and which consideration items can be accordingly aggregated, and where any orders that have been cancelled, have an invalid time, or have already been fully filled will be skipped without causing the rest of the available orders to revert. Additionally, any remaining orders will be skipped once `maximumFulfilled` available orders have been located. Similar to the standard fulfillment method, all offer items will be transferred from the respective offerer to the fulfiller, then all consideration items will be transferred from the fulfiller to the named recipient. -- Calling one of two "match" functions, `matchOrders` and `matchAdvancedOrders`, where a group of explicit orders are supplied alongside a group of fulfillments specifying which offer items to apply to which consideration items (and with the "advanced" case operating in a similar fashion to the standard method, but supporting partial fills via supplied `numerator` and `denominator` fractional values as well as an optional `extraData` argument that will be supplied as part of a call to the `isValidOrderIncludingExtraData` view function on the zone when fulfilling restricted order types). Note that orders fulfilled in this manner do not have an explicit fulfiller; instead, Seaport will simply ensure coincidence of wants across each order. +- Calling one of two "match" functions, `matchOrders` and `matchAdvancedOrders`, where a group of explicit orders are supplied alongside a group of fulfillments specifying which offer items to apply to which consideration items (and with the "advanced" case operating in a similar fashion to the standard method, but supporting partial fills via supplied `numerator` and `denominator` fractional values as well as an optional `extraData` argument that will be supplied as part of a call to the `validateOrder` function when fulfilling restricted order types or to `generateOrder` and `ratifyOrder` as "context" on contract order types). Note that orders fulfilled in this manner do not have an explicit fulfiller; instead, Seaport will simply ensure coincidence of wants across each order. Note also that contract orders do not enforce usage of a specific conduit, but a contract offerer can require the usage of a specific conduit by setting allowances or approval on tokens for specific conduits. If a fulfiller does not supply the correct conduit key, the call will revert. There's currently no endpoint for finding which conduit a given contract offerer prefers. While the standard method can technically be used for fulfilling any order, it suffers from key efficiency limitations in certain scenarios: @@ -58,6 +65,14 @@ While the standard method can technically be used for fulfilling any order, it s - It requires the fulfiller to approve each consideration item, even if the consideration item can be fulfilled using an offer item (as is commonly the case when fulfilling an order that offers ERC20 items for an ERC721 or ERC1155 item and also includes consideration items with the same ERC20 item type for paying fees). - It can result in unnecessary transfers, whereas in the "match" case those transfers can be reduced to a more minimal set. +> Note: When a collection-wide criteria-based item (criteria = 0) is provided as an input to a contract order, the contract offerer has full latitude to choose any identifier they want mid-flight, which differs from the usual behavior. For regular criteria-based orders with identifierOrCriteria = 0, the fulfiller can pick which identifier to receive by providing a CriteriaResolver. For contract offers with identifierOrCriteria = 0, Seaport does not expect a corresponding CriteriaResolver, and will revert if one is provided. See `_getGeneratedOrder` and `_compareItems` for more detail. + +> Note: Calls to Seaport that would fulfill or match a collection of advanced orders can be monitored and where there are unused offer items, it's possible for a third party to claim them. Anyone can monitor the mempool to find calls to `fulfillAvailableOrders`, `fulfillAvailableAdvancedOrders`, `matchOrders`, `matchAdvancedOrders` and calculate if there are any unused offer item amounts. If there are unused offer item amounts, the third party can create orders with no offer items, but with consideration items mirroring the unused offer items and populate the fulfillment aggregation data to match the unused offer items with the new mirrored consideration items. This would allow the third party to claim the unused offer items. A contract offerer or a zone could prevent this, but by default, it's possible. + +> Note: Contract orders can supply additional offer amounts when the order is executed. However, if they supply extra offer items with criteria, on the fly, the fulfiller won't be able to supply the necessary criteria resolvers, which would make fulfilling the order infeasible. Contract offerers should specifically avoid returning criteria-based items and generally avoid mismatches between previewOrder and what's executed on-chain. + +> Note: In some cases, contract offerers will be able to lower the value of an offered NFT by transferring out valuable tokens that are attached to the NFT. For example, a contract offerer could modify a property of an NFT when Seaport calls `generateOrder`. Consider using a mirrored order that allows for a post-transfer validation, such as a contract order or a restricted order, in cases like this. + ### Balance and Approval Requirements When creating an offer, the following requirements should be checked to ensure that the order will be fulfillable: @@ -149,14 +164,14 @@ When matching a group of orders via `matchOrders` or `matchAdvancedOrders`, step 8. Scan each consideration item and ensure that none still have a nonzero amount remaining 9. Perform transfers as part of each execution - Use either conduit or Seaport directly to source approvals, depending on the original order type - - Ignore each execution where `to == from` or `amount == 0` _(NOTE: the current implementation does not perform this last optimization)_ + - Ignore each execution where `to == from` ## Known Limitations and Workarounds - As all offer and consideration items are allocated against one another in memory, there are scenarios in which the actual received item amount will differ from the amount specified by the order — notably, this includes items with a fee-on-transfer mechanic. Orders that contain items of this nature (or, more broadly, items that have some post-fulfillment state that should be met) should leverage "restricted" order types and route the order fulfillment through a zone contract that performs the necessary checks after order fulfillment is completed. -- As all offer items are taken directly from the offerer and all consideration items are given directly to the named recipient, there are scenarios where those accounts can increase the gas cost of order fulfillment or block orders from being fulfilled outright depending on the item being transferred. If the item in question is Ether or a similar native token, a recipient can throw in the payable fallback or even spend excess gas from the submitter. Similar mechanics can be leveraged by both offerers and receives if the item in question is a token with a transfer hook (like ERC1155 and ERC777) or a non-standard token implementation. Potential remediations to this category of issue include wrapping Ether as WETH as a fallback if the initial transfer fails and allowing submitters to specify the amount of gas that should be allocated as part of a given fulfillment. Orders that support explicit fulfillments can also elect to leave problematic or unwanted offer items unspent as long as all consideration items are received in full. +- As all offer items are taken directly from the offerer and all consideration items are given directly to the named recipient, there are scenarios where those accounts can increase the gas cost of order fulfillment or block orders from being fulfilled outright depending on the item being transferred. If the item in question is Ether or a similar native token, a recipient can throw in the payable fallback or even spend excess gas from the submitter. Similar mechanics can be leveraged by both offerers and receivers if the item in question is a token with a transfer hook (like ERC1155 and ERC777) or a non-standard token implementation. Potential remediations to this category of issue include wrapping Ether as WETH as a fallback if the initial transfer fails and allowing submitters to specify the amount of gas that should be allocated as part of a given fulfillment. Orders that support explicit fulfillments can also elect to leave problematic or unwanted offer items unspent as long as all consideration items are received in full. - As fulfillments may be executed in whatever sequence the fulfiller specifies as long as the fulfillments are all executable, as restricted orders are validated via zones prior to execution, and as orders may be combined with other orders or have additional consideration items supplied, any items with modifiable state are at risk of having that state modified during execution if a payable Ether recipient or onReceived 1155 transfer hook is able to modify that state. By way of example, imagine an offerer offers WETH and requires some ERC721 item as consideration, where the ERC721 should have some additional property like not having been used to mint some other ERC721 item. Then, even if the offerer enforces that the ERC721 have that property via a restricted order that checks for the property, a malicious fulfiller could include a second order (or even just an additional consideration item) that uses the ERC721 item being sold to mint before it is transferred to the offerer. One category of remediation for this problem is to use restricted orders that do not implement `isValidOrder` and actually require that order fulfillment is routed through them so that they can perform post-fulfillment validation. Another interesting solution to this problem that retains order composability is to "fight fire with fire" and have the offerer include a "validator" ERC1155 consideration item on orders that require additional assurances; this would be a contract that contains the ERC1155 interface but is not actually an 1155 token, and instead leverages the `onReceived` hook as a means to validate that the expected invariants were upheld, reverting the "transfer" if the check fails (so in the case of the example above, this hook would ensure that the offerer was the owner of the ERC721 item in question and that it had not yet been used to mint the other ERC721). The key limitation to this mechanic is the amount of data that can be supplied in-band via this route; only three arguments ("from", "identifier", and "amount") are available to utilize. -- As all consideration items are supplied at the time of order creation, dynamic adjustment of recipients or amounts after creation (e.g. modifications to royalty payout info) is not supported. However, a zone can enforce that a given restricted order contains _new_ dynamically computed consideration items by deriving them and either supplying them manually or ensuring that they are present via `isValidZoneIncludingExtraData` since consideration items can be extended arbitrarily, with the important caveat that no more than the original offer item amounts can be spent. +- As all consideration items are supplied at the time of order creation, dynamic adjustment of recipients or amounts after creation (e.g. modifications to royalty payout info) is not supported. However, a zone can enforce that a given restricted order contains _new_ dynamically computed consideration items by deriving them and either supplying them manually or ensuring that they are present via `validateOrder` since consideration items can be extended arbitrarily, with the important caveat that no more than the original offer item amounts can be spent. - As all criteria-based items are tied to a particular token, there is no native way to construct orders where items specify cross-token criteria. Additionally, each potential identifier for a particular criteria-based item must have the same amount as any other identifier. - As orders that contain items with ascending or descending amounts may not be filled as quickly as a fulfiller would like (e.g. transactions taking longer than expected to be included), there is a risk that fulfillment on those orders will supply a larger item amount, or receive back a smaller item amount, than they intended or expected. One way to prevent these outcomes is to utilize `matchOrders`, supplying a contrasting order for the fulfiller that explicitly specifies the maximum allowable offer items to be spent and consideration items to be received back. Special care should be taken when handling orders that contain both brief durations as well as items with ascending or descending amounts, as realized amounts may shift appreciably in a short window of time. - As all items on orders supporting partial fills must be "cleanly divisible" when performing a partial fill, orders with multiple items should be constructed with care. A straightforward heuristic is to start with a "unit" bundle (e.g. 1 NFT item A, 3 NFT item B, and 5 NFT item C for 2 ETH) then applying a multiple to that unit bundle (e.g. 7 of those units results in a partial order for 7 NFT item A, 21 NFT item B, and 35 NFT item C for 14 ETH). diff --git a/docs/ZoneDocumentation.md b/docs/ZoneDocumentation.md new file mode 100644 index 000000000..21294191c --- /dev/null +++ b/docs/ZoneDocumentation.md @@ -0,0 +1,21 @@ +# Zone Documentation + +The `zone` of the order is an optional secondary account attached to the order with two additional privileges: + +1. The zone may cancel orders where it is named as the zone by calling `cancel`. (Note that offerers can also cancel their own orders, either individually or for all orders signed with their current counter at once by calling `incrementCounter`). +2. "Restricted" orders (as specified by the order type) must be approved as indicated by a call to a `validateOrder` when the caller is not the zone. + +An example zone contract implementation can be found at `/contracts/zones/PausableZone.sol`. + +The `PausableZone` contract can be used by its controller to cancel orders, execute fulfillment on restricted order, and pause all orders which use it as a zone. + +## Ideas + +New zones can be permissionlessly deployed and utilized to extend the feature set of the core Seaport marketplace. Examples include: + +- Helping to prevent sales of compromised items +- Pausing orders in case of an emergency without invalidating approvals +- Limiting the number of NFTs from a particular collection that can be sold in a given amount of time +- Enforcing a particular floor or ceiling price for certain items +- Making arbitrary calls to outside data sources +- Tracking additional incentives for completing valid orders diff --git a/docs/prepare-docs.js b/docs/prepare-docs.js new file mode 100644 index 000000000..a661d5764 --- /dev/null +++ b/docs/prepare-docs.js @@ -0,0 +1,33 @@ +const fs = require("fs"); +const glob = require("glob"); + +/** + * This script is used to prepare the solidity files for use with `forge doc` + * Seaport handles decoding structs from calldata itself, but solc will use its + * default decoding if given names. Thus, to comply with natspec, use @custom + * tags to specify "unnamed" params and their names. This is not compatible with + * forge doc, so they are turned back into normal @param tags before generating + * documentation. + */ + +glob("contracts/**/*.sol", {}, (er, files) => { + files.forEach((file) => { + let content = fs.readFileSync(file, "utf-8"); + + // Restore normal @param tags. + content = content.replace( + /@custom:param ([a-zA-z0-9]+)/g, + "@param $1 " + ); + + // Replace @custom:name tags with name of the param in the correct location. + content = content.replace( + /\/\*\*\s*\*?\s*@custom:name\s*([a-zA-Z0-9]*)\s*\*\/\s*([a-zA-z[\]]+ calldata)\s*(,|\))/g, + "$2 $1$3" + ); + + // Once files have been overwritten, call forge doc to generate + // documentation complete with descriptions for "unnamed" variables. + fs.writeFileSync(file, content, "utf-8"); + }); +}); diff --git a/eip-712-types/bulkOrder.js b/eip-712-types/bulkOrder.js new file mode 100644 index 000000000..8e8ac533f --- /dev/null +++ b/eip-712-types/bulkOrder.js @@ -0,0 +1,35 @@ +const bulkOrderType = { + BulkOrder: [{ name: "tree", type: "OrderComponents[2][2][2][2][2][2][2]" }], + OrderComponents: [ + { name: "offerer", type: "address" }, + { name: "zone", type: "address" }, + { name: "offer", type: "OfferItem[]" }, + { name: "consideration", type: "ConsiderationItem[]" }, + { name: "orderType", type: "uint8" }, + { name: "startTime", type: "uint256" }, + { name: "endTime", type: "uint256" }, + { name: "zoneHash", type: "bytes32" }, + { name: "salt", type: "uint256" }, + { name: "conduitKey", type: "bytes32" }, + { name: "counter", type: "uint256" }, + ], + OfferItem: [ + { name: "itemType", type: "uint8" }, + { name: "token", type: "address" }, + { name: "identifierOrCriteria", type: "uint256" }, + { name: "startAmount", type: "uint256" }, + { name: "endAmount", type: "uint256" }, + ], + ConsiderationItem: [ + { name: "itemType", type: "uint8" }, + { name: "token", type: "address" }, + { name: "identifierOrCriteria", type: "uint256" }, + { name: "startAmount", type: "uint256" }, + { name: "endAmount", type: "uint256" }, + { name: "recipient", type: "address" }, + ], +}; + +module.exports = Object.freeze({ + bulkOrderType, +}); diff --git a/foundry.toml b/foundry.toml index 30d596dca..9da1ed98c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,45 +1,62 @@ -[default] -solc = '0.8.14' -via_ir = true +[profile.default] +solc = '0.8.17' src = 'contracts' out = 'out' libs = ["node_modules", "lib"] test = 'test/foundry' remappings = [ - 'ds-test=lib/ds-test/src/', - 'forge-std=lib/forge-std/src/', + 'ds-test/=lib/ds-test/src/', + 'forge-std/=lib/forge-std/src/', '@rari-capital/solmate/=lib/solmate/', - 'contracts/=contracts/', 'murky/=lib/murky/src/', + 'openzeppelin-contracts/=lib/openzeppelin-contracts/' +] +optimizer_runs = 4_294_967_295 +fs_permissions = [ + { access = "read", path = "./optimized-out" }, + { access = "read", path = "./reference-out" }, ] -fuzz_runs = 5000 -fuzz_max_global_rejects = 2_000_000 -optimizer_runs = 19_066 -[reference] -solc = '0.8.7' -via_ir = false +[fuzz] +runs = 1_000 +max_test_rejects = 1_000_000 + +[profile.reference] +solc = '0.8.13' src = 'reference' out = 'reference-out' -# specify something so it doesn't try to compile the 0.8.14 files in test/foundry +script = 'reference' +# specify something so it doesn't try to compile the 0.8.17 files in test/foundry test = 'reference' -[optimized] +[profile.optimized] +via_ir = true out = 'optimized-out' +script = 'contracts' +# no need to compile tests with via-ir since they load optimized bytecode directly by default +test ='contracts' -[test] -via_ir = false +[profile.test] src = 'test/foundry' -[lite] +[profile.test.fuzz] +runs = 1_000 + +[profile.lite] out = 'optimized-out' -via_ir = false -fuzz_runs = 1000 -[local] -via_ir = false -fuzz_runs = 1000 -src = 'reference' -out = 'reference-out' +[profile.debug] +src = 'contracts' + +[profile.offerers] +src='offerers' +test='offerers' +out = 'offerers-out' +script = 'offerers' + +[fmt] +line_length = 80 +tab_width = 4 +bracket_spacing = true # See more config options https://github.com/gakonst/foundry/tree/master/config diff --git a/hardhat-coverage.config.ts b/hardhat-coverage.config.ts index bea7c4d82..03dca0df8 100644 --- a/hardhat-coverage.config.ts +++ b/hardhat-coverage.config.ts @@ -1,13 +1,11 @@ -import * as dotenv from "dotenv"; +import type { HardhatUserConfig } from "hardhat/config"; -import { HardhatUserConfig, task } from "hardhat/config"; -import "@nomiclabs/hardhat-waffle"; +import "dotenv/config"; +import "@nomicfoundation/hardhat-chai-matchers"; import "@typechain/hardhat"; import "hardhat-gas-reporter"; import "solidity-coverage"; -dotenv.config(); - // You need to export an object to set up your config // Go to https://hardhat.org/config/ to learn more @@ -15,7 +13,7 @@ const config: HardhatUserConfig = { solidity: { compilers: [ { - version: "0.8.14", + version: "0.8.17", settings: { viaIR: false, optimizer: { diff --git a/hardhat-reference-coverage.config.ts b/hardhat-reference-coverage.config.ts index 1b4b935a9..ecb577cff 100644 --- a/hardhat-reference-coverage.config.ts +++ b/hardhat-reference-coverage.config.ts @@ -1,13 +1,11 @@ -import * as dotenv from "dotenv"; +import type { HardhatUserConfig } from "hardhat/config"; -import { HardhatUserConfig, task } from "hardhat/config"; -import "@nomiclabs/hardhat-waffle"; +import "dotenv/config"; +import "@nomicfoundation/hardhat-chai-matchers"; import "@typechain/hardhat"; import "hardhat-gas-reporter"; import "solidity-coverage"; -dotenv.config(); - // You need to export an object to set up your config // Go to https://hardhat.org/config/ to learn more @@ -15,7 +13,7 @@ const config: HardhatUserConfig = { solidity: { compilers: [ { - version: "0.8.7", + version: "0.8.13", settings: { viaIR: false, optimizer: { diff --git a/hardhat-reference.config.ts b/hardhat-reference.config.ts index 5ae11e791..beca9daea 100644 --- a/hardhat-reference.config.ts +++ b/hardhat-reference.config.ts @@ -1,15 +1,14 @@ -import * as dotenv from "dotenv"; +import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; +import { subtask } from "hardhat/config"; + +import type { HardhatUserConfig } from "hardhat/config"; -import { HardhatUserConfig, subtask, task } from "hardhat/config"; -import "@nomiclabs/hardhat-waffle"; +import "dotenv/config"; +import "@nomicfoundation/hardhat-chai-matchers"; import "@typechain/hardhat"; import "hardhat-gas-reporter"; import "solidity-coverage"; -dotenv.config(); - -import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; - // Filter Reference Contracts subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( async (_, __, runSuper) => { @@ -29,7 +28,7 @@ const config: HardhatUserConfig = { solidity: { compilers: [ { - version: "0.8.7", + version: "0.8.13", settings: { viaIR: false, optimizer: { diff --git a/hardhat.config.ts b/hardhat.config.ts index 2676323cf..575615292 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,15 +1,21 @@ -import * as dotenv from "dotenv"; +import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; +import { subtask, task } from "hardhat/config"; + +import { compareLastTwoReports } from "./scripts/compare_reports"; +import { printLastReport } from "./scripts/print_report"; +import { getReportPathForCommit } from "./scripts/utils"; +import { writeReports } from "./scripts/write_reports"; + +import type { HardhatUserConfig } from "hardhat/config"; -import { HardhatUserConfig, subtask } from "hardhat/config"; -import "@nomiclabs/hardhat-waffle"; +import "dotenv/config"; +import "@nomiclabs/hardhat-ethers"; +import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomiclabs/hardhat-etherscan"; import "@typechain/hardhat"; import "hardhat-gas-reporter"; import "solidity-coverage"; -import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; - -dotenv.config(); - // Filter Reference Contracts subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( async (_, __, runSuper) => { @@ -19,6 +25,43 @@ subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( } ); +task("write-reports", "Write pending gas reports").setAction( + async (taskArgs, hre) => { + writeReports(hre); + } +); + +task("compare-reports", "Compare last two gas reports").setAction( + async (taskArgs, hre) => { + compareLastTwoReports(hre); + } +); + +task("print-report", "Print the last gas report").setAction( + async (taskArgs, hre) => { + printLastReport(hre); + } +); + +const optimizerSettingsNoSpecializer = { + enabled: true, + runs: 4_294_967_295, + details: { + peephole: true, + inliner: true, + jumpdestRemover: true, + orderLiterals: true, + deduplicate: true, + cse: true, + constantOptimizer: true, + yulDetails: { + stackAllocation: true, + optimizerSteps: + "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul [j]Tpeulxa[rul]xa[r]cLgvifCTUca[r]LSsTOtfDnca[r]Iulc]jmul[jul] VcTOcul jmul", + }, + }, +}; + // You need to export an object to set up your config // Go to https://hardhat.org/config/ to learn more @@ -26,12 +69,21 @@ const config: HardhatUserConfig = { solidity: { compilers: [ { - version: "0.8.14", + version: "0.8.17", settings: { viaIR: true, optimizer: { - enabled: true, - runs: 19066, + ...(process.env.NO_SPECIALIZER + ? optimizerSettingsNoSpecializer + : { enabled: true, runs: 4_294_967_295 }), + }, + metadata: { + bytecodeHash: "none", + }, + outputSelection: { + "*": { + "*": ["evm.assembly", "irOptimized", "devdoc"], + }, }, }, }, @@ -57,17 +109,36 @@ const config: HardhatUserConfig = { }, }, }, + "contracts/helper/TransferHelper.sol": { + version: "0.8.14", + settings: { + viaIR: true, + optimizer: { + enabled: true, + runs: 1000000, + }, + }, + }, }, }, networks: { hardhat: { blockGasLimit: 30_000_000, throwOnCallFailures: false, + allowUnlimitedContractSize: false, + }, + verificationNetwork: { + url: process.env.NETWORK_RPC ?? "", }, }, gasReporter: { enabled: process.env.REPORT_GAS !== undefined, currency: "USD", + outputFile: getReportPathForCommit(), + noColors: true, + }, + etherscan: { + apiKey: process.env.EXPLORER_API_KEY, }, // specify separate cache for hardhat, since it could possibly conflict with foundry's paths: { cache: "hh-cache" }, diff --git a/lib/ds-test b/lib/ds-test index 9310e879d..cd98eff28 160000 --- a/lib/ds-test +++ b/lib/ds-test @@ -1 +1 @@ -Subproject commit 9310e879db8ba3ea6d5c6489a579118fd264a3f5 +Subproject commit cd98eff28324bfac652e63a239a60632a761790b diff --git a/lib/forge-std b/lib/forge-std index d72ef5823..d666309ed 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit d72ef58231da19b23df3cc6fa91e623a0b728878 +Subproject commit d666309ed272e7fa16fa35f28d63ee6442df45fc diff --git a/lib/murky b/lib/murky index 2caa3b01b..5f962edf9 160000 --- a/lib/murky +++ b/lib/murky @@ -1 +1 @@ -Subproject commit 2caa3b01b888a03152cfebfec8acb24eb8036c16 +Subproject commit 5f962edf98f2aeaf2706f7bfd07fac4532b42cc6 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 000000000..24d1bb668 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 24d1bb668a1152528a6e6d71c2e285d227ed19d9 diff --git a/lib/solmate b/lib/solmate index eaaccf88a..3a752b8c8 160000 --- a/lib/solmate +++ b/lib/solmate @@ -1 +1 @@ -Subproject commit eaaccf88ac5290299884437e1aee098a96583d54 +Subproject commit 3a752b8c83427ed1ea1df23f092ea7a810205b6c diff --git a/package.json b/package.json index c6b30f07a..141c8079e 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,31 @@ { "name": "seaport", - "version": "1.1.0", + "version": "1.2.0", "description": "Seaport is a marketplace protocol for safely and efficiently buying and selling NFTs. Each listing contains an arbitrary number of items that the offerer is willing to give (the \"offer\") along with an arbitrary number of items that must be received along with their respective receivers (the \"consideration\").", "main": "contracts/Seaport.sol", "author": "0age", "license": "MIT", "private": false, "engines": { - "node": ">=16.0.0" + "node": ">=16.15.1" }, "dependencies": { + "@nomicfoundation/hardhat-network-helpers": "^1.0.7", "ethers": "^5.5.3", "ethers-eip712": "^0.2.0", - "hardhat": "https://github.com/0age/hardhat/releases/download/viaIR-2.9.3/hardhat-v2.9.3.tgz" + "hardhat": "^2.12.1-ir.0", + "merkletreejs": "^0.3.9" }, "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.4", - "@nomiclabs/hardhat-waffle": "^2.0.1", + "@nomicfoundation/hardhat-chai-matchers": "^1.0.5", + "@nomiclabs/hardhat-ethers": "^2.0.6", + "@nomiclabs/hardhat-etherscan": "^3.1.0", "@rari-capital/solmate": "^6.2.0", "@typechain/ethers-v5": "^10.0.0", "@typechain/hardhat": "^6.0.0", "@types/chai": "^4.3.0", - "@types/mocha": "^9.0.0", - "@types/node": "^17.0.8", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.11", "@typescript-eslint/eslint-plugin": "^5.9.1", "@typescript-eslint/parser": "^5.9.1", "chai": "^4.3.4", @@ -34,15 +37,14 @@ "eslint-plugin-n": "^15.2.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^6.0.0", - "ethereum-waffle": "^3.4.0", "hardhat-gas-reporter": "^1.0.7", "husky": ">=6", "lint-staged": ">=10", "prettier": "^2.5.1", - "prettier-plugin-solidity": "^1.0.0-beta.19", + "prettier-plugin-solidity": "^1.1.0", "scuffed-abi": "^1.0.4", "solhint": "^3.3.6", - "solidity-coverage": "^0.7.0", + "solidity-coverage": "^0.8.2", "ts-node": "^10.4.0", "typechain": "^8.0.0", "typescript": "^4.5.4" @@ -50,79 +52,57 @@ "resolutions": { "async": ">=2.6.4", "cross-fetch": ">=3.1.5", + "got": ">=11.8.5", "lodash": ">=4.17.21", "node-fetch": ">=2.6.7", "underscore": ">=1.12.1", - "yargs-parser": ">=5.0.1" + "undici": ">=5.8.2", + "yargs-parser": ">=5.0.1", + "minimist": ">=1.2.6", + "json-schema": ">=0.4.0", + "simple-get": ">=2.8.2", + "tar": ">=4.4.18", + "normalize-url": ">=4.5.1", + "ws": ">=5.2.3", + "path-parse": ">=1.0.7", + "elliptic": ">=6.5.4", + "minimatch": ">=3.0.5", + "flat": ">=5.0.1", + "json5": ">=1.0.2", + "cookiejar": ">=2.1.4" }, "scripts": { - "build": "hardhat compile --config ./hardhat.config.ts", + "build": "yarn clean; hardhat compile --config ./hardhat.config.ts; yarn show:headroom;", + "build:quick": "hardhat compile --config ./hardhat.config.ts; yarn show:headroom;", "build:ref": "hardhat compile --config ./hardhat-reference.config.ts", - "test": "hardhat test --config ./hardhat.config.ts", + "build:nospec": "yarn clean; NO_SPECIALIZER=true hardhat compile --config ./hardhat.config.ts; yarn show:headroom;", + "clean": "hardhat clean; hardhat clean --config ./hardhat-reference.config.ts; forge clean; rm -rf coverage coverage.json hh-cache hh-cache-ref", + "test": "yarn clean; hardhat test --config ./hardhat.config.ts", + "test:quick": "hardhat test --config ./hardhat.config.ts", + "test:nospec": "NO_SPECIALIZER=true hardhat test --config ./hardhat.config.ts", "test:ref": "REFERENCE=true hardhat test --config ./hardhat-reference.config.ts", - "profile": "REPORT_GAS=true hardhat test --config ./hardhat.config.ts", - "coverage": "hardhat coverage --config ./hardhat-coverage.config.ts --solcoverjs ./config/.solcover.js", + "profile": "yarn clean; REPORT_GAS=true hardhat test --config ./hardhat.config.ts; hardhat compare-reports", + "profile:show": "hardhat compare-reports", + "profile:nospec": "yarn clean; NO_SPECIALIZER=true REPORT_GAS=true hardhat test --config ./hardhat.config.ts", + "coverage": "yarn clean; hardhat coverage --config ./hardhat-coverage.config.ts --solcoverjs ./config/.solcover.js", "coverage:ref": "REFERENCE=true hardhat coverage --config ./hardhat-reference-coverage.config.ts --solcoverjs ./config/.solcover-reference.js", - "lint:check": "prettier --check **.sol && prettier --check **.js && prettier --check **.ts && hardhat compile --config ./hardhat.config.ts && npx solhint --config ./config/.solhint.json --ignore-path ./config/.solhintignore 'contracts/**/*.sol'", - "lint:fix": "prettier --write **.sol && prettier --write **.js && prettier --write **.ts", + "coverage:forge": "SEAPORT_COVERAGE=true forge coverage --report summary", + "generate:optimized-yul": "yarn build; jq -r '.output.contracts.\"contracts/Seaport.sol\".Seaport.irOptimized' artifacts/build-info/\"$(jq -r '.buildInfo[17:]' artifacts/contracts/Seaport.sol/Seaport.dbg.json)\" | cat > Seaport.yul", + "lint:check": "yarn lint:check:format && yarn lint:check:solhint && yarn lint:check:eslint", + "lint:check:format": "prettier --check **.{sol,js,ts}", + "lint:check:solhint": "yarn build && solhint --config ./config/.solhint.json --ignore-path ./config/.solhintignore contracts/**/*.sol", + "lint:check:eslint": "eslint . --ext js,ts", + "lint:fix": "yarn lint:fix:format && yarn lint:fix:eslint", + "lint:fix:format": "prettier --write **.{sol,js,ts}", + "lint:fix:eslint": "eslint --fix . --ext js,ts", + "show:headroom": "jq -r '.deployedBytecode' artifacts/contracts/Seaport.sol/Seaport.json | tr -d '\n' | wc -m | awk '{print 24577 - ($1 - 2)/2}'", "test:forge": "FOUNDRY_PROFILE=reference forge build; FOUNDRY_PROFILE=optimized forge build; FOUNDRY_PROFILE=test forge test -vvv", - "test:lite": "FOUNDRY_PROFILE=reference forge build; FOUNDRY_PROFILE=lite forge test -vvv", + "test:forge:lite": "FOUNDRY_PROFILE=reference forge build; FOUNDRY_PROFILE=lite forge test -vvv", "prepare": "husky install" }, "lint-staged": { "*.sol": "prettier --write", "*.js": "prettier --write", "*.ts": "prettier --write" - }, - "prettier": { - "overrides": [ - { - "files": "*.sol", - "options": { - "tabWidth": 4, - "printWidth": 80, - "bracketSpacing": true - } - } - ] - }, - "eslintConfig": { - "env": { - "browser": false, - "es2021": true, - "mocha": true, - "node": true - }, - "plugins": [ - "@typescript-eslint", - "import" - ], - "extends": [ - "standard", - "plugin:prettier/recommended", - "eslint:recommended", - "plugin:import/recommended", - "plugin:import/typescript" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 12 - }, - "rules": { - "node/no-unsupported-features/es-syntax": [ - "error", - { - "ignores": [ - "modules" - ] - } - ] - } - }, - "eslintIgnore": [ - "node_modules", - "artifacts", - "cache", - "coverage" - ] + } } diff --git a/reference/ReferenceConsideration.sol b/reference/ReferenceConsideration.sol index 35ea3a61c..91501bfed 100644 --- a/reference/ReferenceConsideration.sol +++ b/reference/ReferenceConsideration.sol @@ -1,42 +1,41 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { ConsiderationInterface -} from "contracts/interfaces/ConsiderationInterface.sol"; +} from "../contracts/interfaces/ConsiderationInterface.sol"; + +import { OrderType } from "../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { - OrderComponents, - BasicOrderParameters, - OrderParameters, - Order, AdvancedOrder, - OrderStatus, + BasicOrderParameters, CriteriaResolver, + Execution, Fulfillment, FulfillmentComponent, - Execution -} from "contracts/lib/ConsiderationStructs.sol"; + Order, + OrderComponents, + OrderParameters +} from "../contracts/lib/ConsiderationStructs.sol"; import { ReferenceOrderCombiner } from "./lib/ReferenceOrderCombiner.sol"; -import { OrderToExecute, AccumulatorStruct } from "./lib/ReferenceConsiderationStructs.sol"; +import { OrderToExecute } from "./lib/ReferenceConsiderationStructs.sol"; /** * @title ReferenceConsideration * @author 0age * @custom:coauthor d1ll0n * @custom:coauthor transmissions11 - * @custom:version rc-1.1 - * @notice Consideration is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. - * It minimizes external calls to the greatest extent possible and - * provides lightweight methods for common routes as well as more - * flexible methods for composing advanced orders or groups of orders. - * Each order contains an arbitrary number of items that may be spent - * (the "offer") along with an arbitrary number of items that must be - * received back by the indicated recipients (the "consideration"). + * @custom:version 1.2-reference + * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155 + * marketplace. It minimizes external calls to the greatest extent + * possible and provides lightweight methods for common routes as well + * as more flexible methods for composing advanced orders or groups of + * orders. Each order contains an arbitrary number of items that may be + * spent (the "offer") along with an arbitrary number of items that must + * be received back by the indicated recipients (the "consideration"). */ contract ReferenceConsideration is ConsiderationInterface, @@ -50,9 +49,20 @@ contract ReferenceConsideration is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceOrderCombiner(conduitController) - {} + constructor( + address conduitController + ) ReferenceOrderCombiner(conduitController) {} + + /** + * @notice Accept native token transfers during execution that may then be + * used to facilitate native token transfers, where any tokens that + * remain will be transferred to the caller. Native tokens are only + * acceptable mid-fulfillment (and not during basic fulfillment). + */ + receive() external payable { + // Ensure the reentrancy guard is currently set to accept native tokens. + _assertAcceptingNativeTokens(); + } /** * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by @@ -80,14 +90,45 @@ contract ReferenceConsideration is * @return fulfilled A boolean indicating whether the order has been * fulfilled. */ - function fulfillBasicOrder(BasicOrderParameters calldata parameters) - external - payable - override - notEntered - nonReentrant - returns (bool fulfilled) - { + function fulfillBasicOrder( + BasicOrderParameters calldata parameters + ) external payable override nonReentrant(false) returns (bool fulfilled) { + // Validate and fulfill the basic order. + fulfilled = _validateAndFulfillBasicOrder(parameters); + } + + /** + * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by + * supplying Ether (or other native tokens), ERC20 tokens, an ERC721 + * item, or an ERC1155 item as consideration. Six permutations are + * supported: Native token to ERC721, Native token to ERC1155, ERC20 + * to ERC721, ERC20 to ERC1155, ERC721 to ERC20, and ERC1155 to + * ERC20 (with native tokens supplied as msg.value). For an order to + * be eligible for fulfillment via this method, it must contain a + * single offer item (though that item may have a greater amount if + * the item is not an ERC721). An arbitrary number of "additional + * recipients" may also be supplied which will each receive native + * tokens or ERC20 items from the fulfiller as consideration. Refer + * to the documentation for a more comprehensive summary of how to + * utilize with this method and what orders are compatible with it. + * Note that this function costs less gas than `fulfillBasicOrder` + * due to the zero bytes in the function selector (0x00000000) which + * also results in earlier function dispatch. + * + * @param parameters Additional information on the fulfilled order. Note + * that the offerer and the fulfiller must first approve + * this contract (or their chosen conduit if indicated) + * before any tokens can be transferred. Also note that + * contract recipients of ERC1155 consideration items must + * implement `onERC1155Received` in order to receive those + * items. + * + * @return fulfilled A boolean indicating whether the order has been + * fulfilled. + */ + function fulfillBasicOrder_efficient_6GL6yc( + BasicOrderParameters calldata parameters + ) external payable override nonReentrant(false) returns (bool fulfilled) { // Validate and fulfill the basic order. fulfilled = _validateAndFulfillBasicOrder(parameters); } @@ -114,16 +155,17 @@ contract ReferenceConsideration is * @return fulfilled A boolean indicating whether the order has been * fulfilled. */ - function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey) + function fulfillOrder( + Order calldata order, + bytes32 fulfillerConduitKey + ) external payable override - notEntered - nonReentrant + nonReentrant(order.parameters.orderType == OrderType.CONTRACT) returns (bool fulfilled) { // Convert order to "advanced" order, then validate and fulfill it. - // prettier-ignore fulfilled = _validateAndFulfillAdvancedOrder( _convertOrderToAdvanced(order), new CriteriaResolver[](0), // No criteria resolvers supplied. @@ -181,8 +223,7 @@ contract ReferenceConsideration is external payable override - notEntered - nonReentrant + nonReentrant(advancedOrder.parameters.orderType == OrderType.CONTRACT) returns (bool fulfilled) { // Validate and fulfill the order. @@ -247,8 +288,7 @@ contract ReferenceConsideration is external payable override - notEntered - nonReentrant + nonReentrant(true) returns (bool[] memory availableOrders, Execution[] memory executions) { // Convert orders to "advanced" orders. @@ -348,8 +388,7 @@ contract ReferenceConsideration is external payable override - notEntered - nonReentrant + nonReentrant(true) returns (bool[] memory availableOrders, Execution[] memory executions) { // Convert Advanced Orders to Orders to Execute @@ -380,21 +419,23 @@ contract ReferenceConsideration is * criteria-based or partial filling of orders (though filling the * remainder of a partially-filled order is supported). * - * @param orders The orders to match. Note that both the offerer - * and fulfiller on each order must first approve - * this contract (or their proxy if indicated by - * the order) to transfer any relevant tokens on - * their behalf and each consideration recipient - * must implement `onERC1155Received` in order to - * receive ERC1155 tokens. - * @param fulfillments An array of elements allocating offer components - * to consideration components. Note that each - * consideration component must be fully met in - * order for the match operation to be valid. + * @param orders The orders to match. Note that both the offerer and + * fulfiller on each order must first approve this + * contract (or their conduit if indicated by the + * order) to transfer any relevant tokens on their + * behalf and each consideration recipient must + * implement `onERC1155Received` in order to receive + * ERC1155 tokens. + * @param fulfillments An array of elements allocating offer components to + * consideration components. Note that each + * consideration component must be fully met in order + * for the match operation to be valid. * - * @return executions An array of elements indicating the sequence of - * transfers performed as part of matching the given - * orders. + * @return executions An array of elements indicating the sequence of + * transfers performed as part of matching the given + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of this + * array. */ function matchOrders( Order[] calldata orders, @@ -403,8 +444,7 @@ contract ReferenceConsideration is external payable override - notEntered - nonReentrant + nonReentrant(true) returns (Execution[] memory executions) { // Convert to advanced, validate, and match orders using fulfillments. @@ -412,7 +452,8 @@ contract ReferenceConsideration is _matchAdvancedOrders( _convertOrdersToAdvanced(orders), new CriteriaResolver[](0), // No criteria resolvers supplied. - fulfillments + fulfillments, + msg.sender ); } @@ -447,21 +488,26 @@ contract ReferenceConsideration is * to consideration components. Note that each * consideration component must be fully met in * order for the match operation to be valid. + * @param recipient The intended recipient for all unspent offer + * item amounts, or the caller if the null address + * is supplied. * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the given - * orders. + * orders. Note that unspent offer item amounts or + * native tokens will not be reflected as part of this + * array. */ function matchAdvancedOrders( AdvancedOrder[] memory advancedOrders, CriteriaResolver[] calldata criteriaResolvers, - Fulfillment[] calldata fulfillments + Fulfillment[] calldata fulfillments, + address recipient ) external payable override - notEntered - nonReentrant + nonReentrant(true) returns (Execution[] memory executions) { // Validate and match the advanced orders using supplied fulfillments. @@ -469,7 +515,8 @@ contract ReferenceConsideration is _matchAdvancedOrders( advancedOrders, criteriaResolvers, - fulfillments + fulfillments, + recipient == address(0) ? msg.sender : recipient ); } @@ -482,12 +529,9 @@ contract ReferenceConsideration is * @return cancelled A boolean indicating whether the supplied orders have * been successfully cancelled. */ - function cancel(OrderComponents[] calldata orders) - external - override - notEntered - returns (bool cancelled) - { + function cancel( + OrderComponents[] calldata orders + ) external override notEntered returns (bool cancelled) { // Cancel the orders. cancelled = _cancel(orders); } @@ -503,12 +547,9 @@ contract ReferenceConsideration is * @return validated A boolean indicating whether the supplied orders have * been successfully validated. */ - function validate(Order[] calldata orders) - external - override - notEntered - returns (bool validated) - { + function validate( + Order[] calldata orders + ) external override notEntered returns (bool validated) { // Validate the orders. validated = _validate(orders); } @@ -537,15 +578,11 @@ contract ReferenceConsideration is * * @return orderHash the order hash. */ - function getOrderHash(OrderComponents calldata order) - external - view - override - returns (bytes32 orderHash) - { + function getOrderHash( + OrderComponents calldata order + ) external view override returns (bytes32 orderHash) { // Derive order hash by supplying order parameters along with the // counter. - // prettier-ignore orderHash = _deriveOrderHash( OrderParameters( order.offerer, @@ -567,7 +604,11 @@ contract ReferenceConsideration is /** * @notice Retrieve the status of a given order by hash, including whether * the order has been cancelled or validated and the fraction of the - * order that has been filled. + * order that has been filled. Since the _orderStatus[orderHash] + * does not get set for contract orders, getOrderStatus will always + * return (false, false, 0, 0) for those hashes. Note that this + * function is susceptible to view reentrancy and so should be used + * with care when calling from other contracts. * * @param orderHash The order hash in question. * @@ -581,7 +622,9 @@ contract ReferenceConsideration is * @return totalSize The total size of the order that is either filled or * unfilled (i.e. the "denominator"). */ - function getOrderStatus(bytes32 orderHash) + function getOrderStatus( + bytes32 orderHash + ) external view override @@ -603,12 +646,9 @@ contract ReferenceConsideration is * * @return counter The current counter. */ - function getCounter(address offerer) - external - view - override - returns (uint256 counter) - { + function getCounter( + address offerer + ) external view override returns (uint256 counter) { // Return the counter for the supplied offerer. counter = _getCounter(offerer); } @@ -634,6 +674,21 @@ contract ReferenceConsideration is return _information(); } + /** + * @dev Gets the contract offerer nonce for the specified contract offerer. + * Note that this function is susceptible to view reentrancy and so + * should be used with care when calling from other contracts. + * + * @param contractOfferer The contract offerer for which to get the nonce. + * + * @return nonce The contract offerer nonce. + */ + function getContractOffererNonce( + address contractOfferer + ) external view override returns (uint256 nonce) { + nonce = _contractNonces[contractOfferer]; + } + /** * @notice Retrieve the name of this contract. * diff --git a/reference/conduit/ReferenceConduit.sol b/reference/conduit/ReferenceConduit.sol index 118d26c1f..e38b19f74 100644 --- a/reference/conduit/ReferenceConduit.sol +++ b/reference/conduit/ReferenceConduit.sol @@ -1,20 +1,20 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { ConduitInterface } from "contracts/interfaces/ConduitInterface.sol"; +import { + ConduitInterface +} from "../../contracts/interfaces/ConduitInterface.sol"; -import { ConduitItemType } from "contracts/conduit/lib/ConduitEnums.sol"; +import { ConduitItemType } from "../../contracts/conduit/lib/ConduitEnums.sol"; -// prettier-ignore import { ReferenceTokenTransferrer } from "../lib/ReferenceTokenTransferrer.sol"; -// prettier-ignore import { - ConduitTransfer, - ConduitBatch1155Transfer -} from "contracts/conduit/lib/ConduitStructs.sol"; + ConduitBatch1155Transfer, + ConduitTransfer +} from "../../contracts/conduit/lib/ConduitStructs.sol"; /** * @title ReferenceConduit @@ -47,11 +47,10 @@ contract ReferenceConduit is ConduitInterface, ReferenceTokenTransferrer { * @return magicValue A magic value indicating that the transfers were * performed successfully. */ - function execute(ConduitTransfer[] calldata transfers) - external - override - returns (bytes4 magicValue) - { + function execute( + ConduitTransfer[] calldata transfers + ) external override returns (bytes4 magicValue) { + // Ensure that the caller is an open channel. if (!_channels[msg.sender]) { revert ChannelClosed(msg.sender); } @@ -79,6 +78,7 @@ contract ReferenceConduit is ConduitInterface, ReferenceTokenTransferrer { function executeBatch1155( ConduitBatch1155Transfer[] calldata batchTransfers ) external override returns (bytes4 magicValue) { + // Ensure that the caller is an open channel. if (!_channels[msg.sender]) { revert ChannelClosed(msg.sender); } @@ -116,6 +116,7 @@ contract ReferenceConduit is ConduitInterface, ReferenceTokenTransferrer { ConduitTransfer[] calldata standardTransfers, ConduitBatch1155Transfer[] calldata batchTransfers ) external override returns (bytes4 magicValue) { + // Ensure that the caller is an open channel. if (!_channels[msg.sender]) { revert ChannelClosed(msg.sender); } @@ -144,6 +145,7 @@ contract ReferenceConduit is ConduitInterface, ReferenceTokenTransferrer { * @param isOpen The status of the channel (either open or closed). */ function updateChannel(address channel, bool isOpen) external override { + // Ensure that the caller is the controller. if (msg.sender != _controller) { revert InvalidController(); } @@ -153,8 +155,10 @@ contract ReferenceConduit is ConduitInterface, ReferenceTokenTransferrer { revert ChannelStatusAlreadySet(channel, isOpen); } + // Update the channel status. _channels[channel] = isOpen; + // Emit an event indicating that the channel status was updated. emit ChannelUpdated(channel, isOpen); } @@ -197,7 +201,7 @@ contract ReferenceConduit is ConduitInterface, ReferenceTokenTransferrer { } else if (item.itemType == ConduitItemType.ERC721) { // Ensure that exactly one 721 item is being transferred. if (item.amount != 1) { - revert InvalidERC721TransferAmount(); + revert InvalidERC721TransferAmount(item.amount); } // Transfer ERC721 token. diff --git a/reference/conduit/ReferenceConduitController.sol b/reference/conduit/ReferenceConduitController.sol index 5dadf32a0..22dcc978c 100644 --- a/reference/conduit/ReferenceConduitController.sol +++ b/reference/conduit/ReferenceConduitController.sol @@ -1,14 +1,15 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; import { ReferenceConduit } from "./ReferenceConduit.sol"; -// prettier-ignore import { - ConduitControllerInterface -} from "contracts/interfaces/ConduitControllerInterface.sol"; + ConduitControllerInterface +} from "../../contracts/interfaces/ConduitControllerInterface.sol"; -import { ConduitInterface } from "contracts/interfaces/ConduitInterface.sol"; +import { + ConduitInterface +} from "../../contracts/interfaces/ConduitInterface.sol"; /** * @title ConduitController @@ -56,11 +57,10 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return conduit The address of the newly deployed conduit. */ - function createConduit(bytes32 conduitKey, address initialOwner) - external - override - returns (address conduit) - { + function createConduit( + bytes32 conduitKey, + address initialOwner + ) external override returns (address conduit) { // Ensure that an initial owner has been supplied. if (initialOwner == address(0)) { revert InvalidInitialOwner(); @@ -199,10 +199,10 @@ contract ReferenceConduitController is ConduitControllerInterface { * @param conduit The conduit for which to initiate ownership transfer. * @param newPotentialOwner The new potential owner of the conduit. */ - function transferOwnership(address conduit, address newPotentialOwner) - external - override - { + function transferOwnership( + address conduit, + address newPotentialOwner + ) external override { // Ensure the caller is the current owner of the conduit in question. _assertCallerIsConduitOwner(conduit); @@ -288,12 +288,9 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return owner The owner of the supplied conduit. */ - function ownerOf(address conduit) - external - view - override - returns (address owner) - { + function ownerOf( + address conduit + ) external view override returns (address owner) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -310,12 +307,9 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return conduitKey The conduit key used to deploy the supplied conduit. */ - function getKey(address conduit) - external - view - override - returns (bytes32 conduitKey) - { + function getKey( + address conduit + ) external view override returns (bytes32 conduitKey) { // Attempt to retrieve a conduit key for the conduit in question. conduitKey = _conduits[conduit].key; @@ -336,12 +330,9 @@ contract ReferenceConduitController is ConduitControllerInterface { * @return exists A boolean indicating whether the derived conduit has been * deployed or not. */ - function getConduit(bytes32 conduitKey) - external - view - override - returns (address conduit, bool exists) - { + function getConduit( + bytes32 conduitKey + ) external view override returns (address conduit, bool exists) { // Derive address from deployer, conduit key and creation code hash. conduit = address( uint160( @@ -372,12 +363,9 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return potentialOwner The potential owner, if any, for the conduit. */ - function getPotentialOwner(address conduit) - external - view - override - returns (address potentialOwner) - { + function getPotentialOwner( + address conduit + ) external view override returns (address potentialOwner) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -394,12 +382,10 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return isOpen The status of the channel on the given conduit. */ - function getChannelStatus(address conduit, address channel) - external - view - override - returns (bool isOpen) - { + function getChannelStatus( + address conduit, + address channel + ) external view override returns (bool isOpen) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -415,12 +401,9 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return totalChannels The total number of open channels for the conduit. */ - function getTotalChannels(address conduit) - external - view - override - returns (uint256 totalChannels) - { + function getTotalChannels( + address conduit + ) external view override returns (uint256 totalChannels) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -438,12 +421,10 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return channel The open channel, if any, at the specified channel index. */ - function getChannel(address conduit, uint256 channelIndex) - external - view - override - returns (address channel) - { + function getChannel( + address conduit, + uint256 channelIndex + ) external view override returns (address channel) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); @@ -468,12 +449,9 @@ contract ReferenceConduitController is ConduitControllerInterface { * * @return channels An array of open channels on the given conduit. */ - function getChannels(address conduit) - external - view - override - returns (address[] memory channels) - { + function getChannels( + address conduit + ) external view override returns (address[] memory channels) { // Ensure that the conduit in question exists. _assertConduitExists(conduit); diff --git a/reference/lib/ReferenceAmountDeriver.sol b/reference/lib/ReferenceAmountDeriver.sol index e0f6be5f9..fa17dd181 100644 --- a/reference/lib/ReferenceAmountDeriver.sol +++ b/reference/lib/ReferenceAmountDeriver.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { AmountDerivationErrors -} from "contracts/interfaces/AmountDerivationErrors.sol"; +} from "../../contracts/interfaces/AmountDerivationErrors.sol"; import { FractionData } from "./ReferenceConsiderationStructs.sol"; diff --git a/reference/lib/ReferenceAssertions.sol b/reference/lib/ReferenceAssertions.sol index bc0188425..f9a6bfd1a 100644 --- a/reference/lib/ReferenceAssertions.sol +++ b/reference/lib/ReferenceAssertions.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { OrderParameters } from "contracts/lib/ConsiderationStructs.sol"; +import { OrderParameters } from "../../contracts/lib/ConsiderationStructs.sol"; import { ReferenceGettersAndDerivers } from "./ReferenceGettersAndDerivers.sol"; -import { TokenTransferrerErrors } from "contracts/interfaces/TokenTransferrerErrors.sol"; +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; import { ReferenceCounterManager } from "./ReferenceCounterManager.sol"; -import "contracts/lib/ConsiderationConstants.sol"; - /** * @title Assertions * @author 0age @@ -30,9 +30,9 @@ contract ReferenceAssertions is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceGettersAndDerivers(conduitController) - {} + constructor( + address conduitController + ) ReferenceGettersAndDerivers(conduitController) {} /** * @dev Internal view function to to ensure that the supplied consideration @@ -95,23 +95,27 @@ contract ReferenceAssertions is /** * @dev Internal pure function to validate calldata offsets for dynamic - * types in BasicOrderParameters. This ensures that functions using the - * calldata object normally will be using the same data as optimized - * functions. Note that no parameters are supplied as all basic order - * functions use the same calldata encoding. + * types in BasicOrderParameters and other parameters. This ensures + * that functions using the calldata object normally will be using the + * same data as the assembly functions and that values that are bound + * to a given range are within that range. Note that no parameters are + * supplied as all basic order functions use the same calldata + * encoding. */ - function _assertValidBasicOrderParameterOffsets() internal pure { + function _assertValidBasicOrderParameters() internal pure { /* * Checks: * 1. Order parameters struct offset == 0x20 * 2. Additional recipients arr offset == 0x200 * 3. Signature offset == 0x240 + (recipients.length * 0x40) + * 4. BasicOrderType between 0 and 23 (i.e. < 24) */ // Declare a boolean designating basic order parameter offset validity. bool validOffsets = (abi.decode(msg.data[4:36], (uint256)) == 32 && abi.decode(msg.data[548:580], (uint256)) == 576 && abi.decode(msg.data[580:612], (uint256)) == - 608 + 64 * abi.decode(msg.data[612:644], (uint256))); + 608 + 64 * abi.decode(msg.data[612:644], (uint256))) && + abi.decode(msg.data[292:324], (uint256)) < 24; // Revert with an error if basic order parameter offsets are invalid. if (!validOffsets) { diff --git a/reference/lib/ReferenceBasicOrderFulfiller.sol b/reference/lib/ReferenceBasicOrderFulfiller.sol index 995530ea0..50222a7d3 100644 --- a/reference/lib/ReferenceBasicOrderFulfiller.sol +++ b/reference/lib/ReferenceBasicOrderFulfiller.sol @@ -1,25 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { - OrderType, + BasicOrderRouteType, BasicOrderType, ItemType, - BasicOrderRouteType -} from "contracts/lib/ConsiderationEnums.sol"; + OrderType +} from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { AdditionalRecipient, BasicOrderParameters, - OfferItem, ConsiderationItem, - SpentItem, - ReceivedItem -} from "contracts/lib/ConsiderationStructs.sol"; + OfferItem, + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; -// prettier-ignore import { AccumulatorStruct, BasicFulfillmentHashes, @@ -28,8 +25,6 @@ import { import { ReferenceOrderValidator } from "./ReferenceOrderValidator.sol"; -import "contracts/lib/ConsiderationConstants.sol"; - /** * @title BasicOrderFulfiller * @author 0age @@ -50,9 +45,9 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceOrderValidator(conduitController) - { + constructor( + address conduitController + ) ReferenceOrderValidator(conduitController) { createMappings(); } @@ -341,7 +336,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { } // Derive & validate order using parameters and update order status. - _prepareBasicFulfillment( + bytes32 orderHash = _prepareBasicFulfillment( parameters, orderType, receivedItemType, @@ -350,9 +345,6 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { offeredItemType ); - // Read offerer from calldata and place on the stack. - address payable offerer = parameters.offerer; - // Determine conduitKey argument used by transfer functions. bytes32 conduitKey; if ( @@ -390,7 +382,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC721 to caller using offerer's conduit if applicable. _transferERC721( parameters.offerToken, - offerer, + parameters.offerer, msg.sender, parameters.offerIdentifier, parameters.offerAmount, @@ -404,7 +396,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC1155 to caller using offerer's conduit if applicable. _transferERC1155( parameters.offerToken, - offerer, + parameters.offerer, msg.sender, parameters.offerIdentifier, parameters.offerAmount, @@ -418,7 +410,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC721 to caller using offerer's conduit if applicable. _transferERC721( parameters.offerToken, - offerer, + parameters.offerer, msg.sender, parameters.offerIdentifier, parameters.offerAmount, @@ -429,7 +421,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC20 tokens to all recipients and wrap up. _transferERC20AndFinalize( msg.sender, - offerer, + parameters.offerer, parameters.considerationToken, parameters.considerationAmount, parameters, @@ -440,7 +432,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC1155 to caller using offerer's conduit if applicable. _transferERC1155( parameters.offerToken, - offerer, + parameters.offerer, msg.sender, parameters.offerIdentifier, parameters.offerAmount, @@ -451,7 +443,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC20 tokens to all recipients and wrap up. _transferERC20AndFinalize( msg.sender, - offerer, + parameters.offerer, parameters.considerationToken, parameters.considerationAmount, parameters, @@ -463,7 +455,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { _transferERC721( parameters.considerationToken, msg.sender, - offerer, + parameters.offerer, parameters.considerationIdentifier, parameters.considerationAmount, conduitKey, @@ -472,7 +464,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC20 tokens to all recipients and wrap up. _transferERC20AndFinalize( - offerer, + parameters.offerer, msg.sender, parameters.offerToken, parameters.offerAmount, @@ -487,7 +479,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { _transferERC1155( parameters.considerationToken, msg.sender, - offerer, + parameters.offerer, parameters.considerationIdentifier, parameters.considerationAmount, conduitKey, @@ -496,7 +488,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Transfer ERC20 tokens to all recipients and wrap up. _transferERC20AndFinalize( - offerer, + parameters.offerer, msg.sender, parameters.offerToken, parameters.offerAmount, @@ -509,6 +501,15 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // Trigger any remaining accumulated transfers via call to the conduit. _triggerIfArmed(accumulatorStruct); + // Determine whether order is restricted and, if so, that it is valid. + _assertRestrictedBasicOrderValidity( + orderHash, + orderType, + parameters, + offeredItemType, + receivedItemType + ); + return true; } @@ -565,6 +566,8 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { * consideration item on the order. * @param offeredItemType The item type of the offered item on * the order. + * + * @return orderHash The calculated order hash. */ function _prepareBasicFulfillment( BasicOrderParameters calldata parameters, @@ -573,14 +576,14 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { ItemType additionalRecipientsItemType, address additionalRecipientsToken, ItemType offeredItemType - ) internal { + ) internal returns (bytes32 orderHash) { // Ensure current timestamp falls between order start time and end time. _verifyTime(parameters.startTime, parameters.endTime, true); // Verify that calldata offsets for all dynamic types were produced by // default encoding. This is only required on the optimized contract, // but is included here to maintain parity. - _assertValidBasicOrderParameterOffsets(); + _assertValidBasicOrderParameters(); // Ensure supplied consideration array length is not less than original. _assertConsiderationLengthIsNotLessThanOriginalConsiderationLength( @@ -663,7 +666,7 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { */ for ( uint256 recipientCount = 0; - recipientCount < parameters.additionalRecipients.length; + recipientCount < parameters.totalOriginalAdditionalRecipients; ++recipientCount ) { // Get the next additionalRecipient. @@ -683,15 +686,6 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { // OrderFulfilled ReceivedItem[]. consideration[recipientCount + 1] = additionalReceivedItem; - // Skip hashing items not contained in the - // Original Recipients. - if ( - recipientCount >= - parameters.totalOriginalAdditionalRecipients - ) { - continue; - } - // Create a new consideration item for each additional // recipient. additionalRecipientItem = ConsiderationItem( @@ -820,14 +814,6 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { consideration ); } - // Determine whether order is restricted and, if so, that it is valid. - _assertRestrictedBasicOrderValidity( - hashes.orderHash, - parameters.zoneHash, - orderType, - parameters.offerer, - parameters.zone - ); // Verify and update the status of the derived order. _validateBasicOrderAndUpdateStatus( @@ -835,6 +821,9 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { parameters.offerer, parameters.signature ); + + // Return the derived order hash. + return hashes.orderHash; } /** @@ -850,8 +839,8 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { uint256 amount, BasicOrderParameters calldata parameters ) internal { - // Put ether value supplied by the caller on the stack. - uint256 etherRemaining = msg.value; + // Put native token value supplied by the caller on the stack. + uint256 nativeTokensRemaining = msg.value; // Iterate over each additional recipient. for (uint256 i = 0; i < parameters.additionalRecipients.length; ++i) { @@ -860,36 +849,39 @@ contract ReferenceBasicOrderFulfiller is ReferenceOrderValidator { parameters.additionalRecipients[i] ); - // Read ether amount to transfer to recipient and place on stack. + // Read native token amount to transfer to recipient & put on stack. uint256 additionalRecipientAmount = additionalRecipient.amount; - // Ensure that sufficient Ether is available. - if (additionalRecipientAmount > etherRemaining) { - revert InsufficientEtherSupplied(); + // Ensure that sufficient native tokens are available. + if (additionalRecipientAmount > nativeTokensRemaining) { + revert InsufficientNativeTokensSupplied(); } - // Transfer Ether to the additional recipient. - _transferEth( + // Transfer native token to the additional recipient. + _transferNativeTokens( additionalRecipient.recipient, additionalRecipientAmount ); - // Reduce ether value available. - etherRemaining -= additionalRecipientAmount; + // Reduce native token value available. + nativeTokensRemaining -= additionalRecipientAmount; } - // Ensure that sufficient Ether is still available. - if (amount > etherRemaining) { - revert InsufficientEtherSupplied(); + // Ensure that sufficient native token is still available. + if (amount > nativeTokensRemaining) { + revert InsufficientNativeTokensSupplied(); } - // Transfer Ether to the offerer. - _transferEth(parameters.offerer, amount); + // Transfer native token to the offerer. + _transferNativeTokens(parameters.offerer, amount); - // If any Ether remains after transfers, return it to the caller. - if (etherRemaining > amount) { - // Transfer remaining Ether to the caller. - _transferEth(payable(msg.sender), etherRemaining - amount); + // If any native token remains after transfers, return it to the caller. + if (nativeTokensRemaining > amount) { + // Transfer remaining native token to the caller. + _transferNativeTokens( + payable(msg.sender), + nativeTokensRemaining - amount + ); } } diff --git a/reference/lib/ReferenceConsiderationBase.sol b/reference/lib/ReferenceConsiderationBase.sol index aeef28021..4357df44b 100644 --- a/reference/lib/ReferenceConsiderationBase.sol +++ b/reference/lib/ReferenceConsiderationBase.sol @@ -1,19 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { ConduitControllerInterface -} from "contracts/interfaces/ConduitControllerInterface.sol"; +} from "../../contracts/interfaces/ConduitControllerInterface.sol"; -// prettier-ignore import { ConsiderationEventsAndErrors -} from "contracts/interfaces/ConsiderationEventsAndErrors.sol"; +} from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; -import { OrderStatus } from "contracts/lib/ConsiderationStructs.sol"; - -import { ReentrancyErrors } from "contracts/interfaces/ReentrancyErrors.sol"; +import { + ReentrancyErrors +} from "../../contracts/interfaces/ReentrancyErrors.sol"; /** * @title ConsiderationBase @@ -27,7 +25,7 @@ contract ReferenceConsiderationBase is { // Declare constants for name, version, and reentrancy sentinel values. string internal constant _NAME = "Consideration"; - string internal constant _VERSION = "rc.1.1"; + string internal constant _VERSION = "1.2-reference"; uint256 internal constant _NOT_ENTERED = 1; uint256 internal constant _ENTERED = 2; @@ -38,6 +36,7 @@ contract ReferenceConsiderationBase is bytes32 internal immutable _OFFER_ITEM_TYPEHASH; bytes32 internal immutable _CONSIDERATION_ITEM_TYPEHASH; bytes32 internal immutable _ORDER_TYPEHASH; + bytes32 internal immutable _BULK_ORDER_TYPEHASH; uint256 internal immutable _CHAIN_ID; bytes32 internal immutable _DOMAIN_SEPARATOR; @@ -47,6 +46,9 @@ contract ReferenceConsiderationBase is // Cache the conduit creation code hash used by the conduit controller. bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH; + // Map bulk order tree height to its respective EIP-712 typehash. + mapping(uint256 => bytes32) internal _bulkOrderTypehashes; + /** * @dev Derive and set hashes, reference chainId, and associated domain * separator during deployment. @@ -65,22 +67,97 @@ contract ReferenceConsiderationBase is _OFFER_ITEM_TYPEHASH, _CONSIDERATION_ITEM_TYPEHASH, _ORDER_TYPEHASH, + _BULK_ORDER_TYPEHASH, _DOMAIN_SEPARATOR ) = _deriveTypehashes(); // Store the current chainId and derive the current domain separator. _CHAIN_ID = block.chainid; - // Set the supplied conduit controller to temp. - ConduitControllerInterface tempConduitController = ConduitControllerInterface( - conduitController - ); + // Set supplied conduit controller to an in-memory controller interface. + ConduitControllerInterface tempController = ConduitControllerInterface( + conduitController + ); - _CONDUIT_CONTROLLER = tempConduitController; + // Assign the in-memory interface as an immutable. + _CONDUIT_CONTROLLER = tempController; // Retrieve the conduit creation code hash from the supplied controller. (_CONDUIT_CREATION_CODE_HASH, ) = ( - tempConduitController.getConduitCodeHashes() + tempController.getConduitCodeHashes() + ); + + _bulkOrderTypehashes[1] = bytes32( + 0x3ca2711d29384747a8f61d60aad3c450405f7aaff5613541dee28df2d6986d32 + ); + _bulkOrderTypehashes[2] = bytes32( + 0xbf8e29b89f29ed9b529c154a63038ffca562f8d7cd1e2545dda53a1b582dde30 + ); + _bulkOrderTypehashes[3] = bytes32( + 0x53c6f6856e13104584dd0797ca2b2779202dc2597c6066a42e0d8fe990b0024d + ); + _bulkOrderTypehashes[4] = bytes32( + 0xa02eb7ff164c884e5e2c336dc85f81c6a93329d8e9adf214b32729b894de2af1 + ); + _bulkOrderTypehashes[5] = bytes32( + 0x39c9d33c18e050dda0aeb9a8086fb16fc12d5d64536780e1da7405a800b0b9f6 + ); + _bulkOrderTypehashes[6] = bytes32( + 0x1c19f71958cdd8f081b4c31f7caf5c010b29d12950be2fa1c95070dc47e30b55 + ); + _bulkOrderTypehashes[7] = bytes32( + 0xca74fab2fece9a1d58234a274220ad05ca096a92ef6a1ca1750b9d90c948955c + ); + _bulkOrderTypehashes[8] = bytes32( + 0x7ff98d9d4e55d876c5cfac10b43c04039522f3ddfb0ea9bfe70c68cfb5c7cc14 + ); + _bulkOrderTypehashes[9] = bytes32( + 0xbed7be92d41c56f9e59ac7a6272185299b815ddfabc3f25deb51fe55fe2f9e8a + ); + _bulkOrderTypehashes[10] = bytes32( + 0xd1d97d1ef5eaa37a4ee5fbf234e6f6d64eb511eb562221cd7edfbdde0848da05 + ); + _bulkOrderTypehashes[11] = bytes32( + 0x896c3f349c4da741c19b37fec49ed2e44d738e775a21d9c9860a69d67a3dae53 + ); + _bulkOrderTypehashes[12] = bytes32( + 0xbb98d87cc12922b83759626c5f07d72266da9702d19ffad6a514c73a89002f5f + ); + _bulkOrderTypehashes[13] = bytes32( + 0xe6ae19322608dd1f8a8d56aab48ed9c28be489b689f4b6c91268563efc85f20e + ); + _bulkOrderTypehashes[14] = bytes32( + 0x6b5b04cbae4fcb1a9d78e7b2dfc51a36933d023cf6e347e03d517b472a852590 + ); + _bulkOrderTypehashes[15] = bytes32( + 0xd1eb68309202b7106b891e109739dbbd334a1817fe5d6202c939e75cf5e35ca9 + ); + _bulkOrderTypehashes[16] = bytes32( + 0x1da3eed3ecef6ebaa6e5023c057ec2c75150693fd0dac5c90f4a142f9879fde8 + ); + _bulkOrderTypehashes[17] = bytes32( + 0xeee9a1392aa395c7002308119a58f2582777a75e54e0c1d5d5437bd2e8bf6222 + ); + _bulkOrderTypehashes[18] = bytes32( + 0xc3939feff011e53ab8c35ca3370aad54c5df1fc2938cd62543174fa6e7d85877 + ); + _bulkOrderTypehashes[19] = bytes32( + 0x0efca7572ac20f5ae84db0e2940674f7eca0a4726fa1060ffc2d18cef54b203d + ); + _bulkOrderTypehashes[20] = bytes32( + 0x5a4f867d3d458dabecad65f6201ceeaba0096df2d0c491cc32e6ea4e64350017 + ); + _bulkOrderTypehashes[21] = bytes32( + 0x80987079d291feebf21c2230e69add0f283cee0b8be492ca8050b4185a2ff719 + ); + _bulkOrderTypehashes[22] = bytes32( + 0x3bd8cff538aba49a9c374c806d277181e9651624b3e31111bc0624574f8bca1d + ); + _bulkOrderTypehashes[23] = bytes32( + 0x5d6a3f098a0bc373f808c619b1bb4028208721b3c4f8d6bc8a874d659814eb76 + ); + _bulkOrderTypehashes[24] = bytes32( + 0x1d51df90cba8de7637ca3e8fe1e3511d1dc2f23487d05dbdecb781860c21ac1c ); } @@ -118,7 +195,6 @@ contract ReferenceConsiderationBase is bytes32 _nameHash, bytes32 _versionHash ) internal view virtual returns (bytes32) { - // prettier-ignore return keccak256( abi.encode( _eip712DomainTypeHash, @@ -143,6 +219,7 @@ contract ReferenceConsiderationBase is * @return considerationItemTypehash The EIP-712 typehash for * ConsiderationItem types. * @return orderTypehash The EIP-712 typehash for Order types. + * @return bulkOrderTypeHash * @return domainSeparator The domain separator. */ function _deriveTypehashes() @@ -155,6 +232,7 @@ contract ReferenceConsiderationBase is bytes32 offerItemTypehash, bytes32 considerationItemTypehash, bytes32 orderTypehash, + bytes32 bulkOrderTypeHash, bytes32 domainSeparator ) { @@ -165,7 +243,6 @@ contract ReferenceConsiderationBase is versionHash = keccak256(bytes(_VERSION)); // Construct the OfferItem type string. - // prettier-ignore bytes memory offerItemTypeString = abi.encodePacked( "OfferItem(", "uint8 itemType,", @@ -177,7 +254,6 @@ contract ReferenceConsiderationBase is ); // Construct the ConsiderationItem type string. - // prettier-ignore bytes memory considerationItemTypeString = abi.encodePacked( "ConsiderationItem(", "uint8 itemType,", @@ -190,7 +266,6 @@ contract ReferenceConsiderationBase is ); // Construct the OrderComponents type string, not including the above. - // prettier-ignore bytes memory orderComponentsPartialTypeString = abi.encodePacked( "OrderComponents(", "address offerer,", @@ -208,7 +283,6 @@ contract ReferenceConsiderationBase is ); // Construct the primary EIP-712 domain type string. - // prettier-ignore eip712DomainTypehash = keccak256( abi.encodePacked( "EIP712Domain(", @@ -235,6 +309,24 @@ contract ReferenceConsiderationBase is ) ); + // Encode the type string for the BulkOrder struct. + bytes memory bulkOrderPartialTypeString = abi.encodePacked( + "BulkOrder(OrderComponents[2][2][2][2][2][2][2] tree)" + ); + + // Generate the keccak256 hash of the concatenated type strings for the + // BulkOrder, considerationItem, offerItem, and orderComponents. + bulkOrderTypeHash = keccak256( + abi.encodePacked( + bulkOrderPartialTypeString, + considerationItemTypeString, + offerItemTypeString, + orderComponentsPartialTypeString + ) + ); + + // Derive the initial domain separator using the domain typehash, the + // name hash, and the version hash. domainSeparator = _deriveInitialDomainSeparator( eip712DomainTypehash, nameHash, diff --git a/reference/lib/ReferenceConsiderationStructs.sol b/reference/lib/ReferenceConsiderationStructs.sol index c2ea41296..adb4e2a19 100644 --- a/reference/lib/ReferenceConsiderationStructs.sol +++ b/reference/lib/ReferenceConsiderationStructs.sol @@ -1,15 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { - OrderType, - ItemType -} from "contracts/lib/ConsiderationEnums.sol"; + ItemType, + OrderType +} from "../../contracts/lib/ConsiderationEnums.sol"; -import { SpentItem, ReceivedItem } from "contracts/lib/ConsiderationStructs.sol"; +import { + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConduitTransfer } from "contracts/conduit/lib/ConduitStructs.sol"; +import { + ConduitTransfer +} from "../../contracts/conduit/lib/ConduitStructs.sol"; // This file should only be used by the Reference Implementation @@ -20,6 +24,7 @@ struct ConsiderationItemIndicesAndValidity { uint256 orderIndex; uint256 itemIndex; bool invalidFulfillment; + bool missingItemAmount; } /** @@ -59,6 +64,8 @@ struct OrderToExecute { ReceivedItem[] receivedItems; // Consideration bytes32 conduitKey; uint120 numerator; + uint256[] spentItemOriginalAmounts; + uint256[] receivedItemOriginalAmounts; } /** diff --git a/reference/lib/ReferenceCounterManager.sol b/reference/lib/ReferenceCounterManager.sol index 500124f7e..32608da42 100644 --- a/reference/lib/ReferenceCounterManager.sol +++ b/reference/lib/ReferenceCounterManager.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore import { ConsiderationEventsAndErrors -} from "contracts/interfaces/ConsiderationEventsAndErrors.sol"; +} from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; import { ReferenceReentrancyGuard } from "./ReferenceReentrancyGuard.sol"; @@ -22,19 +21,28 @@ contract ReferenceCounterManager is mapping(address => uint256) private _counters; /** - * @dev Internal function to cancel all orders from a given offerer with a - * given zone in bulk by incrementing a counter. Note that only the - * offerer may increment the counter. + * @dev Internal function to cancel all orders from a given offerer in bulk + * by incrementing a counter. Note that only the offerer may increment + * the counter. Note that the counter is incremented by a large, + * quasi-random interval, which makes it infeasible to "activate" + * signed orders by incrementing the counter. This activation + * functionality can be achieved instead with restricted orders or + * contract orders. * * @return newCounter The new counter. */ - function _incrementCounter() - internal - notEntered - returns (uint256 newCounter) - { + function _incrementCounter() internal returns (uint256 newCounter) { + // Use second half of the previous block hash as a quasi-random number. + uint256 quasiRandomNumber = uint256(blockhash(block.number - 1)) >> 128; + + // Retrieve the original counter value. + uint256 originalCounter = _counters[msg.sender]; + // Increment current counter for the supplied offerer. - newCounter = ++_counters[msg.sender]; + newCounter = quasiRandomNumber + originalCounter; + + // Update the counter with the new value. + _counters[msg.sender] = newCounter; // Emit an event containing the new counter. emit CounterIncremented(newCounter, msg.sender); @@ -48,11 +56,9 @@ contract ReferenceCounterManager is * * @return currentCounter The current counter. */ - function _getCounter(address offerer) - internal - view - returns (uint256 currentCounter) - { + function _getCounter( + address offerer + ) internal view returns (uint256 currentCounter) { // Return the counter for the supplied offerer. currentCounter = _counters[offerer]; } diff --git a/reference/lib/ReferenceCriteriaResolution.sol b/reference/lib/ReferenceCriteriaResolution.sol index 15c6e0d5b..4ad5e05bd 100644 --- a/reference/lib/ReferenceCriteriaResolution.sol +++ b/reference/lib/ReferenceCriteriaResolution.sol @@ -1,27 +1,23 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { ItemType, Side } from "contracts/lib/ConsiderationEnums.sol"; +import { ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, - ConsiderationItem, - OrderParameters, AdvancedOrder, + ConsiderationItem, CriteriaResolver, - SpentItem, - ReceivedItem -} from "contracts/lib/ConsiderationStructs.sol"; + OfferItem, + OrderParameters, + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; import { OrderToExecute } from "./ReferenceConsiderationStructs.sol"; -import "contracts/lib/ConsiderationConstants.sol"; - -// prettier-ignore import { CriteriaResolutionErrors -} from "contracts/interfaces/CriteriaResolutionErrors.sol"; +} from "../../contracts/interfaces/CriteriaResolutionErrors.sol"; /** * @title CriteriaResolution @@ -61,7 +57,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { // Ensure that the order index is in range. if (orderIndex >= ordersToExecute.length) { - revert OrderCriteriaResolverOutOfRange(); + revert OrderCriteriaResolverOutOfRange(criteriaResolver.side); } // Skip criteria resolution for order if not fulfilled. @@ -139,7 +135,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { revert CriteriaNotEnabledForItem(); } - // If criteria is not 0 (i.e. a collection-wide offer)... + // If criteria is not 0 (i.e. a collection-wide criteria item)... if (identifierOrCriteria != uint256(0)) { // Verify identifier inclusion in criteria root using proof. _verifyProof( @@ -147,6 +143,9 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { identifierOrCriteria, criteriaResolver.criteriaProof ); + } else if (criteriaResolver.criteriaProof.length != 0) { + // Revert if a proof is supplied for a collection-wide item. + revert InvalidProof(); } } @@ -172,7 +171,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { if ( _isItemWithCriteria(orderToExecute.spentItems[j].itemType) ) { - revert UnresolvedOfferCriteria(); + revert UnresolvedOfferCriteria(i, j); } } @@ -187,7 +186,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { orderToExecute.receivedItems[j].itemType ) ) { - revert UnresolvedConsiderationCriteria(); + revert UnresolvedConsiderationCriteria(i, j); } } } @@ -226,7 +225,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { uint256 orderIndex = criteriaResolver.orderIndex; if (orderIndex != 0) { - revert OrderCriteriaResolverOutOfRange(); + revert OrderCriteriaResolverOutOfRange(criteriaResolver.side); } // Read component index from memory and place it on the stack. @@ -303,6 +302,9 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { identifierOrCriteria, criteriaResolver.criteriaProof ); + } else if (criteriaResolver.criteriaProof.length != 0) { + // Revert if a proof is supplied for a collection-wide item. + revert InvalidProof(); } } @@ -319,7 +321,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { advancedOrder.parameters.consideration[i].itemType ) ) { - revert UnresolvedConsiderationCriteria(); + revert UnresolvedConsiderationCriteria(0, i); } } @@ -332,7 +334,7 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { if ( _isItemWithCriteria(advancedOrder.parameters.offer[i].itemType) ) { - revert UnresolvedOfferCriteria(); + revert UnresolvedOfferCriteria(0, i); } } } @@ -348,11 +350,9 @@ contract ReferenceCriteriaResolution is CriteriaResolutionErrors { * @return withCriteria A boolean indicating that the item type in question * represents a criteria-based item. */ - function _isItemWithCriteria(ItemType itemType) - internal - pure - returns (bool withCriteria) - { + function _isItemWithCriteria( + ItemType itemType + ) internal pure returns (bool withCriteria) { // ERC721WithCriteria is item type 4. ERC1155WithCriteria is item type // 5. withCriteria = uint256(itemType) > 3; diff --git a/reference/lib/ReferenceExecutor.sol b/reference/lib/ReferenceExecutor.sol index 41680aa07..02e58fa30 100644 --- a/reference/lib/ReferenceExecutor.sol +++ b/reference/lib/ReferenceExecutor.sol @@ -1,29 +1,24 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -// prettier-ignore -import { - ERC20Interface, - ERC721Interface, - ERC1155Interface -} from "contracts/interfaces/AbridgedTokenInterfaces.sol"; - -import { ConduitItemType } from "contracts/conduit/lib/ConduitEnums.sol"; +import { ConduitItemType } from "../../contracts/conduit/lib/ConduitEnums.sol"; -import { ConduitInterface } from "contracts/interfaces/ConduitInterface.sol"; +import { + ConduitInterface +} from "../../contracts/interfaces/ConduitInterface.sol"; -import { ConduitTransfer, ConduitBatch1155Transfer } from "contracts/conduit/lib/ConduitStructs.sol"; +import { + ConduitTransfer +} from "../../contracts/conduit/lib/ConduitStructs.sol"; -import { ItemType } from "contracts/lib/ConsiderationEnums.sol"; +import { ItemType } from "../../contracts/lib/ConsiderationEnums.sol"; -import { ReceivedItem } from "contracts/lib/ConsiderationStructs.sol"; +import { ReceivedItem } from "../../contracts/lib/ConsiderationStructs.sol"; import { ReferenceVerifiers } from "./ReferenceVerifiers.sol"; import { ReferenceTokenTransferrer } from "./ReferenceTokenTransferrer.sol"; -import "contracts/lib/ConsiderationConstants.sol"; - import { AccumulatorStruct } from "./ReferenceConsiderationStructs.sol"; /** @@ -41,9 +36,9 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceVerifiers(conduitController) - {} + constructor( + address conduitController + ) ReferenceVerifiers(conduitController) {} /** * @dev Internal function to transfer a given item. @@ -74,7 +69,7 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { } // Transfer the native tokens to the recipient. - _transferEth(item.recipient, item.amount); + _transferNativeTokens(item.recipient, item.amount); } else if (item.itemType == ItemType.ERC20) { // Ensure that no identifier is supplied. if (item.identifier != 0) { @@ -117,12 +112,17 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { /** * @dev Internal function to transfer Ether or other native tokens to a - * given recipient. + * given recipient. Note that this reference implementation deviates + * from the primary contract, which "bubbles up" revert data when + * present (the reference contract always throws a generic error). * * @param to The recipient of the transfer. * @param amount The amount to transfer. */ - function _transferEth(address payable to, uint256 amount) internal { + function _transferNativeTokens( + address payable to, + uint256 amount + ) internal { // Ensure that the supplied amount is non-zero. _assertNonZeroAmount(amount); @@ -132,7 +132,7 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { // If the call fails... if (!success) { // Revert with a generic error message. - revert EtherTransferGenericFailure(to, amount); + revert NativeTokenTransferGenericFailure(to, amount); } } @@ -221,7 +221,7 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { if (conduitKey == bytes32(0)) { // Ensure that exactly one 721 item is being transferred. if (amount != 1) { - revert InvalidERC721TransferAmount(); + revert InvalidERC721TransferAmount(amount); } // Perform transfer via the token contract directly. @@ -325,9 +325,9 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { * @param accumulatorStruct A struct containing conduit transfer data * and its corresponding conduitKey. */ - function _triggerIfArmed(AccumulatorStruct memory accumulatorStruct) - internal - { + function _triggerIfArmed( + AccumulatorStruct memory accumulatorStruct + ) internal { // Exit if the accumulator is not "armed". if (accumulatorStruct.transfers.length == 0) { return; @@ -442,11 +442,9 @@ contract ReferenceExecutor is ReferenceVerifiers, ReferenceTokenTransferrer { * @return conduit The address of the conduit associated with the given * conduit key. */ - function _getConduit(bytes32 conduitKey) - internal - view - returns (address conduit) - { + function _getConduit( + bytes32 conduitKey + ) internal view returns (address conduit) { // Derive the address of the conduit using the conduit key. conduit = _deriveConduit(conduitKey); diff --git a/reference/lib/ReferenceFulfillmentApplier.sol b/reference/lib/ReferenceFulfillmentApplier.sol index e2c105cac..9cd14006a 100644 --- a/reference/lib/ReferenceFulfillmentApplier.sol +++ b/reference/lib/ReferenceFulfillmentApplier.sol @@ -1,28 +1,27 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { ItemType, Side } from "contracts/lib/ConsiderationEnums.sol"; +import { ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, - ConsiderationItem, - ReceivedItem, - OrderParameters, - AdvancedOrder, Execution, FulfillmentComponent, + ReceivedItem, SpentItem -} from "contracts/lib/ConsiderationStructs.sol"; +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationItemIndicesAndValidity, OrderToExecute } from "./ReferenceConsiderationStructs.sol"; - -import "contracts/lib/ConsiderationConstants.sol"; +import { + ConsiderationItemIndicesAndValidity, + OrderToExecute +} from "./ReferenceConsiderationStructs.sol"; -// prettier-ignore import { FulfillmentApplicationErrors -} from "contracts/interfaces/FulfillmentApplicationErrors.sol"; +} from "../../contracts/interfaces/FulfillmentApplicationErrors.sol"; + +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; /** * @title FulfillmentApplier @@ -32,7 +31,10 @@ import { * consideration items) as well as fulfilling available orders (where * order items and consideration items are independently aggregated). */ -contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { +contract ReferenceFulfillmentApplier is + FulfillmentApplicationErrors, + TokenTransferrerErrors +{ /** * @dev Internal pure function to match offer items to consideration items * on a group of orders via a supplied fulfillment. @@ -45,13 +47,17 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { * Note that each consideration amount must * be zero in order for the match operation * to be valid. + * @param fulfillmentIndex The index of the fulfillment component + * that does not match the initial offer + * item. * * @return execution The transfer performed as a result of the fulfillment. */ function _applyFulfillment( OrderToExecute[] memory ordersToExecute, - FulfillmentComponent[] calldata offerComponents, - FulfillmentComponent[] calldata considerationComponents + FulfillmentComponent[] memory offerComponents, + FulfillmentComponent[] memory considerationComponents, + uint256 fulfillmentIndex ) internal pure returns (Execution memory execution) { // Ensure 1+ of both offer and consideration components are supplied. if ( @@ -67,11 +73,19 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { ReceivedItem memory considerationItem = ( _aggregateValidFulfillmentConsiderationItems( ordersToExecute, - considerationComponents, - 0 + considerationComponents ) ); + // Skip aggregating offer items if no consideration items are available. + if (considerationItem.amount == 0) { + // Set the offerer and recipient to null address if execution + // amount is zero. This will cause the execution item to be skipped. + execution.offerer = address(0); + execution.item.recipient = payable(0); + return execution; + } + // Validate & aggregate offer items and store result as an Execution. ( execution @@ -86,7 +100,6 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { ) = _aggregateValidFulfillmentOfferItems( ordersToExecute, offerComponents, - 0, address(0) // unused ); @@ -96,7 +109,9 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { execution.item.token != considerationItem.token || execution.item.identifier != considerationItem.identifier ) { - revert MismatchedFulfillmentOfferAndConsiderationComponents(); + revert MismatchedFulfillmentOfferAndConsiderationComponents( + fulfillmentIndex + ); } // If total consideration amount exceeds the offer amount... @@ -195,41 +210,39 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { // If no available order was located... if (nextComponentIndex == 0) { // Return with an empty execution element that will be filtered. - // prettier-ignore - return Execution( - ReceivedItem( - ItemType.NATIVE, + return + Execution( + ReceivedItem( + ItemType.NATIVE, + address(0), + 0, + 0, + payable(address(0)) + ), address(0), - 0, - 0, - payable(address(0)) - ), - address(0), - bytes32(0) - ); + bytes32(0) + ); } // If the fulfillment components are offer components... if (side == Side.OFFER) { // Return execution for aggregated items provided by offerer. - // prettier-ignore - return _aggregateValidFulfillmentOfferItems( - ordersToExecute, - fulfillmentComponents, - nextComponentIndex - 1, - recipient - ); + return + _aggregateValidFulfillmentOfferItems( + ordersToExecute, + fulfillmentComponents, + recipient + ); } else { // Otherwise, fulfillment components are consideration // components. Return execution for aggregated items provided by // the fulfiller. - // prettier-ignore - return _aggregateConsiderationItems( - ordersToExecute, - fulfillmentComponents, - nextComponentIndex - 1, - fulfillerConduitKey - ); + return + _aggregateConsiderationItems( + ordersToExecute, + fulfillmentComponents, + fulfillerConduitKey + ); } } @@ -237,9 +250,9 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { * @dev Internal pure function to check the indicated offer item * matches original item. * - * @param orderToExecute The order to compare. - * @param offer The offer to compare - * @param execution The aggregated offer item + * @param orderToExecute The order to compare. + * @param offer The offer to compare. + * @param execution The aggregated offer item. * * @return invalidFulfillment A boolean indicating whether the * fulfillment is invalid. @@ -266,106 +279,110 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { * @param offerComponents An array of FulfillmentComponent structs * indicating the order index and item index of each * candidate offer item for aggregation. - * @param startIndex The initial order index to begin iteration on when - * searching for offer items to aggregate. + * @param recipient The recipient for the aggregated offer items. * * @return execution The aggregated offer items. */ function _aggregateValidFulfillmentOfferItems( OrderToExecute[] memory ordersToExecute, FulfillmentComponent[] memory offerComponents, - uint256 startIndex, address recipient ) internal pure returns (Execution memory execution) { + bool foundItem = false; + // Get the order index and item index of the offer component. - uint256 orderIndex = offerComponents[startIndex].orderIndex; - uint256 itemIndex = offerComponents[startIndex].itemIndex; + uint256 orderIndex; + uint256 itemIndex; - // Declare a variable indicating whether the aggregation is invalid. + OrderToExecute memory orderToExecute; + + // Declare variables indicating whether the aggregation is invalid. // Ensure that the order index is not out of range. - bool invalidFulfillment = (orderIndex >= ordersToExecute.length); - if (!invalidFulfillment) { - // Get the order based on offer components order index. - OrderToExecute memory orderToExecute = ordersToExecute[orderIndex]; - // Ensure that the item index is not out of range. - invalidFulfillment = - invalidFulfillment || - (itemIndex >= orderToExecute.spentItems.length); + bool invalidFulfillment; + + // Ensure that no available items have missing amounts. + bool missingItemAmount; + + // Loop through the offer components, checking for validity. + for (uint256 i = 0; i < offerComponents.length; ++i) { + // Get the order index and item index of the offer component. + orderIndex = offerComponents[i].orderIndex; + itemIndex = offerComponents[i].itemIndex; + + // Ensure that the order index is not out of range. + invalidFulfillment = orderIndex >= ordersToExecute.length; + // Break if invalid. + if (invalidFulfillment) { + break; + } - if (!invalidFulfillment) { + // Get the order based on offer components order index. + orderToExecute = ordersToExecute[orderIndex]; + if ( + orderToExecute.numerator != 0 && + itemIndex < orderToExecute.spentItems.length + ) { // Get the spent item based on the offer components item index. SpentItem memory offer = orderToExecute.spentItems[itemIndex]; - // Create the Execution. - execution = Execution( - ReceivedItem( - offer.itemType, - offer.token, - offer.identifier, - offer.amount, - payable(recipient) - ), - orderToExecute.offerer, - orderToExecute.conduitKey - ); + if (!foundItem) { + foundItem = true; + + // Create the Execution. + execution = Execution( + ReceivedItem( + offer.itemType, + offer.token, + offer.identifier, + offer.amount, + payable(recipient) + ), + orderToExecute.offerer, + orderToExecute.conduitKey + ); + + // If component index > 0, swap component pointer with + // pointer to first component so that any remainder after + // fulfillment can be added back to the first item. + if (i != 0) { + FulfillmentComponent + memory firstComponent = offerComponents[0]; + offerComponents[0] = offerComponents[i]; + offerComponents[i] = firstComponent; + } + } else { + // Update the Received Item amount. + execution.item.amount = + execution.item.amount + + offer.amount; + + // Ensure indicated offer item matches original item. + invalidFulfillment = _checkMatchingOffer( + orderToExecute, + offer, + execution + ); + } - // Zero out amount on original offerItem to indicate it is spent + // Ensure the item has a nonzero amount. + missingItemAmount = offer.amount == 0; + invalidFulfillment = invalidFulfillment || missingItemAmount; + + // Zero out amount on original offerItem to indicate it's spent. offer.amount = 0; - // Loop through the offer components, checking for validity. - for ( - uint256 i = startIndex + 1; - i < offerComponents.length; - ++i - ) { - // Get the order index and item index of the offer - // component. - orderIndex = offerComponents[i].orderIndex; - itemIndex = offerComponents[i].itemIndex; - - // Ensure that the order index is not out of range. - invalidFulfillment = orderIndex >= ordersToExecute.length; - // Break if invalid - if (invalidFulfillment) { - break; - } - // Get the order based on offer components order index. - orderToExecute = ordersToExecute[orderIndex]; - if (orderToExecute.numerator != 0) { - // Ensure that the item index is not out of range. - invalidFulfillment = (itemIndex >= - orderToExecute.spentItems.length); - // Break if invalid - if (invalidFulfillment) { - break; - } - // Get the spent item based on the offer components - // item index. - offer = orderToExecute.spentItems[itemIndex]; - // Update the Received Item Amount. - execution.item.amount = - execution.item.amount + - offer.amount; - // Zero out amount on original offerItem to indicate - // it is spent, - offer.amount = 0; - // Ensure the indicated offer item matches original - // item. - invalidFulfillment = _checkMatchingOffer( - orderToExecute, - offer, - execution - ); - // Break if invalid - if (invalidFulfillment) { - break; - } - } + // Break if invalid. + if (invalidFulfillment) { + break; } } } + // Revert if an order/item was out of range or was not aggregatable. if (invalidFulfillment) { + if (missingItemAmount) { + revert MissingItemAmount(); + } revert InvalidFulfillmentComponentData(); } } @@ -380,8 +397,6 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { * @param considerationComponents An array designating consideration * components to aggregate if part of an * available order. - * @param nextComponentIndex The index of the next potential - * consideration component. * @param fulfillerConduitKey A bytes32 value indicating what conduit, * if any, to source the fulfiller's token * approvals from. The zero hash signifies @@ -393,7 +408,6 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { function _aggregateConsiderationItems( OrderToExecute[] memory ordersToExecute, FulfillmentComponent[] memory considerationComponents, - uint256 nextComponentIndex, bytes32 fulfillerConduitKey ) internal view returns (Execution memory execution) { // Validate and aggregate consideration items on available orders and @@ -401,8 +415,7 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { ReceivedItem memory receiveConsiderationItem = ( _aggregateValidFulfillmentConsiderationItems( ordersToExecute, - considerationComponents, - nextComponentIndex + considerationComponents ) ); @@ -418,8 +431,8 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { * @dev Internal pure function to check the indicated consideration item * matches original item. * - * @param consideration The consideration to compare - * @param receivedItem The aggregated received item + * @param consideration The consideration to compare. + * @param receivedItem The aggregated received item. * * @return invalidFulfillment A boolean indicating whether the fulfillment * is invalid. @@ -446,119 +459,113 @@ contract ReferenceFulfillmentApplier is FulfillmentApplicationErrors { * indicating the order index and item index * of each candidate consideration item for * aggregation. - * @param startIndex The initial order index to begin iteration - * on when searching for consideration items - * to aggregate. * * @return receivedItem The aggregated consideration items. */ function _aggregateValidFulfillmentConsiderationItems( OrderToExecute[] memory ordersToExecute, - FulfillmentComponent[] memory considerationComponents, - uint256 startIndex + FulfillmentComponent[] memory considerationComponents ) internal pure returns (ReceivedItem memory receivedItem) { - // Declare struct in memory to avoid declaring multiple local variables + bool foundItem = false; + + // Declare struct in memory to avoid declaring multiple local variables. ConsiderationItemIndicesAndValidity memory potentialCandidate; - potentialCandidate.orderIndex = considerationComponents[startIndex] - .orderIndex; - potentialCandidate.itemIndex = considerationComponents[startIndex] - .itemIndex; - // Ensure that order index is in range. - potentialCandidate.invalidFulfillment = (potentialCandidate - .orderIndex >= ordersToExecute.length); - - if (!potentialCandidate.invalidFulfillment) { - // Retrieve relevant item using order index. - OrderToExecute memory orderToExecute = ordersToExecute[ - potentialCandidate.orderIndex - ]; - // Ensure that the item index is not out of range. + + ReceivedItem memory consideration; + + OrderToExecute memory orderToExecute; + + // Loop through the consideration components and validate + // their fulfillment. + for (uint256 i = 0; i < considerationComponents.length; ++i) { + // Get the order index and item index of the consideration + // component. + potentialCandidate.orderIndex = considerationComponents[i] + .orderIndex; + potentialCandidate.itemIndex = considerationComponents[i].itemIndex; + + /// Ensure that the order index is not out of range. potentialCandidate.invalidFulfillment = - potentialCandidate.invalidFulfillment || - (potentialCandidate.itemIndex >= - orderToExecute.receivedItems.length); - if (!potentialCandidate.invalidFulfillment) { + potentialCandidate.orderIndex >= ordersToExecute.length; + + // Break if invalid. + if (potentialCandidate.invalidFulfillment) { + break; + } + + // Get order based on consideration components order index. + orderToExecute = ordersToExecute[potentialCandidate.orderIndex]; + + // Confirm that the order is being fulfilled. + if ( + orderToExecute.numerator != 0 && + potentialCandidate.itemIndex < + orderToExecute.receivedItems.length + ) { // Retrieve relevant item using item index. - ReceivedItem memory consideration = orderToExecute - .receivedItems[potentialCandidate.itemIndex]; - - // Create the received item. - receivedItem = ReceivedItem( - consideration.itemType, - consideration.token, - consideration.identifier, - consideration.amount, - consideration.recipient - ); + consideration = orderToExecute.receivedItems[ + potentialCandidate.itemIndex + ]; + + if (!foundItem) { + foundItem = true; + + // Create the received item. + receivedItem = ReceivedItem( + consideration.itemType, + consideration.token, + consideration.identifier, + consideration.amount, + consideration.recipient + ); + + // If component index > 0, swap component pointer with + // pointer to first component so that any remainder after + // fulfillment can be added back to the first item. + if (i != 0) { + FulfillmentComponent + memory firstComponent = considerationComponents[0]; + considerationComponents[0] = considerationComponents[i]; + considerationComponents[i] = firstComponent; + } + } else { + // Update Received Item amount. + receivedItem.amount = + receivedItem.amount + + consideration.amount; + + // Ensure the indicated consideration item matches + // original item. + potentialCandidate + .invalidFulfillment = _checkMatchingConsideration( + consideration, + receivedItem + ); + } + + // Ensure the item has a nonzero amount. + potentialCandidate.missingItemAmount = + consideration.amount == 0; + potentialCandidate.invalidFulfillment = + potentialCandidate.invalidFulfillment || + potentialCandidate.missingItemAmount; - // Zero out amount on original offerItem to indicate it is spent + // Zero out amount on original consideration item to + // indicate it is spent. consideration.amount = 0; - // Loop through the consideration components and validate - // their fulfillment. - for ( - uint256 i = startIndex + 1; - i < considerationComponents.length; - ++i - ) { - // Get the order index and item index of the consideration - // component. - potentialCandidate.orderIndex = considerationComponents[i] - .orderIndex; - potentialCandidate.itemIndex = considerationComponents[i] - .itemIndex; - - /// Ensure that the order index is not out of range. - potentialCandidate.invalidFulfillment = - potentialCandidate.orderIndex >= ordersToExecute.length; - // Break if invalid - if (potentialCandidate.invalidFulfillment) { - break; - } - // Get the order based on consideration components order - // index. - orderToExecute = ordersToExecute[ - potentialCandidate.orderIndex - ]; - // Confirm this is a fulfilled order. - if (orderToExecute.numerator != 0) { - // Ensure that the item index is not out of range. - potentialCandidate - .invalidFulfillment = (potentialCandidate - .itemIndex >= orderToExecute.receivedItems.length); - // Break if invalid - if (potentialCandidate.invalidFulfillment) { - break; - } - // Retrieve relevant item using item index. - consideration = orderToExecute.receivedItems[ - potentialCandidate.itemIndex - ]; - // Updating Received Item Amount - receivedItem.amount = - receivedItem.amount + - consideration.amount; - // Zero out amount on original consideration item to - // indicate it is spent - consideration.amount = 0; - // Ensure the indicated consideration item matches - // original item. - potentialCandidate - .invalidFulfillment = _checkMatchingConsideration( - consideration, - receivedItem - ); - // Break if invalid - if (potentialCandidate.invalidFulfillment) { - break; - } - } + // Break if invalid. + if (potentialCandidate.invalidFulfillment) { + break; } } } // Revert if an order/item was out of range or was not aggregatable. if (potentialCandidate.invalidFulfillment) { + if (potentialCandidate.missingItemAmount) { + revert MissingItemAmount(); + } revert InvalidFulfillmentComponentData(); } } diff --git a/reference/lib/ReferenceGenerateOrderReturndataDecoder.sol b/reference/lib/ReferenceGenerateOrderReturndataDecoder.sol new file mode 100644 index 000000000..2331f4cbf --- /dev/null +++ b/reference/lib/ReferenceGenerateOrderReturndataDecoder.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; + +contract ReferenceGenerateOrderReturndataDecoder { + function decode( + bytes calldata returnedBytes + ) external pure returns (SpentItem[] memory, ReceivedItem[] memory) { + return abi.decode(returnedBytes, (SpentItem[], ReceivedItem[])); + } +} diff --git a/reference/lib/ReferenceGettersAndDerivers.sol b/reference/lib/ReferenceGettersAndDerivers.sol index 60461d6ba..2ea74a5a8 100644 --- a/reference/lib/ReferenceGettersAndDerivers.sol +++ b/reference/lib/ReferenceGettersAndDerivers.sol @@ -1,12 +1,14 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { ConsiderationItem, OfferItem, OrderParameters } from "contracts/lib/ConsiderationStructs.sol"; +import { + ConsiderationItem, + OfferItem, + OrderParameters +} from "../../contracts/lib/ConsiderationStructs.sol"; import { ReferenceConsiderationBase } from "./ReferenceConsiderationBase.sol"; -import "contracts/lib/ConsiderationConstants.sol"; - /** * @title GettersAndDerivers * @author 0age @@ -22,9 +24,9 @@ contract ReferenceGettersAndDerivers is ReferenceConsiderationBase { * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceConsiderationBase(conduitController) - {} + constructor( + address conduitController + ) ReferenceConsiderationBase(conduitController) {} /** * @dev Internal view function to derive the EIP-712 hash for an offer item. @@ -33,11 +35,9 @@ contract ReferenceGettersAndDerivers is ReferenceConsiderationBase { * * @return The hash. */ - function _hashOfferItem(OfferItem memory offerItem) - internal - view - returns (bytes32) - { + function _hashOfferItem( + OfferItem memory offerItem + ) internal view returns (bytes32) { return keccak256( abi.encode( @@ -59,11 +59,9 @@ contract ReferenceGettersAndDerivers is ReferenceConsiderationBase { * * @return The hash. */ - function _hashConsiderationItem(ConsiderationItem memory considerationItem) - internal - view - returns (bytes32) - { + function _hashConsiderationItem( + ConsiderationItem memory considerationItem + ) internal view returns (bytes32) { return keccak256( abi.encode( @@ -149,11 +147,10 @@ contract ReferenceGettersAndDerivers is ReferenceConsiderationBase { * * @return value The hash. */ - function _deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) - internal - pure - returns (bytes32 value) - { + function _deriveEIP712Digest( + bytes32 domainSeparator, + bytes32 orderHash + ) internal pure returns (bytes32 value) { value = keccak256( abi.encodePacked(uint16(0x1901), domainSeparator, orderHash) ); @@ -171,11 +168,9 @@ contract ReferenceGettersAndDerivers is ReferenceConsiderationBase { * @return conduit The address of the conduit associated with the given * conduit key. */ - function _deriveConduit(bytes32 conduitKey) - internal - view - returns (address conduit) - { + function _deriveConduit( + bytes32 conduitKey + ) internal view returns (address conduit) { // Derive the address of the conduit. conduit = address( uint160( @@ -200,12 +195,14 @@ contract ReferenceGettersAndDerivers is ReferenceConsiderationBase { * scratch. */ function _domainSeparator() internal view returns (bytes32) { - // prettier-ignore - return block.chainid == _CHAIN_ID - ? _DOMAIN_SEPARATOR - : _deriveDomainSeparator(_EIP_712_DOMAIN_TYPEHASH, - _NAME_HASH, - _VERSION_HASH); + return + block.chainid == _CHAIN_ID + ? _DOMAIN_SEPARATOR + : _deriveDomainSeparator( + _EIP_712_DOMAIN_TYPEHASH, + _NAME_HASH, + _VERSION_HASH + ); } /** diff --git a/reference/lib/ReferenceOrderCombiner.sol b/reference/lib/ReferenceOrderCombiner.sol index 570fb33e2..90f8d9f96 100644 --- a/reference/lib/ReferenceOrderCombiner.sol +++ b/reference/lib/ReferenceOrderCombiner.sol @@ -1,31 +1,37 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { Side, ItemType } from "contracts/lib/ConsiderationEnums.sol"; +import { + ItemType, + OrderType, + Side +} from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { - AdditionalRecipient, - OfferItem, + AdvancedOrder, ConsiderationItem, - SpentItem, - ReceivedItem, - OrderParameters, + CriteriaResolver, + Execution, Fulfillment, FulfillmentComponent, - Execution, - Order, - AdvancedOrder, - CriteriaResolver -} from "contracts/lib/ConsiderationStructs.sol"; + OfferItem, + OrderParameters, + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { AccumulatorStruct, OrderToExecute } from "./ReferenceConsiderationStructs.sol"; +import { + AccumulatorStruct, + OrderToExecute +} from "./ReferenceConsiderationStructs.sol"; import { ReferenceOrderFulfiller } from "./ReferenceOrderFulfiller.sol"; import { ReferenceFulfillmentApplier } from "./ReferenceFulfillmentApplier.sol"; -import "contracts/lib/ConsiderationConstants.sol"; +import { + SeaportInterface +} from "../../contracts/interfaces/SeaportInterface.sol"; /** * @title OrderCombiner @@ -46,9 +52,9 @@ contract ReferenceOrderCombiner is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceOrderFulfiller(conduitController) - {} + constructor( + address conduitController + ) ReferenceOrderFulfiller(conduitController) {} /** * @notice Internal function to attempt to fill a group of orders, fully or @@ -136,7 +142,7 @@ contract ReferenceOrderCombiner is returns (bool[] memory availableOrders, Execution[] memory executions) { // Validate orders, apply amounts, & determine if they utilize conduits - _validateOrdersAndPrepareToFulfill( + bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill( advancedOrders, ordersToExecute, criteriaResolvers, @@ -147,11 +153,13 @@ contract ReferenceOrderCombiner is // Execute transfers. (availableOrders, executions) = _executeAvailableFulfillments( + advancedOrders, ordersToExecute, offerFulfillments, considerationFulfillments, fulfillerConduitKey, - recipient + recipient, + orderHashes ); // Return order fulfillment details and executions. @@ -179,6 +187,8 @@ contract ReferenceOrderCombiner is * instead cause the invalid order to be skipped. * @param maximumFulfilled The maximum number of orders to fulfill. * @param recipient The intended recipient for all received items. + * + * @return orderHashes The hashes of the orders being fulfilled. */ function _validateOrdersAndPrepareToFulfill( AdvancedOrder[] memory advancedOrders, @@ -187,16 +197,21 @@ contract ReferenceOrderCombiner is bool revertOnInvalid, uint256 maximumFulfilled, address recipient - ) internal { + ) internal returns (bytes32[] memory orderHashes) { // Read length of orders array and place on the stack. uint256 totalOrders = advancedOrders.length; // Track the order hash for each order being fulfilled. - bytes32[] memory orderHashes = new bytes32[](totalOrders); + orderHashes = new bytes32[](totalOrders); + + // Determine whether or not order matching is underway. + bool nonMatchFn = msg.sig != + SeaportInterface.matchAdvancedOrders.selector && + msg.sig != SeaportInterface.matchOrders.selector; - // Check if we are in a match function - bool nonMatchFn = msg.sig != 0x55944a42 && msg.sig != 0xa8174404; - bool anyNativeOfferItems; + // Declare a variable for tracking whether native offer items are + // present on orders that are not contract orders. + bool anyNativeOfferItemsOnNonContractOrders; // Iterate over each order. for (uint256 i = 0; i < totalOrders; ++i) { @@ -222,12 +237,7 @@ contract ReferenceOrderCombiner is bytes32 orderHash, uint256 numerator, uint256 denominator - ) = _validateOrderAndUpdateStatus( - advancedOrder, - criteriaResolvers, - revertOnInvalid, - orderHashes - ); + ) = _validateOrderAndUpdateStatus(advancedOrder, revertOnInvalid); // Do not track hash or adjust prices if order is not fulfilled. if (numerator == 0) { @@ -244,11 +254,8 @@ contract ReferenceOrderCombiner is // Otherwise, track the order hash in question. orderHashes[i] = orderHash; - // Skip underflow check as maximumFulfilled is nonzero. - unchecked { - // Decrement the number of fulfilled orders. - maximumFulfilled--; - } + // Decrement the number of fulfilled orders. + maximumFulfilled--; // Place the start time for the order on the stack. uint256 startTime = advancedOrder.parameters.startTime; @@ -264,9 +271,11 @@ contract ReferenceOrderCombiner is // Retrieve the offer item. OfferItem memory offerItem = offer[j]; - anyNativeOfferItems = - anyNativeOfferItems || - offerItem.itemType == ItemType.NATIVE; + anyNativeOfferItemsOnNonContractOrders = + anyNativeOfferItemsOnNonContractOrders || + (offerItem.itemType == ItemType.NATIVE && + advancedOrder.parameters.orderType != + OrderType.CONTRACT); // Apply order fill fraction to offer item end amount. uint256 endAmount = _getFraction( @@ -356,7 +365,7 @@ contract ReferenceOrderCombiner is } } - if (anyNativeOfferItems && nonMatchFn) { + if (anyNativeOfferItemsOnNonContractOrders && nonMatchFn) { revert InvalidNativeOfferItem(); } @@ -442,6 +451,7 @@ contract ReferenceOrderCombiner is * direct approvals set on Consideration). * @param recipient The intended recipient for all received * items. + * @param orderHashes An array of order hashes for each order. * * @return availableOrders An array of booleans indicating if each * order with an index corresponding to the @@ -452,11 +462,13 @@ contract ReferenceOrderCombiner is * of matching the given orders. */ function _executeAvailableFulfillments( + AdvancedOrder[] memory advancedOrders, OrderToExecute[] memory ordersToExecute, FulfillmentComponent[][] memory offerFulfillments, FulfillmentComponent[][] memory considerationFulfillments, bytes32 fulfillerConduitKey, - address recipient + address recipient, + bytes32[] memory orderHashes ) internal returns (bool[] memory availableOrders, Execution[] memory executions) @@ -493,11 +505,8 @@ contract ReferenceOrderCombiner is // If offerer and recipient on the execution are the same... if (execution.item.recipient == execution.offerer) { - // Executions start at 0, infeasible to increment > 2^256. - unchecked { - // Increment total filtered executions. - ++totalFilteredExecutions; - } + // Increment total filtered executions. + ++totalFilteredExecutions; } else { // Otherwise, assign the execution to the executions array. executions[i - totalFilteredExecutions] = execution; @@ -522,11 +531,8 @@ contract ReferenceOrderCombiner is // If offerer and recipient on the execution are the same... if (execution.item.recipient == execution.offerer) { - // Executions start at 0, infeasible to increment > 2^256. - unchecked { - // Increment total filtered executions. - ++totalFilteredExecutions; - } + // Increment total filtered executions. + ++totalFilteredExecutions; } else { // Otherwise, assign the execution to the executions array. executions[ @@ -566,8 +572,11 @@ contract ReferenceOrderCombiner is } // Perform final checks and compress executions into standard and batch. availableOrders = _performFinalChecksAndExecuteOrders( + advancedOrders, ordersToExecute, - executions + executions, + orderHashes, + recipient ); return (availableOrders, executions); @@ -586,22 +595,66 @@ contract ReferenceOrderCombiner is * @param executions An array of elements indicating the sequence of * transfers to perform when fulfilling the given * orders. + * @param orderHashes An array of order hashes for each order. * - * @return availableOrders An array of booleans indicating if each order - * with an index corresponding to the index of the - * returned boolean was fulfillable or not. + * @return availableOrders An array of booleans indicating if each order + * with an index corresponding to the index of the + * returned boolean was fulfillable or not. */ function _performFinalChecksAndExecuteOrders( + AdvancedOrder[] memory advancedOrders, OrderToExecute[] memory ordersToExecute, - Execution[] memory executions + Execution[] memory executions, + bytes32[] memory orderHashes, + address recipient ) internal returns (bool[] memory availableOrders) { + // Put ether value supplied by the caller on the stack. + uint256 nativeTokensRemaining = msg.value; + // Retrieve the length of the advanced orders array and place on stack. - uint256 totalOrders = ordersToExecute.length; + uint256 totalOrders = advancedOrders.length; // Initialize array for tracking available orders. availableOrders = new bool[](totalOrders); - // Iterate over orders to ensure all considerations are met. - for (uint256 i = 0; i < totalOrders; ++i) { + + // Create the accumulator struct. + AccumulatorStruct memory accumulatorStruct; + + { + // Iterate over each execution. + for (uint256 i = 0; i < executions.length; ++i) { + // Retrieve the execution and the associated received item. + Execution memory execution = executions[i]; + ReceivedItem memory item = execution.item; + + // If execution transfers native tokens, reduce value available. + if (item.itemType == ItemType.NATIVE) { + // Ensure that sufficient native tokens are still available. + if (item.amount > nativeTokensRemaining) { + revert InsufficientNativeTokensSupplied(); + } + + // Reduce ether remaining by amount. + nativeTokensRemaining -= item.amount; + } + + // Transfer the item specified by the execution. + _transfer( + item, + execution.offerer, + execution.conduitKey, + accumulatorStruct + ); + } + + // Trigger remaining accumulated transfers via call to the conduit. + _triggerIfArmed(accumulatorStruct); + } + + // duplicate recipient onto stack to avoid stack-too-deep + address _recipient = recipient; + // Iterate over orders to ensure all consideration items are met. + for (uint256 i = 0; i < ordersToExecute.length; ++i) { // Retrieve the order in question. OrderToExecute memory orderToExecute = ordersToExecute[i]; @@ -616,67 +669,142 @@ contract ReferenceOrderCombiner is // Mark the order as available. availableOrders[i] = true; - // Retrieve consideration items to ensure they are fulfilled. - ReceivedItem[] memory consideration = ( - orderToExecute.receivedItems - ); - - // Iterate over each consideration item to ensure it is met. - for (uint256 j = 0; j < consideration.length; ++j) { - // Retrieve remaining amount on the consideration item. - uint256 unmetAmount = consideration[j].amount; + // Retrieve the original order in question. + AdvancedOrder memory advancedOrder = advancedOrders[i]; - // Revert if the remaining amount is not zero. - if (unmetAmount != 0) { - revert ConsiderationNotMet(i, j, unmetAmount); + // Retrieve the order parameters. + OrderParameters memory parameters = advancedOrder.parameters; + + { + // Retrieve offer items. + OfferItem[] memory offer = parameters.offer; + + // Read length of offer array & place on the stack. + uint256 totalOfferItems = offer.length; + + // Iterate over each offer item to restore it. + for (uint256 j = 0; j < totalOfferItems; ++j) { + SpentItem memory offerSpentItem = orderToExecute.spentItems[ + j + ]; + + // Retrieve remaining amount on the offer item. + uint256 unspentAmount = offerSpentItem.amount; + + // Retrieve original amount on the offer item. + uint256 originalAmount = orderToExecute + .spentItemOriginalAmounts[j]; + + // Transfer to recipient if unspent amount is not zero. + // Note that the transfer will not be reflected in the + // executions array. + if (unspentAmount != 0) { + _transfer( + _convertSpentItemToReceivedItemWithRecipient( + offerSpentItem, + _recipient + ), + parameters.offerer, + parameters.conduitKey, + accumulatorStruct + ); + } + + // Restore original amount on the offer item. + offerSpentItem.amount = originalAmount; } } - } - // Put ether value supplied by the caller on the stack. - uint256 etherRemaining = msg.value; + { + // Retrieve consideration items to ensure they are fulfilled. + ReceivedItem[] memory consideration = ( + orderToExecute.receivedItems + ); - // Create the accumulator struct. - AccumulatorStruct memory accumulatorStruct; + // Iterate over each consideration item to ensure it is met. + for (uint256 j = 0; j < consideration.length; ++j) { + // Retrieve remaining amount on the consideration item. + uint256 unmetAmount = consideration[j].amount; - // Iterate over each execution. - for (uint256 i = 0; i < executions.length; ++i) { - // Retrieve the execution and the associated received item. - Execution memory execution = executions[i]; - ReceivedItem memory item = execution.item; - - // If execution transfers native tokens, reduce value available. - if (item.itemType == ItemType.NATIVE) { - // Ensure that sufficient native tokens are still available. - if (item.amount > etherRemaining) { - revert InsufficientEtherSupplied(); + // Revert if the remaining amount is not zero. + if (unmetAmount != 0) { + revert ConsiderationNotMet(i, j, unmetAmount); + } + + // Restore original amount. + consideration[j].amount = orderToExecute + .receivedItemOriginalAmounts[j]; } + } - // Reduce ether remaining by amount. - etherRemaining -= item.amount; + { + // Get offer items as well. + SpentItem[] memory offer = (orderToExecute.spentItems); + + // Iterate over each consideration item to ensure it is met. + for (uint256 j = 0; j < offer.length; ++j) { + // Restore original amount. + offer[j].amount = orderToExecute.spentItemOriginalAmounts[ + j + ]; + } } - // Transfer the item specified by the execution. - _transfer( - item, - execution.offerer, - execution.conduitKey, - accumulatorStruct + // Ensure restricted orders have valid submitter or pass check. + _assertRestrictedAdvancedOrderValidity( + advancedOrder, + orderToExecute, + orderHashes, + orderHashes[i], + advancedOrder.parameters.zoneHash, + advancedOrder.parameters.orderType, + orderToExecute.offerer, + advancedOrder.parameters.zone ); } // Trigger any remaining accumulated transfers via call to the conduit. _triggerIfArmed(accumulatorStruct); - // If any ether remains after fulfillments, return it to the caller. - if (etherRemaining != 0) { - _transferEth(payable(msg.sender), etherRemaining); + // If any native token remains after fulfillments, return it to the + // caller. + if (nativeTokensRemaining != 0) { + _transferNativeTokens(payable(msg.sender), nativeTokensRemaining); } // Return the array containing available orders. return availableOrders; } + /** + * @dev Internal function to convert a spent item to an equivalent + * ReceivedItem with a specified recipient. + * + * @param offerItem The "offerItem" represented by a SpentItem + * struct. + * @param recipient The intended recipient of the converted + * ReceivedItem + * + * @return ReceivedItem The derived ReceivedItem including the + * specified recipient. + */ + function _convertSpentItemToReceivedItemWithRecipient( + SpentItem memory offerItem, + address recipient + ) internal pure returns (ReceivedItem memory) { + address payable _recipient; + _recipient = payable(recipient); + + return + ReceivedItem( + offerItem.itemType, + offerItem.token, + offerItem.identifier, + offerItem.amount, + _recipient + ); + } + /** * @dev Internal function to match an arbitrary number of full or partial * orders, each with an arbitrary number of items for offer and @@ -708,6 +836,8 @@ contract ReferenceOrderCombiner is * to consideration components. Note that each * consideration component must be fully met in * order for the match operation to be valid. + * @param recipient The intended recipient for all unspent offer + * item amounts. * * @return executions An array of elements indicating the sequence of * transfers performed as part of matching the @@ -716,7 +846,8 @@ contract ReferenceOrderCombiner is function _matchAdvancedOrders( AdvancedOrder[] memory advancedOrders, CriteriaResolver[] memory criteriaResolvers, - Fulfillment[] calldata fulfillments + Fulfillment[] calldata fulfillments, + address recipient ) internal returns (Execution[] memory executions) { // Convert Advanced Orders to Orders to Execute OrderToExecute[] @@ -725,17 +856,27 @@ contract ReferenceOrderCombiner is ); // Validate orders, apply amounts, & determine if they utilize conduits. - _validateOrdersAndPrepareToFulfill( + bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill( advancedOrders, ordersToExecute, criteriaResolvers, true, // Signifies that invalid orders should revert. advancedOrders.length, - address(0) + recipient ); + // Emit OrdersMatched event. + emit OrdersMatched(orderHashes); + // Fulfill the orders using the supplied fulfillments. - return _fulfillAdvancedOrders(ordersToExecute, fulfillments); + return + _fulfillAdvancedOrders( + advancedOrders, + ordersToExecute, + fulfillments, + orderHashes, + recipient + ); } /** @@ -750,14 +891,18 @@ contract ReferenceOrderCombiner is * that the final amount of each consideration * component must be zero for a match operation to * be considered valid. + * @param orderHashes An array of order hashes for each order. * - * @return executions An array of elements indicating the sequence - * of transfers performed as part of - * matching the given orders. + * @return executions An array of elements indicating the sequence + * of transfers performed as part of + * matching the given orders. */ function _fulfillAdvancedOrders( + AdvancedOrder[] memory advancedOrders, OrderToExecute[] memory ordersToExecute, - Fulfillment[] calldata fulfillments + Fulfillment[] calldata fulfillments, + bytes32[] memory orderHashes, + address recipient ) internal returns (Execution[] memory executions) { // Retrieve fulfillments array length and place on the stack. uint256 totalFulfillments = fulfillments.length; @@ -777,16 +922,14 @@ contract ReferenceOrderCombiner is Execution memory execution = _applyFulfillment( ordersToExecute, fulfillment.offerComponents, - fulfillment.considerationComponents + fulfillment.considerationComponents, + i ); // If offerer and recipient on the execution are the same... if (execution.item.recipient == execution.offerer) { - // Executions start at 0, infeasible to increment > 2^256. - unchecked { - // Increment total filtered executions. - ++totalFilteredExecutions; - } + // Increment total filtered executions. + ++totalFilteredExecutions; } else { // Otherwise, assign the execution to the executions array. executions[i - totalFilteredExecutions] = execution; @@ -809,7 +952,13 @@ contract ReferenceOrderCombiner is } // Perform final checks and execute orders. - _performFinalChecksAndExecuteOrders(ordersToExecute, executions); + _performFinalChecksAndExecuteOrders( + advancedOrders, + ordersToExecute, + executions, + orderHashes, + recipient + ); // Return executions. return executions; diff --git a/reference/lib/ReferenceOrderFulfiller.sol b/reference/lib/ReferenceOrderFulfiller.sol index a0f424908..374429887 100644 --- a/reference/lib/ReferenceOrderFulfiller.sol +++ b/reference/lib/ReferenceOrderFulfiller.sol @@ -1,35 +1,36 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { OrderType, ItemType } from "contracts/lib/ConsiderationEnums.sol"; +import { + ItemType, + OrderType +} from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { - OfferItem, + AdvancedOrder, ConsiderationItem, - SpentItem, - ReceivedItem, - OrderParameters, + CriteriaResolver, + OfferItem, Order, - AdvancedOrder, - CriteriaResolver -} from "contracts/lib/ConsiderationStructs.sol"; + OrderParameters, + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; -// prettier-ignore -import { +import { AccumulatorStruct, - FractionData, + FractionData, OrderToExecute } from "./ReferenceConsiderationStructs.sol"; -import { ReferenceBasicOrderFulfiller } from "./ReferenceBasicOrderFulfiller.sol"; +import { + ReferenceBasicOrderFulfiller +} from "./ReferenceBasicOrderFulfiller.sol"; import { ReferenceCriteriaResolution } from "./ReferenceCriteriaResolution.sol"; import { ReferenceAmountDeriver } from "./ReferenceAmountDeriver.sol"; -import "contracts/lib/ConsiderationConstants.sol"; - /** * @title OrderFulfiller * @author 0age @@ -48,9 +49,9 @@ contract ReferenceOrderFulfiller is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceBasicOrderFulfiller(conduitController) - {} + constructor( + address conduitController + ) ReferenceBasicOrderFulfiller(conduitController) {} /** * @dev Internal function to validate an order and update its status, adjust @@ -84,20 +85,12 @@ contract ReferenceOrderFulfiller is bytes32 fulfillerConduitKey, address recipient ) internal returns (bool) { - // Declare empty bytes32 array (unused, will remain empty). - bytes32[] memory priorOrderHashes; - // Validate order, update status, and determine fraction to fill. ( bytes32 orderHash, uint256 fillNumerator, uint256 fillDenominator - ) = _validateOrderAndUpdateStatus( - advancedOrder, - criteriaResolvers, - true, - priorOrderHashes - ); + ) = _validateOrderAndUpdateStatus(advancedOrder, true); // Apply criteria resolvers using generated orders and details arrays. _applyCriteriaResolversAdvanced(advancedOrder, criteriaResolvers); @@ -114,6 +107,22 @@ contract ReferenceOrderFulfiller is recipient ); + // Declare bytes32 array with this order's hash + bytes32[] memory priorOrderHashes = new bytes32[](1); + priorOrderHashes[0] = orderHash; + + // Ensure restricted orders have a valid submitter or pass a zone check. + _assertRestrictedAdvancedOrderValidity( + advancedOrder, + orderToExecute, + priorOrderHashes, + orderHash, + orderParameters.zoneHash, + orderParameters.orderType, + orderParameters.offerer, + orderParameters.zone + ); + // Emit an event signifying that the order has been fulfilled. emit OrderFulfilled( orderHash, @@ -183,9 +192,13 @@ contract ReferenceOrderFulfiller is for (uint256 i = 0; i < orderParameters.offer.length; ++i) { // Retrieve the offer item. OfferItem memory offerItem = orderParameters.offer[i]; - // Offer items for the native token can not be received - // outside of a match order function. - if (offerItem.itemType == ItemType.NATIVE) { + + // Offer items for the native token can not be received outside + // of a match order function except as part of a contract order. + if ( + offerItem.itemType == ItemType.NATIVE && + orderParameters.orderType != OrderType.CONTRACT + ) { revert InvalidNativeOfferItem(); } @@ -225,7 +238,7 @@ contract ReferenceOrderFulfiller is ); // Put ether value supplied by the caller on the stack. - uint256 etherRemaining = msg.value; + uint256 nativeTokensRemaining = msg.value; // Declare a nested scope to minimize stack depth. { @@ -258,11 +271,11 @@ contract ReferenceOrderFulfiller is // Reduce available value if offer spent ETH or a native token. if (receivedItem.itemType == ItemType.NATIVE) { // Ensure that sufficient native tokens are still available. - if (amount > etherRemaining) { - revert InsufficientEtherSupplied(); + if (amount > nativeTokensRemaining) { + revert InsufficientNativeTokensSupplied(); } // Reduce ether remaining by amount. - etherRemaining -= amount; + nativeTokensRemaining -= amount; } // Transfer item from caller to recipient specified by the item. @@ -278,10 +291,10 @@ contract ReferenceOrderFulfiller is // Trigger any remaining accumulated transfers via call to the conduit. _triggerIfArmed(accumulatorStruct); - // If any ether remains after fulfillments... - if (etherRemaining != 0) { + // If any native token remains after fulfillments... + if (nativeTokensRemaining != 0) { // return it to the caller. - _transferEth(payable(msg.sender), etherRemaining); + _transferNativeTokens(payable(msg.sender), nativeTokensRemaining); } // Return the order to execute. return orderToExecute; @@ -295,11 +308,9 @@ contract ReferenceOrderFulfiller is * * @return advancedOrder The new advanced order. */ - function _convertOrderToAdvanced(Order calldata order) - internal - pure - returns (AdvancedOrder memory advancedOrder) - { + function _convertOrderToAdvanced( + Order calldata order + ) internal pure returns (AdvancedOrder memory advancedOrder) { // Convert to partial order (1/1 or full fill) and return new value. advancedOrder = AdvancedOrder( order.parameters, @@ -318,11 +329,9 @@ contract ReferenceOrderFulfiller is * * @return advancedOrders The new array of partial orders. */ - function _convertOrdersToAdvanced(Order[] calldata orders) - internal - pure - returns (AdvancedOrder[] memory advancedOrders) - { + function _convertOrdersToAdvanced( + Order[] calldata orders + ) internal pure returns (AdvancedOrder[] memory advancedOrders) { // Read the number of orders from calldata and place on the stack. uint256 totalOrders = orders.length; @@ -347,16 +356,15 @@ contract ReferenceOrderFulfiller is * * @return orderToExecute The new order to execute. */ - function _convertAdvancedToOrder(AdvancedOrder memory advancedOrder) - internal - pure - returns (OrderToExecute memory orderToExecute) - { + function _convertAdvancedToOrder( + AdvancedOrder memory advancedOrder + ) internal pure returns (OrderToExecute memory orderToExecute) { // Retrieve the advanced orders offers. OfferItem[] memory offer = advancedOrder.parameters.offer; // Create an array of spent items equal to the offer length. SpentItem[] memory spentItems = new SpentItem[](offer.length); + uint256[] memory spentItemOriginalAmounts = new uint256[](offer.length); // Iterate over each offer item on the order. for (uint256 i = 0; i < offer.length; ++i) { @@ -371,11 +379,12 @@ contract ReferenceOrderFulfiller is offerItem.startAmount ); - // Add to array of spent items + // Add to array of spent items. spentItems[i] = spentItem; + spentItemOriginalAmounts[i] = offerItem.startAmount; } - // Retrieve the advanced orders considerations. + // Retrieve the consideration array from the advanced order. ConsiderationItem[] memory consideration = advancedOrder .parameters .consideration; @@ -384,6 +393,11 @@ contract ReferenceOrderFulfiller is ReceivedItem[] memory receivedItems = new ReceivedItem[]( consideration.length ); + // Create an array of uint256 values equal in length to the + // consideration length containing the amounts of each item. + uint256[] memory receivedItemOriginalAmounts = new uint256[]( + consideration.length + ); // Iterate over each consideration item on the order. for (uint256 i = 0; i < consideration.length; ++i) { @@ -399,8 +413,11 @@ contract ReferenceOrderFulfiller is considerationItem.recipient ); - // Add to array of received items + // Add to array of received items. receivedItems[i] = receivedItem; + + // Add to array of received item amounts. + receivedItemOriginalAmounts[i] = considerationItem.startAmount; } // Create the order to execute from the advanced order data. @@ -409,8 +426,11 @@ contract ReferenceOrderFulfiller is spentItems, receivedItems, advancedOrder.parameters.conduitKey, - advancedOrder.numerator + advancedOrder.numerator, + spentItemOriginalAmounts, + receivedItemOriginalAmounts ); + // Return the order. return orderToExecute; } diff --git a/reference/lib/ReferenceOrderValidator.sol b/reference/lib/ReferenceOrderValidator.sol index ce0734902..aa4cb252d 100644 --- a/reference/lib/ReferenceOrderValidator.sol +++ b/reference/lib/ReferenceOrderValidator.sol @@ -1,22 +1,35 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { OrderType } from "contracts/lib/ConsiderationEnums.sol"; +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore import { - OrderParameters, - Order, AdvancedOrder, + ConsiderationItem, + OfferItem, + Order, OrderComponents, + OrderParameters, OrderStatus, - CriteriaResolver -} from "contracts/lib/ConsiderationStructs.sol"; + ReceivedItem, + SpentItem +} from "../../contracts/lib/ConsiderationStructs.sol"; import { ReferenceExecutor } from "./ReferenceExecutor.sol"; import { ReferenceZoneInteraction } from "./ReferenceZoneInteraction.sol"; +import { + ContractOffererInterface +} from "../../contracts/interfaces/ContractOffererInterface.sol"; + +import { + ReferenceGenerateOrderReturndataDecoder +} from "./ReferenceGenerateOrderReturndataDecoder.sol"; + /** * @title OrderValidator * @author 0age @@ -30,6 +43,9 @@ contract ReferenceOrderValidator is // Track status of each order (validated, cancelled, and fraction filled). mapping(bytes32 => OrderStatus) private _orderStatus; + // Track nonces for contract offerers. + mapping(address => uint256) internal _contractNonces; + /** * @dev Derive and set hashes, reference chainId, and associated domain * separator during deployment. @@ -38,9 +54,9 @@ contract ReferenceOrderValidator is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceExecutor(conduitController) - {} + constructor( + address conduitController + ) ReferenceExecutor(conduitController) {} /** * @dev Internal function to verify and update the status of a basic order. @@ -83,23 +99,12 @@ contract ReferenceOrderValidator is * fill, and update its status. The desired fill amount is supplied as * a fraction, as is the returned amount to fill. * - * @param advancedOrder The order to fulfill as well as the fraction to - * fill. Note that all offer and consideration - * amounts must divide with no remainder in order - * for a partial fill to be valid. - * @param criteriaResolvers An array where each element contains a reference - * to a specific offer or consideration, a token - * identifier, and a proof that the supplied token - * identifier is contained in the order's merkle - * root. Note that a criteria of zero indicates - * that any (transferable) token identifier is - * valid and that no proof needs to be supplied. - * @param revertOnInvalid A boolean indicating whether to revert if the - * order is invalid due to the time or order status. - * @param priorOrderHashes The order hashes of each order supplied prior to - * the current order as part of a "match" variety of - * order fulfillment (e.g. this array will be empty - * for single or "fulfill available"). + * @param advancedOrder The order to fulfill as well as the fraction to + * fill. Note that all offer and consideration + * amounts must divide with no remainder in order for + * a partial fill to be valid. + * @param revertOnInvalid A boolean indicating whether to revert if the + * order is invalid due to the time or order status. * * @return orderHash The order hash. * @return newNumerator A value indicating the portion of the order that @@ -108,9 +113,7 @@ contract ReferenceOrderValidator is */ function _validateOrderAndUpdateStatus( AdvancedOrder memory advancedOrder, - CriteriaResolver[] memory criteriaResolvers, - bool revertOnInvalid, - bytes32[] memory priorOrderHashes + bool revertOnInvalid ) internal returns ( @@ -138,6 +141,21 @@ contract ReferenceOrderValidator is uint256 numerator = uint256(advancedOrder.numerator); uint256 denominator = uint256(advancedOrder.denominator); + // If the order is a contract order, return the generated order. + if (orderParameters.orderType == OrderType.CONTRACT) { + // Ensure that numerator and denominator are both equal to 1. + if (numerator != 1 || denominator != 1) { + revert BadFraction(); + } + + return + _getGeneratedOrder( + orderParameters, + advancedOrder.extraData, + revertOnInvalid + ); + } + // Ensure that the supplied numerator and denominator are valid. if (numerator > denominator || numerator == 0) { revert BadFraction(); @@ -155,18 +173,6 @@ contract ReferenceOrderValidator is // Retrieve current counter and use it w/ parameters to get order hash. orderHash = _assertConsiderationLengthAndGetOrderHash(orderParameters); - // Ensure a valid submitter. - _assertRestrictedAdvancedOrderValidity( - advancedOrder, - criteriaResolvers, - priorOrderHashes, - orderHash, - orderParameters.zoneHash, - orderParameters.orderType, - orderParameters.offerer, - orderParameters.zone - ); - // Retrieve the order status using the derived order hash. OrderStatus storage orderStatus = _orderStatus[orderHash]; @@ -234,13 +240,10 @@ contract ReferenceOrderValidator is _greatestCommonDivisor(filledNumerator, denominator) ); - // Note: this may not be necessary — need to validate. - uint256 safeScaleDown = scaleDown == 0 ? 1 : scaleDown; - // Scale all fractional values down by gcd. - numerator = numerator / safeScaleDown; - filledNumerator = filledNumerator / safeScaleDown; - denominator = denominator / safeScaleDown; + numerator = numerator / scaleDown; + filledNumerator = filledNumerator / scaleDown; + denominator = denominator / scaleDown; // Perform the overflow check a second time. uint256 maxOverhead = type(uint256).max - type(uint120).max; @@ -265,42 +268,264 @@ contract ReferenceOrderValidator is } /** - * @dev Internal function to derive the greatest common divisor of two - * values using the classical euclidian algorithm. + * @dev Internal function to generate a contract order. When a + * collection-wide criteria-based item (criteria = 0) is provided as an + * input to a contract order, the contract offerer has full latitude to + * choose any identifier it wants mid-flight, which differs from the + * usual behavior. For regular criteria-based orders with + * identifierOrCriteria = 0, the fulfiller can pick which identifier to + * receive by providing a CriteriaResolver. For contract offers with + * identifierOrCriteria = 0, Seaport does not expect a corresponding + * CriteriaResolver, and will revert if one is provided. * - * @param a The first value. - * @param b The second value. + * @param orderParameters The parameters for the order. + * @param context The context for generating the order. + * @param revertOnInvalid Whether to revert on invalid input. * - * @return greatestCommonDivisor The greatest common divisor. + * @return orderHash The order hash. + * @return numerator The numerator. + * @return denominator The denominator. */ - function _greatestCommonDivisor(uint256 a, uint256 b) + function _getGeneratedOrder( + OrderParameters memory orderParameters, + bytes memory context, + bool revertOnInvalid + ) internal - pure - returns (uint256 greatestCommonDivisor) + returns (bytes32 orderHash, uint256 numerator, uint256 denominator) { - while (b > 0) { - uint256 c = b; - b = a % c; - a = c; + // Ensure that consideration array length is equal to the total original + // consideration items value. + if ( + orderParameters.consideration.length != + orderParameters.totalOriginalConsiderationItems + ) { + revert ConsiderationLengthNotEqualToTotalOriginal(); } - greatestCommonDivisor = a; + { + // Increment contract nonce and use it to derive order hash. Note: + // nonce will be incremented even for skipped orders, and even if + // generateOrder's return data does not satisfy all the constraints. + uint256 contractNonce = _contractNonces[orderParameters.offerer]++; + // Derive order hash from contract nonce and offerer address. + orderHash = bytes32( + contractNonce ^ + (uint256(uint160(orderParameters.offerer)) << 96) + ); + } + + // Convert offer and consideration to spent and received items. + ( + SpentItem[] memory originalOfferItems, + SpentItem[] memory originalConsiderationItems + ) = _convertToSpent( + orderParameters.offer, + orderParameters.consideration + ); + + // Create arrays for returned offer and consideration items. + SpentItem[] memory offer; + ReceivedItem[] memory consideration; + + { + // Do a low-level call to get success status and any return data. + (bool success, bytes memory returnData) = orderParameters + .offerer + .call( + abi.encodeWithSelector( + ContractOffererInterface.generateOrder.selector, + msg.sender, + originalOfferItems, + originalConsiderationItems, + context + ) + ); + + // If call succeeds, try to decode offer and consideration items. + if (success) { + // Try to decode offer and consideration items from returndata. + try + (new ReferenceGenerateOrderReturndataDecoder()).decode( + returnData + ) + returns ( + SpentItem[] memory _offer, + ReceivedItem[] memory _consideration + ) { + offer = _offer; + consideration = _consideration; + } catch { + // If decoding fails, revert or return empty. + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + } else { + // If the call fails, revert or return empty. + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + } + + { + // Designate lengths. + uint256 originalOfferLength = orderParameters.offer.length; + uint256 newOfferLength = offer.length; + + // Explicitly specified offer items cannot be removed. + if (originalOfferLength > newOfferLength) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } else if (newOfferLength > originalOfferLength) { + // If new offer items are added, extend the original offer. + OfferItem[] memory extendedOffer = new OfferItem[]( + newOfferLength + ); + // Copy original offer items to new array. + for (uint256 i = 0; i < originalOfferLength; ++i) { + extendedOffer[i] = orderParameters.offer[i]; + } + // Update order parameters with extended offer. + orderParameters.offer = extendedOffer; + } + + // Loop through each new offer and ensure the new amounts are at + // least as much as the respective original amounts. + for (uint256 i = 0; i < originalOfferLength; ++i) { + // Designate original and new offer items. + OfferItem memory originalOffer = orderParameters.offer[i]; + SpentItem memory newOffer = offer[i]; + + // Set returned identifier for criteria-based items with + // criteria = 0. Note that this reset means that a contract + // offerer has full latitude to choose any identifier it wants + // mid-flight, in contrast to the normal behavior, where the + // fulfiller can pick which identifier to receive by providing a + // CriteriaResolver. + if ( + uint256(originalOffer.itemType) > 3 && + originalOffer.identifierOrCriteria == 0 + ) { + originalOffer.itemType = ItemType( + uint256(originalOffer.itemType) - 2 + ); + originalOffer.identifierOrCriteria = newOffer.identifier; + } + + // Ensure the original and generated items are compatible. + if ( + originalOffer.startAmount != originalOffer.endAmount || + originalOffer.endAmount > newOffer.amount || + originalOffer.itemType != newOffer.itemType || + originalOffer.token != newOffer.token || + originalOffer.identifierOrCriteria != newOffer.identifier + ) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + // Update the original amounts to use the generated amounts. + originalOffer.startAmount = newOffer.amount; + originalOffer.endAmount = newOffer.amount; + } + + // Add new offer items if there are more than original. + for (uint256 i = originalOfferLength; i < newOfferLength; ++i) { + OfferItem memory originalOffer = orderParameters.offer[i]; + SpentItem memory newOffer = offer[i]; + + originalOffer.itemType = newOffer.itemType; + originalOffer.token = newOffer.token; + originalOffer.identifierOrCriteria = newOffer.identifier; + originalOffer.startAmount = newOffer.amount; + originalOffer.endAmount = newOffer.amount; + } + } + + { + // Designate lengths & memory locations. + ConsiderationItem[] memory originalConsiderationArray = ( + orderParameters.consideration + ); + uint256 newConsiderationLength = consideration.length; + + // New consideration items cannot be created. + if (newConsiderationLength > originalConsiderationArray.length) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + // Loop through and check consideration. + for (uint256 i = 0; i < newConsiderationLength; ++i) { + ReceivedItem memory newConsideration = consideration[i]; + ConsiderationItem memory originalConsideration = ( + originalConsiderationArray[i] + ); + + if ( + uint256(originalConsideration.itemType) > 3 && + originalConsideration.identifierOrCriteria == 0 + ) { + originalConsideration.itemType = ItemType( + uint256(originalConsideration.itemType) - 2 + ); + originalConsideration + .identifierOrCriteria = newConsideration.identifier; + } + + // All fields must match the originally supplied fields except + // for the amount (which may be reduced by the contract offerer) + // and the recipient if not provided by the recipient. + if ( + originalConsideration.startAmount != + originalConsideration.endAmount || + newConsideration.amount > originalConsideration.endAmount || + originalConsideration.itemType != + newConsideration.itemType || + originalConsideration.token != newConsideration.token || + originalConsideration.identifierOrCriteria != + newConsideration.identifier || + (originalConsideration.recipient != address(0) && + originalConsideration.recipient != + (newConsideration.recipient)) + ) { + return _revertOrReturnEmpty(revertOnInvalid, orderHash); + } + + // Update the original amounts to use the generated amounts. + originalConsideration.startAmount = newConsideration.amount; + originalConsideration.endAmount = newConsideration.amount; + originalConsideration.recipient = newConsideration.recipient; + } + + // Shorten original consideration array if longer than new array. + ConsiderationItem[] memory shortenedConsiderationArray = ( + new ConsiderationItem[](newConsiderationLength) + ); + + // Iterate over original consideration array and copy to new. + for (uint256 i = 0; i < newConsiderationLength; ++i) { + shortenedConsiderationArray[i] = originalConsiderationArray[i]; + } + + // Replace original consideration array with new shortend array. + orderParameters.consideration = shortenedConsiderationArray; + } + + // Return the order hash, the numerator, and the denominator. + return (orderHash, 1, 1); } /** * @dev Internal function to cancel an arbitrary number of orders. Note that - * only the offerer or the zone of a given order may cancel it. + * only the offerer or the zone of a given order may cancel it. Callers + * should ensure that the intended order was cancelled by calling + * `getOrderStatus` and confirming that `isCancelled` returns `true`. + * Also note that contract orders are not cancellable. * * @param orders The orders to cancel. * * @return A boolean indicating whether the supplied orders were * successfully cancelled. */ - function _cancel(OrderComponents[] calldata orders) - internal - notEntered - returns (bool) - { + function _cancel( + OrderComponents[] calldata orders + ) internal returns (bool) { // Declare variables outside of the loop. OrderStatus storage orderStatus; address offerer; @@ -317,9 +542,13 @@ contract ReferenceOrderValidator is offerer = order.offerer; zone = order.zone; - // Ensure caller is either offerer or zone of the order. - if (msg.sender != offerer && msg.sender != zone) { - revert InvalidCanceller(); + // Ensure caller is either offerer or zone of the order and that the + // order is not a contract order. + if ( + order.orderType == OrderType.CONTRACT || + (msg.sender != offerer && msg.sender != zone) + ) { + revert CannotCancelOrder(); } // Derive order hash using the order parameters and the counter. @@ -350,6 +579,7 @@ contract ReferenceOrderValidator is // Emit an event signifying that the order has been cancelled. emit OrderCancelled(orderHash, offerer, zone); } + return true; } @@ -364,11 +594,7 @@ contract ReferenceOrderValidator is * @return A boolean indicating whether the supplied orders were * successfully validated. */ - function _validate(Order[] calldata orders) - internal - notEntered - returns (bool) - { + function _validate(Order[] calldata orders) internal returns (bool) { // Declare variables outside of the loop. OrderStatus storage orderStatus; bytes32 orderHash; @@ -385,6 +611,11 @@ contract ReferenceOrderValidator is // Retrieve the order parameters. OrderParameters calldata orderParameters = order.parameters; + // Skip contract orders. + if (orderParameters.orderType == OrderType.CONTRACT) { + continue; + } + // Move offerer from memory to the stack. offerer = orderParameters.offerer; @@ -406,6 +637,15 @@ contract ReferenceOrderValidator is // If the order has not already been validated... if (!orderStatus.isValidated) { + // Ensure that consideration array length is equal to the total + // original consideration items value. + if ( + orderParameters.consideration.length != + orderParameters.totalOriginalConsiderationItems + ) { + revert ConsiderationLengthNotEqualToTotalOriginal(); + } + // Verify the supplied signature. _verifySignature(offerer, orderHash, order.signature); @@ -413,7 +653,7 @@ contract ReferenceOrderValidator is orderStatus.isValidated = true; // Emit an event signifying the order has been validated. - emit OrderValidated(orderHash, offerer, orderParameters.zone); + emit OrderValidated(orderHash, orderParameters); } } @@ -437,7 +677,9 @@ contract ReferenceOrderValidator is * @return totalSize The total size of the order that is either filled or * unfilled (i.e. the "denominator"). */ - function _getOrderStatus(bytes32 orderHash) + function _getOrderStatus( + bytes32 orderHash + ) internal view returns ( @@ -459,6 +701,120 @@ contract ReferenceOrderValidator is ); } + /** + * @dev Internal pure function to either revert or return an empty tuple + * depending on the value of `revertOnInvalid`. + * + * @param revertOnInvalid Whether to revert on invalid input. + * @param contractOrderHash The contract order hash. + * + * @return orderHash The order hash. + * @return numerator The numerator. + * @return denominator The denominator. + */ + function _revertOrReturnEmpty( + bool revertOnInvalid, + bytes32 contractOrderHash + ) + internal + pure + returns (bytes32 orderHash, uint256 numerator, uint256 denominator) + { + // If invalid input should not revert... + if (!revertOnInvalid) { + // Return the contract order hash and zero values for the numerator + // and denominator. + return (contractOrderHash, 0, 0); + } + + // Otherwise, revert. + revert InvalidContractOrder(contractOrderHash); + } + + /** + * @dev Internal pure function to convert both offer and consideration items + * to spent items. + * + * @param offer The offer items to convert. + * @param consideration The consideration items to convert. + * + * @return spentItems The converted spent items. + * @return receivedItems The converted received items. + */ + function _convertToSpent( + OfferItem[] memory offer, + ConsiderationItem[] memory consideration + ) + internal + pure + returns ( + SpentItem[] memory spentItems, + SpentItem[] memory receivedItems + ) + { + // Create an array of spent items equal to the offer length. + spentItems = new SpentItem[](offer.length); + + // Iterate over each offer item on the order. + for (uint256 i = 0; i < offer.length; ++i) { + // Retrieve the offer item. + OfferItem memory offerItem = offer[i]; + + // Create spent item for event based on the offer item. + SpentItem memory spentItem = SpentItem( + offerItem.itemType, + offerItem.token, + offerItem.identifierOrCriteria, + offerItem.startAmount + ); + + // Add to array of spent items. + spentItems[i] = spentItem; + } + + // Create an array of received items equal to the consideration length. + receivedItems = new SpentItem[](consideration.length); + + // Iterate over each consideration item on the order. + for (uint256 i = 0; i < consideration.length; ++i) { + // Retrieve the consideration item. + ConsiderationItem memory considerationItem = (consideration[i]); + + // Create spent item for event based on the consideration item. + SpentItem memory receivedItem = SpentItem( + considerationItem.itemType, + considerationItem.token, + considerationItem.identifierOrCriteria, + considerationItem.startAmount + ); + + // Add to array of received items. + receivedItems[i] = receivedItem; + } + } + + /** + * @dev Internal function to derive the greatest common divisor of two + * values using the classical euclidian algorithm. + * + * @param a The first value. + * @param b The second value. + * + * @return greatestCommonDivisor The greatest common divisor. + */ + function _greatestCommonDivisor( + uint256 a, + uint256 b + ) internal pure returns (uint256 greatestCommonDivisor) { + while (b > 0) { + uint256 c = b; + b = a % c; + a = c; + } + + greatestCommonDivisor = a; + } + /** * @dev Internal pure function to check whether a given order type indicates * that partial fills are not supported (e.g. only "full fills" are @@ -469,11 +825,9 @@ contract ReferenceOrderValidator is * @return isFullOrder A boolean indicating whether the order type only * supports full fills. */ - function _doesNotSupportPartialFills(OrderType orderType) - internal - pure - returns (bool isFullOrder) - { + function _doesNotSupportPartialFills( + OrderType orderType + ) internal pure returns (bool isFullOrder) { // The "full" order types are even, while "partial" order types are odd. isFullOrder = uint256(orderType) & 1 == 0; } diff --git a/reference/lib/ReferenceReentrancyGuard.sol b/reference/lib/ReferenceReentrancyGuard.sol index 73fc0cb40..7bb4052e7 100644 --- a/reference/lib/ReferenceReentrancyGuard.sol +++ b/reference/lib/ReferenceReentrancyGuard.sol @@ -1,9 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { ReentrancyErrors } from "contracts/interfaces/ReentrancyErrors.sol"; +import { + ConsiderationEventsAndErrors +} from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; -import "contracts/lib/ConsiderationConstants.sol"; +import { + ReentrancyErrors +} from "../../contracts/interfaces/ReentrancyErrors.sol"; + +import { + _ENTERED_AND_ACCEPTING_NATIVE_TOKENS, + _ENTERED, + _NOT_ENTERED +} from "../../contracts/lib/ConsiderationConstants.sol"; /** * @title ReentrancyGuard @@ -11,7 +21,10 @@ import "contracts/lib/ConsiderationConstants.sol"; * @notice ReentrancyGuard contains a storage variable and related functionality * for protecting against reentrancy. */ -contract ReferenceReentrancyGuard is ReentrancyErrors { +contract ReferenceReentrancyGuard is + ConsiderationEventsAndErrors, + ReentrancyErrors +{ // Prevent reentrant calls on protected functions. uint256 private _reentrancyGuard; @@ -24,23 +37,48 @@ contract ReferenceReentrancyGuard is ReentrancyErrors { } /** - * @dev Modifier to set the reentrancy guard sentinel value for the duration - * of the call. + * @dev Modifier to check that the sentinel value for the reentrancy guard + * is not currently set by a previous call. */ - modifier nonReentrant() { - _reentrancyGuard = _ENTERED; + modifier notEntered() { + if (_reentrancyGuard != _NOT_ENTERED) { + revert NoReentrantCalls(); + } + _; - _reentrancyGuard = _NOT_ENTERED; } /** - * @dev Modifier to check that the sentinel value for the reentrancy guard - * is not currently set by a previous call. + * @dev Modifier to set the reentrancy guard sentinel value for the duration + * of the call and check if it is already set by a previous call. + * + * @param acceptNativeTokens A boolean indicating whether native tokens may + * be received during execution or not. */ - modifier notEntered() { - if (_reentrancyGuard == _ENTERED) { + modifier nonReentrant(bool acceptNativeTokens) { + if (_reentrancyGuard != _NOT_ENTERED) { revert NoReentrantCalls(); } + + if (acceptNativeTokens) { + _reentrancyGuard = _ENTERED_AND_ACCEPTING_NATIVE_TOKENS; + } else { + _reentrancyGuard = _ENTERED; + } + _; + + _reentrancyGuard = _NOT_ENTERED; + } + + /** + * @dev Internal view function to ensure that the sentinel value indicating + * native tokens may be received during execution is currently set. + */ + function _assertAcceptingNativeTokens() internal view { + // Ensure that the reentrancy guard is not currently set. + if (_reentrancyGuard != _ENTERED_AND_ACCEPTING_NATIVE_TOKENS) { + revert InvalidMsgValue(msg.value); + } } } diff --git a/reference/lib/ReferenceSignatureVerification.sol b/reference/lib/ReferenceSignatureVerification.sol index 73729e658..1954dc23e 100644 --- a/reference/lib/ReferenceSignatureVerification.sol +++ b/reference/lib/ReferenceSignatureVerification.sol @@ -1,14 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { EIP1271Interface } from "contracts/interfaces/EIP1271Interface.sol"; +import { + EIP1271Interface +} from "../../contracts/interfaces/EIP1271Interface.sol"; -// prettier-ignore import { SignatureVerificationErrors -} from "contracts/interfaces/SignatureVerificationErrors.sol"; +} from "../../contracts/interfaces/SignatureVerificationErrors.sol"; -import "contracts/lib/ConsiderationConstants.sol"; +import { + EIP2098_allButHighestBitMask +} from "../../contracts/lib/ConsiderationConstants.sol"; /** * @title SignatureVerification @@ -19,19 +22,23 @@ contract ReferenceSignatureVerification is SignatureVerificationErrors { /** * @dev Internal view function to verify the signature of an order. An * ERC-1271 fallback will be attempted if either the signature length - * is not 32 or 33 bytes or if the recovered signer does not match the - * supplied signer. Note that in cases where a 32 or 33 byte signature + * is not 64 or 65 bytes or if the recovered signer does not match the + * supplied signer. Note that in cases where a 64 or 65 byte signature * is supplied, only standard ECDSA signatures that recover to a * non-zero address are supported. * - * @param signer The signer for the order. - * @param digest The digest to verify the signature against. - * @param signature A signature from the signer indicating that the order - * has been approved. + * @param signer The signer for the order. + * @param digest The digest to verify signature against. + * @param originalDigest The original digest to verify signature against. + * @param originalSignature The original signature. + * @param signature A signature from the signer indicating that the + * order has been approved. */ function _assertValidSignature( address signer, bytes32 digest, + bytes32 originalDigest, + bytes memory originalSignature, bytes memory signature ) internal view { // Declare r, s, and v signature parameters. @@ -41,19 +48,26 @@ contract ReferenceSignatureVerification is SignatureVerificationErrors { if (signer.code.length > 0) { // If signer is a contract, try verification via EIP-1271. - _assertValidEIP1271Signature(signer, digest, signature); + _assertValidEIP1271Signature( + signer, + originalDigest, + originalSignature + ); // Return early if the ERC-1271 signature check succeeded. return; } else if (signature.length == 64) { - // If signature contains 64 bytes, parse as EIP-2098 signature. (r+s&v) + // If signature contains 64 bytes, parse as EIP-2098 sig. (r+s&v) // Declare temporary vs that will be decomposed into s and v. bytes32 vs; + // Decode signature into r, vs. (r, vs) = abi.decode(signature, (bytes32, bytes32)); + // Decompose vs into s and v. s = vs & EIP2098_allButHighestBitMask; + // If the highest bit is set, v = 28, otherwise v = 27. v = uint8(uint256(vs >> 255)) + 27; } else if (signature.length == 65) { (r, s) = abi.decode(signature, (bytes32, bytes32)); @@ -71,12 +85,9 @@ contract ReferenceSignatureVerification is SignatureVerificationErrors { address recoveredSigner = ecrecover(digest, v, r, s); // Disallow invalid signers. - if (recoveredSigner == address(0)) { + if (recoveredSigner == address(0) || recoveredSigner != signer) { revert InvalidSigner(); // Should a signer be recovered, but it doesn't match the signer... - } else if (recoveredSigner != signer) { - // Attempt EIP-1271 static call to signer in case it's a contract. - _assertValidEIP1271Signature(signer, digest, signature); } } @@ -98,7 +109,7 @@ contract ReferenceSignatureVerification is SignatureVerificationErrors { EIP1271Interface(signer).isValidSignature(digest, signature) != EIP1271Interface.isValidSignature.selector ) { - revert InvalidSigner(); + revert BadContractSignature(); } } } diff --git a/reference/lib/ReferenceTokenTransferrer.sol b/reference/lib/ReferenceTokenTransferrer.sol index bce04c853..054220474 100644 --- a/reference/lib/ReferenceTokenTransferrer.sol +++ b/reference/lib/ReferenceTokenTransferrer.sol @@ -1,16 +1,15 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import "contracts/lib/ConsiderationConstants.sol"; - -// prettier-ignore import { ERC20Interface, ERC721Interface, ERC1155Interface -} from "contracts/interfaces/AbridgedTokenInterfaces.sol"; +} from "../../contracts/interfaces/AbridgedTokenInterfaces.sol"; -import { TokenTransferrerErrors } from "contracts/interfaces/TokenTransferrerErrors.sol"; +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; contract ReferenceTokenTransferrer is TokenTransferrerErrors { /** @@ -29,10 +28,12 @@ contract ReferenceTokenTransferrer is TokenTransferrerErrors { address to, uint256 amount ) internal { + // If the provided token is not a contract, revert. if (token.code.length == 0) { revert NoContract(token); } + // Attempt to transfer the tokens. (bool ok, bytes memory data) = token.call( abi.encodeWithSelector( ERC20Interface.transferFrom.selector, @@ -47,6 +48,7 @@ contract ReferenceTokenTransferrer is TokenTransferrerErrors { revert TokenTransferGenericFailure(token, from, to, 0, amount); } + // If the token does not return a boolean, revert. if (data.length != 0 && data.length >= 32) { if (!abi.decode(data, (bool))) { revert BadReturnValueFromERC20OnTransfer( @@ -75,6 +77,7 @@ contract ReferenceTokenTransferrer is TokenTransferrerErrors { address to, uint256 identifier ) internal { + // If the provided token is not a contract, revert. if (token.code.length == 0) { revert NoContract(token); } @@ -102,6 +105,7 @@ contract ReferenceTokenTransferrer is TokenTransferrerErrors { uint256 identifier, uint256 amount ) internal { + // If the provided token is not a contract, revert. if (token.code.length == 0) { revert NoContract(token); } @@ -135,6 +139,7 @@ contract ReferenceTokenTransferrer is TokenTransferrerErrors { uint256[] memory identifiers, uint256[] memory amounts ) internal { + // If the provided token is not a contract, revert. if (token.code.length == 0) { revert NoContract(token); } diff --git a/reference/lib/ReferenceVerifiers.sol b/reference/lib/ReferenceVerifiers.sol index 5615eff38..9874e070b 100644 --- a/reference/lib/ReferenceVerifiers.sol +++ b/reference/lib/ReferenceVerifiers.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { OrderStatus } from "contracts/lib/ConsiderationStructs.sol"; +import { OrderStatus } from "../../contracts/lib/ConsiderationStructs.sol"; import { ReferenceAssertions } from "./ReferenceAssertions.sol"; -import { ReferenceSignatureVerification } from "./ReferenceSignatureVerification.sol"; +import { + ReferenceSignatureVerification +} from "./ReferenceSignatureVerification.sol"; /** * @title Verifiers @@ -24,9 +26,9 @@ contract ReferenceVerifiers is * that may optionally be used to transfer approved * ERC20/721/1155 tokens. */ - constructor(address conduitController) - ReferenceAssertions(conduitController) - {} + constructor( + address conduitController + ) ReferenceAssertions(conduitController) {} /** * @dev Internal view function to ensure that the current time falls within @@ -48,7 +50,7 @@ contract ReferenceVerifiers is if (startTime > block.timestamp || endTime <= block.timestamp) { // Only revert if revertOnInvalid has been supplied as true. if (revertOnInvalid) { - revert InvalidTime(); + revert InvalidTime(startTime, endTime); } // Return false as the order is invalid. @@ -62,8 +64,8 @@ contract ReferenceVerifiers is /** * @dev Internal view function to verify the signature of an order. An * ERC-1271 fallback will be attempted if either the signature length - * is not 32 or 33 bytes or if the recovered signer does not match the - * supplied offerer. Note that in cases where a 32 or 33 byte signature + * is not 64 or 65 bytes or if the recovered signer does not match the + * supplied offerer. Note that in cases where a 64 or 65 byte signature * is supplied, only standard ECDSA signatures that recover to a * non-zero address are supported. * @@ -82,11 +84,139 @@ contract ReferenceVerifiers is return; } - // Derive EIP-712 digest using the domain separator and the order hash. - bytes32 digest = _deriveEIP712Digest(_domainSeparator(), orderHash); + bytes32 domainSeparator = _domainSeparator(); + + // Derive original EIP-712 digest using domain separator and order hash. + bytes32 originalDigest = _deriveEIP712Digest( + domainSeparator, + orderHash + ); + + bytes32 digest; + bytes memory extractedSignature; + if (_isValidBulkOrderSize(signature)) { + // Rederive order hash and digest using bulk order proof. + (orderHash, extractedSignature) = _computeBulkOrderProof( + signature, + orderHash + ); + digest = _deriveEIP712Digest(domainSeparator, orderHash); + } else { + digest = originalDigest; + extractedSignature = signature; + } // Ensure that the signature for the digest is valid for the offerer. - _assertValidSignature(offerer, digest, signature); + _assertValidSignature( + offerer, + digest, + originalDigest, + signature, + extractedSignature + ); + } + + /** + * @dev Determines whether the specified bulk order size is valid. + * + * @param signature The signature of the bulk order to check. + * + * @return validLength True if bulk order size is valid, false otherwise. + */ + function _isValidBulkOrderSize( + bytes memory signature + ) internal pure returns (bool validLength) { + validLength = + signature.length < 837 && + signature.length > 98 && + ((signature.length - 67) % 32) < 2; + } + + /** + * @dev Computes the bulk order hash for the specified proof and leaf. Note + * that if an index that exceeds the number of orders in the bulk order + * payload will instead "wrap around" and refer to an earlier index. + * + * @param proofAndSignature The proof and signature of the bulk order. + * @param leaf The leaf of the bulk order tree. + * + * @return bulkOrderHash The bulk order hash. + * @return signature The signature of the bulk order. + */ + function _computeBulkOrderProof( + bytes memory proofAndSignature, + bytes32 leaf + ) internal view returns (bytes32 bulkOrderHash, bytes memory signature) { + bytes32 root = leaf; + + // proofAndSignature with odd length is a compact signature (64 bytes). + uint256 length = proofAndSignature.length % 2 == 0 ? 65 : 64; + + // Create a new array of bytes equal to the length of the signature. + signature = new bytes(length); + + // Iterate over each byte in the signature. + for (uint256 i = 0; i < length; ++i) { + // Assign the byte from the proofAndSignature to the signature. + signature[i] = proofAndSignature[i]; + } + + // Compute the key by extracting the next three bytes from the + // proofAndSignature. + uint256 key = (((uint256(uint8(proofAndSignature[length])) << 16) | + ((uint256(uint8(proofAndSignature[length + 1]))) << 8)) | + (uint256(uint8(proofAndSignature[length + 2])))); + + uint256 height = (proofAndSignature.length - length) / 32; + + // Create an array of bytes32 to hold the proof elements. + bytes32[] memory proofElements = new bytes32[](height); + + // Iterate over each proof element. + for (uint256 elementIndex = 0; elementIndex < height; ++elementIndex) { + // Compute the starting index for the current proof element. + uint256 start = (length + 3) + (elementIndex * 32); + + // Create a new array of bytes to hold the current proof element. + bytes memory buffer = new bytes(32); + + // Iterate over each byte in the proof element. + for (uint256 i = 0; i < 32; ++i) { + // Assign the byte from the proofAndSignature to the buffer. + buffer[i] = proofAndSignature[start + i]; + } + + // Decode the current proof element from the buffer and assign it to + // the proofElements array. + proofElements[elementIndex] = abi.decode(buffer, (bytes32)); + } + + // Iterate over each proof element. + for (uint256 i = 0; i < proofElements.length; ++i) { + // Retrieve the proof element. + bytes32 proofElement = proofElements[i]; + + // Check if the current bit of the key is set. + if ((key >> i) % 2 == 0) { + // If the current bit is not set, then concatenate the root and + // the proof element, and compute the keccak256 hash of the + // concatenation to assign it to the root. + root = keccak256(abi.encodePacked(root, proofElement)); + } else { + // If the current bit is set, then concatenate the proof element + // and the root, and compute the keccak256 hash of the + // concatenation to assign it to the root. + root = keccak256(abi.encodePacked(proofElement, root)); + } + } + + // Compute the bulk order hash and return it. + bulkOrderHash = keccak256( + abi.encodePacked(_bulkOrderTypehashes[height], root) + ); + + // Return the signature. + return (bulkOrderHash, signature); } /** diff --git a/reference/lib/ReferenceZoneInteraction.sol b/reference/lib/ReferenceZoneInteraction.sol index dc9578f0b..59f8ddbbe 100644 --- a/reference/lib/ReferenceZoneInteraction.sol +++ b/reference/lib/ReferenceZoneInteraction.sol @@ -1,19 +1,31 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; -import { ZoneInterface } from "contracts/interfaces/ZoneInterface.sol"; +import { ZoneInterface } from "../../contracts/interfaces/ZoneInterface.sol"; -import { OrderType } from "contracts/lib/ConsiderationEnums.sol"; +import { + ContractOffererInterface +} from "../../contracts/interfaces/ContractOffererInterface.sol"; + +import { + ItemType, + OrderType +} from "../../contracts/lib/ConsiderationEnums.sol"; -// prettier-ignore -import { AdvancedOrder, CriteriaResolver } from "contracts/lib/ConsiderationStructs.sol"; +import { + AdditionalRecipient, + AdvancedOrder, + BasicOrderParameters, + ReceivedItem, + SpentItem, + ZoneParameters +} from "../../contracts/lib/ConsiderationStructs.sol"; -import "contracts/lib/ConsiderationConstants.sol"; +import { OrderToExecute } from "./ReferenceConsiderationStructs.sol"; -// prettier-ignore import { ZoneInteractionErrors -} from "contracts/interfaces/ZoneInteractionErrors.sol"; +} from "../../contracts/interfaces/ZoneInteractionErrors.sol"; /** * @title ZoneInteraction @@ -27,32 +39,56 @@ contract ReferenceZoneInteraction is ZoneInteractionErrors { * are the fulfiller or that a staticcall to `isValidOrder` on the zone * returns a magic value indicating that the order is currently valid. * - * @param orderHash The hash of the order. - * @param zoneHash The hash to provide upon calling the zone. - * @param orderType The type of the order. - * @param offerer The offerer in question. - * @param zone The zone in question. + * @param orderHash The hash of the order. + * @param basicOrderParameters The original basic order parameters. + * @param offeredItemType The type of the order. + * @param receivedItemType The offerer in question. */ function _assertRestrictedBasicOrderValidity( bytes32 orderHash, - bytes32 zoneHash, OrderType orderType, - address offerer, - address zone - ) internal view { + BasicOrderParameters calldata basicOrderParameters, + ItemType offeredItemType, + ItemType receivedItemType + ) internal { + // Create a new array for the hash. + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = orderHash; + + // Convert the order params and types to spent and received items. + ( + SpentItem[] memory offer, + ReceivedItem[] memory consideration + ) = _convertToSpentAndReceivedItems( + basicOrderParameters, + offeredItemType, + receivedItemType + ); + // Order type 2-3 require zone or offerer be caller or zone to approve. + // Note that in cases where fulfiller == zone, the restricted order + // validation will be skipped. if ( - uint256(orderType) > 1 && - msg.sender != zone && - msg.sender != offerer + (orderType == OrderType.FULL_RESTRICTED || + orderType == OrderType.PARTIAL_RESTRICTED) && + msg.sender != basicOrderParameters.zone ) { + // Validate the order with the zone. if ( - ZoneInterface(zone).isValidOrder( - orderHash, - msg.sender, - offerer, - zoneHash - ) != ZoneInterface.isValidOrder.selector + ZoneInterface(basicOrderParameters.zone).validateOrder( + ZoneParameters({ + orderHash: orderHash, + fulfiller: msg.sender, + offerer: basicOrderParameters.offerer, + offer: offer, + consideration: consideration, + extraData: "", + orderHashes: orderHashes, + startTime: basicOrderParameters.startTime, + endTime: basicOrderParameters.endTime, + zoneHash: basicOrderParameters.zoneHash + }) + ) != ZoneInterface.validateOrder.selector ) { revert InvalidRestrictedOrder(orderHash); } @@ -64,69 +100,127 @@ contract ReferenceZoneInteraction is ZoneInteractionErrors { * for a given order and to ensure that the submitter is allowed by the * order type. * - * @param advancedOrder The order in question. - * @param criteriaResolvers An array where each element contains a reference - * to a specific offer or consideration, a token - * identifier, and a proof that the supplied token - * identifier is contained in the order's merkle - * root. Note that a criteria of zero indicates - * that any (transferable) token identifier is - * valid and that no proof needs to be supplied. - * @param priorOrderHashes The order hashes of each order supplied prior to - * the current order as part of a "match" variety - * of order fulfillment (e.g. this array will be - * empty for single or "fulfill available"). - * @param orderHash The hash of the order. - * @param zoneHash The hash to provide upon calling the zone. - * @param orderType The type of the order. - * @param offerer The offerer in question. - * @param zone The zone in question. - + * @param advancedOrder The order in question. + * @param orderHashes The order hashes of each order supplied alongside + * the current order as part of a "match" or "fulfill + * available" variety of order fulfillment. + * @param orderHash The hash of the order. + * @param zoneHash The hash to provide upon calling the zone. + * @param orderType The type of the order. + * @param offerer The offerer in question. + * @param zone The zone in question. */ function _assertRestrictedAdvancedOrderValidity( AdvancedOrder memory advancedOrder, - CriteriaResolver[] memory criteriaResolvers, - bytes32[] memory priorOrderHashes, + OrderToExecute memory orderToExecute, + bytes32[] memory orderHashes, bytes32 orderHash, bytes32 zoneHash, OrderType orderType, address offerer, address zone - ) internal view { + ) internal { // Order type 2-3 require zone or offerer be caller or zone to approve. if ( - uint256(orderType) > 1 && - msg.sender != zone && - msg.sender != offerer + (orderType == OrderType.FULL_RESTRICTED || + orderType == OrderType.PARTIAL_RESTRICTED) && msg.sender != zone ) { - // If no extraData or criteria resolvers are supplied... + // Validate the order. + if ( + ZoneInterface(zone).validateOrder( + ZoneParameters({ + orderHash: orderHash, + fulfiller: msg.sender, + offerer: offerer, + offer: orderToExecute.spentItems, + consideration: orderToExecute.receivedItems, + extraData: advancedOrder.extraData, + orderHashes: orderHashes, + startTime: advancedOrder.parameters.startTime, + endTime: advancedOrder.parameters.endTime, + zoneHash: zoneHash + }) + ) != ZoneInterface.validateOrder.selector + ) { + revert InvalidRestrictedOrder(orderHash); + } + } else if (orderType == OrderType.CONTRACT) { + // Ratify the contract order. if ( - advancedOrder.extraData.length == 0 && - criteriaResolvers.length == 0 + ContractOffererInterface(offerer).ratifyOrder( + orderToExecute.spentItems, + orderToExecute.receivedItems, + advancedOrder.extraData, + orderHashes, + uint96(uint256(orderHash)) + ) != ContractOffererInterface.ratifyOrder.selector ) { - if ( - ZoneInterface(zone).isValidOrder( - orderHash, - msg.sender, - offerer, - zoneHash - ) != ZoneInterface.isValidOrder.selector - ) { - revert InvalidRestrictedOrder(orderHash); - } - } else { - if ( - ZoneInterface(zone).isValidOrderIncludingExtraData( - orderHash, - msg.sender, - advancedOrder, - priorOrderHashes, - criteriaResolvers - ) != ZoneInterface.isValidOrder.selector - ) { - revert InvalidRestrictedOrder(orderHash); - } + revert InvalidContractOrder(orderHash); } } } + + /** + * @dev Converts the offer and consideration parameters from a + * BasicOrderParameters object into an array of SpentItem and + * ReceivedItem objects. + * + * @param parameters The BasicOrderParameters object containing + * the offer and consideration parameters to be + * converted. + * @param offerItemType The item type of the offer. + * @param considerationItemType The item type of the consideration. + * + * @return spentItems The converted offer parameters as an array + * of SpentItem objects. + * @return receivedItems The converted consideration parameters as an + * array of ReceivedItem objects. + */ + function _convertToSpentAndReceivedItems( + BasicOrderParameters calldata parameters, + ItemType offerItemType, + ItemType considerationItemType + ) internal pure returns (SpentItem[] memory, ReceivedItem[] memory) { + // Create the spent item. + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({ + itemType: offerItemType, + token: parameters.offerToken, + amount: parameters.offerAmount, + identifier: parameters.offerIdentifier + }); + + // Create the received item. + ReceivedItem[] memory receivedItems = new ReceivedItem[]( + 1 + parameters.additionalRecipients.length + ); + address token = parameters.considerationToken; + uint256 amount = parameters.considerationAmount; + uint256 identifier = parameters.considerationIdentifier; + receivedItems[0] = ReceivedItem({ + itemType: considerationItemType, + token: token, + amount: amount, + identifier: identifier, + recipient: parameters.offerer + }); + + // Iterate through the additional recipients and create the received + // items. + for (uint256 i = 0; i < parameters.additionalRecipients.length; i++) { + AdditionalRecipient calldata additionalRecipient = parameters + .additionalRecipients[i]; + amount = additionalRecipient.amount; + receivedItems[i + 1] = ReceivedItem({ + itemType: considerationItemType, + token: token, + amount: amount, + identifier: identifier, + recipient: additionalRecipient.recipient + }); + } + + // Return the spent and received items. + return (spentItems, receivedItems); + } } diff --git a/reference/shim/Shim.sol b/reference/shim/Shim.sol index 46fd7f5c7..9abdd9933 100644 --- a/reference/shim/Shim.sol +++ b/reference/shim/Shim.sol @@ -1,19 +1,48 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.13; /** * @dev HardHat doesn't support multiple source folders; so import everything * extra that reference tests rely on so they get compiled. Allows for faster * feedback than running an extra yarn build */ -import { EIP1271Wallet } from "contracts/test/EIP1271Wallet.sol"; -import { Reenterer } from "contracts/test/Reenterer.sol"; -import { TestERC20 } from "contracts/test/TestERC20.sol"; -import { TestERC721 } from "contracts/test/TestERC721.sol"; -import { TestERC1155 } from "contracts/test/TestERC1155.sol"; -import { TestZone } from "contracts/test/TestZone.sol"; -import { TransferHelper } from "contracts/helpers/TransferHelper.sol"; -// prettier-ignore +import { EIP1271Wallet } from "../../contracts/test/EIP1271Wallet.sol"; +import { Reenterer } from "../../contracts/test/Reenterer.sol"; +import { TestERC20 } from "../../contracts/test/TestERC20.sol"; +import { TestERC721 } from "../../contracts/test/TestERC721.sol"; +import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; +import { TestZone } from "../../contracts/test/TestZone.sol"; +import { TestPostExecution } from "../../contracts/test/TestPostExecution.sol"; +import { + TestContractOfferer +} from "../../contracts/test/TestContractOfferer.sol"; +import { + TestContractOffererNativeToken +} from "../../contracts/test/TestContractOffererNativeToken.sol"; +import { + TestBadContractOfferer +} from "../../contracts/test/TestBadContractOfferer.sol"; +import { + TestInvalidContractOfferer +} from "../../contracts/test/TestInvalidContractOfferer.sol"; +import { + TestInvalidContractOffererRatifyOrder +} from "../../contracts/test/TestInvalidContractOffererRatifyOrder.sol"; +import { + PausableZoneController +} from "../../contracts/zones/PausableZoneController.sol"; +import { TransferHelper } from "../../contracts/helpers/TransferHelper.sol"; +import { + InvalidERC721Recipient +} from "../../contracts/test/InvalidERC721Recipient.sol"; +import { + ERC721ReceiverMock +} from "../../contracts/test/ERC721ReceiverMock.sol"; +import { TestERC20Panic } from "../../contracts/test/TestERC20Panic.sol"; +import { + ConduitControllerMock +} from "../../contracts/test/ConduitControllerMock.sol"; +import { ConduitMock } from "../../contracts/test/ConduitMock.sol"; import { ImmutableCreate2FactoryInterface -} from "contracts/interfaces/ImmutableCreate2FactoryInterface.sol"; +} from "../../contracts/interfaces/ImmutableCreate2FactoryInterface.sol"; diff --git a/remappings.txt b/remappings.txt index 44817140a..d56027ba4 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,5 @@ -murky/=./lib/murky/src/ -ds-test/=./lib/ds-test/src/ -solmate/=./lib/solmate/src/ -forge-std/=./lib/forge-std/src/ -@rari-capital/solmate=./lib/solmate/ \ No newline at end of file +@rari-capital/solmate=lib/solmate/ +ds-test/=lib/ds-test/src/ +forge-std/=lib/forge-std/src/ +murky/=lib/murky/src/ +openzeppelin-contracts/=lib/openzeppelin-contracts/ \ No newline at end of file diff --git a/script/TransferHelperDeployer.s.sol b/script/TransferHelperDeployer.s.sol new file mode 100644 index 000000000..4c3973fd0 --- /dev/null +++ b/script/TransferHelperDeployer.s.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +import "forge-std/Script.sol"; +import { TransferHelper } from "../contracts/helpers/TransferHelper.sol"; + +interface ImmutableCreate2Factory { + function safeCreate2(bytes32, bytes memory) external; +} + +contract TransferHelperDeployer is Script { + function setUp() public {} + + function run() public { + vm.broadcast(); + ImmutableCreate2Factory factory = ImmutableCreate2Factory( + 0x0000000000FFe8B47B3e2130213B802212439497 + ); + bytes32 salt = bytes32(0); + factory.safeCreate2( + salt, + abi.encodePacked( + type(TransferHelper).creationCode, + abi.encode(address(0x00000000F9490004C11Cef243f5400493c00Ad63)) + ) + ); + } +} diff --git a/scripts/comment-table.ts b/scripts/comment-table.ts new file mode 100644 index 000000000..5eb1baefe --- /dev/null +++ b/scripts/comment-table.ts @@ -0,0 +1,77 @@ +import chalk from "chalk"; + +export const err = chalk.bold.red; +export const warn = chalk.hex("#FFA500"); +export const info = chalk.blue; +export const success = chalk.green; + +export function diffPctString( + newValue: number, + oldValue: number, + warnOnIncrease?: boolean, + diffOnly?: boolean +): string { + if (newValue === null && oldValue === null) { + return warn("null"); + } + const diff = newValue - oldValue; + + if (diff === 0) return info(newValue.toString()); + const pct = +((100 * diff) / oldValue).toFixed(2); + const pctPrefix = pct > 0 ? "+" : ""; + const color = diff > 0 ? (warnOnIncrease ? warn : err) : success; + const valuePrefix = diffOnly && diff > 0 ? "+" : ""; + const value = diffOnly ? diff : newValue; + return `${valuePrefix}${value} (${color(`${pctPrefix}${pct}%`)})`; +} +// eslint-disable-next-line no-control-regex +const stripANSI = (str: string) => str.replace(/\u001b\[.*?m/g, ""); + +export function getColumnSizesAndAlignments( + rows: string[][], + padding = 0 +): Array<[number, boolean]> { + const sizesAndAlignments: Array<[number, boolean]> = []; + const numColumns = rows[0].length; + for (let i = 0; i < numColumns; i++) { + const entries = rows.map((row) => stripANSI(row[i])); + const maxSize = Math.max(...entries.map((e) => e.length)); + const alignLeft = entries + .slice(1) + .filter((e) => !e.includes("null")) + .some((e) => !!e.match(/[a-zA-Z]/g)); + sizesAndAlignments.push([maxSize + padding, alignLeft]); + } + return sizesAndAlignments; +} + +const padColumn = ( + col: string, + size: number, + padWith: string, + alignLeft: boolean +) => { + const padSize = Math.max(0, size - stripANSI(col).length); + const padding = padWith.repeat(padSize); + if (alignLeft) return `${col}${padding}`; + return `${padding}${col}`; +}; + +export const toCommentTable = (rows: string[][]): string[] => { + const sizesAndAlignments = getColumnSizesAndAlignments(rows); + rows.forEach((row) => { + row.forEach((col, c) => { + const [size, alignLeft] = sizesAndAlignments[c]; + row[c] = padColumn(col, size, " ", alignLeft); + }); + }); + + const completeRows = rows.map((row) => `| ${row.join(" | ")} |`); + const rowSeparator = `==${sizesAndAlignments + .map(([size]) => "=".repeat(size)) + .join("===")}==`; + completeRows.splice(1, 0, rowSeparator); + completeRows.unshift(rowSeparator); + completeRows.push(rowSeparator); + return completeRows; +}; diff --git a/scripts/compare_reports.ts b/scripts/compare_reports.ts new file mode 100644 index 000000000..6e7f2e3a4 --- /dev/null +++ b/scripts/compare_reports.ts @@ -0,0 +1,82 @@ +import { diffPctString, toCommentTable } from "./comment-table"; +import { printLastReport } from "./print_report"; +import { getAllReports } from "./utils"; +import { writeReports } from "./write_reports"; + +import type { ContractReport } from "./utils"; +import type { HardhatRuntimeEnvironment } from "hardhat/types"; + +export function compareReports( + oldReport: ContractReport, + newReport: ContractReport +) { + const rows: string[][] = []; + rows.push([`method`, `min`, `max`, `avg`, `calls`]); + oldReport.methods.forEach((r1, i) => { + const r2 = newReport.methods[i]; + rows.push([ + r1.method, + diffPctString(r2.min, r1.min, false, true), + diffPctString(r2.max, r1.max, false, true), + diffPctString(r2.avg, r1.avg, false, true), + diffPctString(r2.calls, r1.calls, false, true), + ]); + }); + const { bytecodeSize: initSize1, deployedBytecodeSize: size1 } = oldReport; + const { bytecodeSize: initSize2, deployedBytecodeSize: size2 } = newReport; + rows.push([ + `runtime size`, + diffPctString(size2, size1, false, true), + "", + "", + "", + ]); + rows.push([ + `init code size`, + diffPctString(initSize2, initSize1, false, true), + "", + "", + "", + ]); + const table = toCommentTable(rows); + const separator = table[0]; + table.splice(table.length - 3, 0, separator); + console.log(table.join("\n")); +} + +export function compareLastTwoReports(hre: HardhatRuntimeEnvironment) { + writeReports(hre); + const reports = getAllReports(); + if (reports.length === 1) { + printLastReport(hre); + return; + } + if (reports.length < 2) { + return; + } + const [currentReport, previousReport] = reports; + const contractName = "Seaport"; + compareReports( + previousReport.contractReports[contractName], + currentReport.contractReports[contractName] + ); + const ts = Math.floor(Date.now() / 1000); + const currentSuffix = + currentReport.name !== currentReport.commitHash + ? ` @ ${currentReport.commitHash}` + : ""; + const previousSuffix = + previousReport.name !== previousReport.commitHash + ? ` @ ${previousReport.commitHash}` + : ""; + console.log( + `Current Report: ${+((currentReport.timestamp - ts) / 60).toFixed( + 2 + )} min ago (${currentReport.name})${currentSuffix}` + ); + console.log( + `Previous Report: ${+((previousReport.timestamp - ts) / 60).toFixed( + 2 + )} min ago (${previousReport.name})${previousSuffix}` + ); +} diff --git a/scripts/print_report.ts b/scripts/print_report.ts new file mode 100644 index 000000000..701fb42f3 --- /dev/null +++ b/scripts/print_report.ts @@ -0,0 +1,54 @@ +import { err, toCommentTable, warn } from "./comment-table"; +import { getAllReports } from "./utils"; +import { writeReports } from "./write_reports"; + +import type { ContractReport } from "./utils"; +import type { HardhatRuntimeEnvironment } from "hardhat/types"; + +export function printReport(report: ContractReport) { + const rows: string[][] = []; + rows.push([`method`, `min`, `max`, `avg`, `calls`]); + report.methods.forEach(({ method, min, max, avg, calls }) => { + rows.push([ + method, + min?.toString() ?? warn("null"), + max?.toString() ?? warn("null"), + avg?.toString() ?? warn("null"), + calls?.toString() ?? warn("null"), + ]); + }); + rows.push([ + `runtime size`, + report.deployedBytecodeSize.toString(), + "", + "", + "", + ]); + rows.push([`init code size`, report.bytecodeSize.toString(), "", "", ""]); + const table = toCommentTable(rows); + const separator = table[0]; + table.splice(table.length - 3, 0, separator); + console.log(table.join("\n")); +} + +export function printLastReport(hre: HardhatRuntimeEnvironment) { + writeReports(hre); + const reports = getAllReports(); + if (reports.length < 1) { + console.log(err(`No gas reports found`)); + return; + } + const contractName = "Seaport"; + const [currentReport] = reports; + printReport(currentReport.contractReports[contractName]); + const ts = Math.floor(Date.now() / 1000); + const suffix = + currentReport.name !== currentReport.commitHash + ? ` @ ${currentReport.commitHash}` + : ""; + console.log( + `Current Report: ${+((currentReport.timestamp - ts) / 60).toFixed( + 2 + )} min ago (${currentReport.name}) ${suffix}` + ); +} diff --git a/scripts/utils.ts b/scripts/utils.ts new file mode 100644 index 000000000..f316bb8a0 --- /dev/null +++ b/scripts/utils.ts @@ -0,0 +1,136 @@ +import { execSync } from "child_process"; +import fs from "fs"; +import path from "path"; + +export const GAS_REPORTS_DIR = path.join(__dirname, "../.gas_reports"); + +if (!fs.existsSync(GAS_REPORTS_DIR)) { + fs.mkdirSync(GAS_REPORTS_DIR); +} + +export type RawMethodReport = { + contract: string; + method: string; + min: number; + max: number; + avg: number; + calls: number; +}; + +export type RawGasReport = { + name: string; + path: string; + timestamp: number; + report: RawMethodReport[]; +}; + +export type MethodReport = { + method: string; + min: number; + max: number; + avg: number; + calls: number; +}; + +export type ContractReport = { + name: string; + deployedBytecodeSize: number; + bytecodeSize: number; + methods: MethodReport[]; +}; + +export type CommitGasReport = { + commitHash: string; + contractReports: Record; +}; + +export function getCommitHash() { + return execSync("git rev-parse HEAD").toString().trim(); +} + +export function getReportPathForCommit(commit?: string): string { + if (!commit) { + commit = getCommitHash(); + } + return path.join(GAS_REPORTS_DIR, `${commit}.md`); +} + +export function haveReportForCurrentCommit(): boolean { + return fs.existsSync(getReportPathForCommit()); +} + +export function fileLastUpdate(filePath: string): number { + let timestamp = parseInt( + execSync(`git log -1 --pretty="format:%ct" ${filePath}`) + .toString() + .trim() || "0" + ); + if (!timestamp) { + timestamp = Math.floor(+fs.statSync(filePath).mtime / 1000); + } + return timestamp; +} + +function parseRawReport(text: string): RawMethodReport[] { + const lines = text + .split("\n") + .slice(6) + .filter((ln) => ln.indexOf("·") !== 0); + const rows = lines + .map((ln) => ln.replace(/\|/g, "").replace(/\s/g, "").split("·")) + .filter((row) => row.length === 7) + .map(([contract, method, min, max, avg, calls]) => ({ + contract, + method, + min: +min, + max: +max, + avg: +avg, + calls: +calls, + })); + return rows; +} + +export function getAllRawReports(): RawGasReport[] { + const reports = fs + .readdirSync(GAS_REPORTS_DIR) + .filter((file) => path.extname(file) === ".md") + .map((file) => { + const reportPath = path.join(GAS_REPORTS_DIR, file); + const timestamp = fileLastUpdate(reportPath); + const text = fs.readFileSync(reportPath, "utf8"); + const report = parseRawReport(text); + return { + name: path.parse(file).name, + path: reportPath, + timestamp, + report, + }; + }); + + reports.sort((a, b) => b.timestamp - a.timestamp); + return reports; +} + +export function getAllReports(): (CommitGasReport & { + name: string; + path: string; + timestamp: number; +})[] { + const reports = fs + .readdirSync(GAS_REPORTS_DIR) + .filter((file) => path.extname(file) === ".json") + .map((file) => { + const reportPath = path.join(GAS_REPORTS_DIR, file); + const timestamp = fileLastUpdate(reportPath); + const report = require(reportPath) as CommitGasReport; + return { + name: path.parse(file).name, + path: reportPath, + timestamp, + ...report, + }; + }); + + reports.sort((a, b) => b.timestamp - a.timestamp); + return reports; +} diff --git a/scripts/write_reports.ts b/scripts/write_reports.ts new file mode 100644 index 000000000..759c9c823 --- /dev/null +++ b/scripts/write_reports.ts @@ -0,0 +1,46 @@ +import fs from "fs"; + +import { getAllRawReports, getCommitHash } from "./utils"; + +import type { CommitGasReport, ContractReport } from "./utils"; +import type { HardhatRuntimeEnvironment } from "hardhat/types"; + +export function writeReports(hre: HardhatRuntimeEnvironment): void { + const rawReports = getAllRawReports(); + if (rawReports.length === 0) { + return; + } + if (rawReports.length > 1) { + throw Error( + `Multiple pending reports. Can only process most recent report to obtain current contract sizes` + ); + } + const [rawReport] = rawReports; + const contractReports: Record = {}; + for (const { contract, ...report } of rawReport.report) { + if (!contractReports[contract]) { + const artifact = hre.artifacts.readArtifactSync(contract); + const bytecode = Buffer.from(artifact.bytecode.slice(2), "hex"); + const deployedBytecode = Buffer.from( + artifact.deployedBytecode.slice(2), + "hex" + ); + contractReports[contract] = { + name: contract, + methods: [], + bytecodeSize: bytecode.byteLength, + deployedBytecodeSize: deployedBytecode.byteLength, + }; + } + contractReports[contract].methods.push(report); + } + const report: CommitGasReport = { + commitHash: getCommitHash(), + contractReports, + }; + fs.unlinkSync(rawReport.path); + fs.writeFileSync( + rawReport.path.replace(".md", ".json"), + JSON.stringify(report, null, 2) + ); +} diff --git a/test/advanced.spec.ts b/test/advanced.spec.ts new file mode 100644 index 000000000..dcc7c15b6 --- /dev/null +++ b/test/advanced.spec.ts @@ -0,0 +1,8571 @@ +import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { constants } from "ethers"; +import { ethers, network } from "hardhat"; + +import { deployContract } from "./utils/contracts"; +import { merkleTree } from "./utils/criteria"; +import { + buildOrderStatus, + buildResolver, + defaultAcceptOfferMirrorFulfillment, + defaultBuyNowMirrorFulfillment, + getItemETH, + random128, + randomBN, + randomHex, + toBN, + toFulfillment, + toFulfillmentComponents, + toKey, +} from "./utils/encoding"; +import { faucet } from "./utils/faucet"; +import { seaportFixture } from "./utils/fixtures"; +import { + VERSION, + minRandom, + simulateAdvancedMatchOrders, + simulateMatchOrders, +} from "./utils/helpers"; + +import type { + ConduitInterface, + ConsiderationInterface, + TestERC1155, + TestERC20, + TestERC721, + TestZone, +} from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { AdvancedOrder, ConsiderationItem } from "./utils/types"; +import type { Wallet } from "ethers"; + +const { parseEther } = ethers.utils; + +describe(`Advanced orders (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let conduitKeyOne: string; + let conduitOne: ConduitInterface; + let marketplaceContract: ConsiderationInterface; + let testERC1155: TestERC1155; + let testERC1155Two: TestERC1155; + let testERC20: TestERC20; + let testERC721: TestERC721; + let stubZone: TestZone; + + let checkExpectedEvents: SeaportFixtures["checkExpectedEvents"]; + let createMirrorAcceptOfferOrder: SeaportFixtures["createMirrorAcceptOfferOrder"]; + let createMirrorBuyNowOrder: SeaportFixtures["createMirrorBuyNowOrder"]; + let createOrder: SeaportFixtures["createOrder"]; + let getTestItem1155: SeaportFixtures["getTestItem1155"]; + let getTestItem1155WithCriteria: SeaportFixtures["getTestItem1155WithCriteria"]; + let getTestItem20: SeaportFixtures["getTestItem20"]; + let getTestItem721: SeaportFixtures["getTestItem721"]; + let getTestItem721WithCriteria: SeaportFixtures["getTestItem721WithCriteria"]; + let mint1155: SeaportFixtures["mint1155"]; + let mint721: SeaportFixtures["mint721"]; + let mint721s: SeaportFixtures["mint721s"]; + let mintAndApprove1155: SeaportFixtures["mintAndApprove1155"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + let set1155ApprovalForAll: SeaportFixtures["set1155ApprovalForAll"]; + let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; + let signOrder: SeaportFixtures["signOrder"]; + let withBalanceChecks: SeaportFixtures["withBalanceChecks"]; + let invalidContractOfferer: SeaportFixtures["invalidContractOfferer"]; + let invalidContractOffererRatifyOrder: SeaportFixtures["invalidContractOffererRatifyOrder"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + checkExpectedEvents, + conduitKeyOne, + conduitOne, + createMirrorAcceptOfferOrder, + createMirrorBuyNowOrder, + createOrder, + getTestItem1155, + getTestItem1155WithCriteria, + getTestItem20, + getTestItem721, + getTestItem721WithCriteria, + marketplaceContract, + mint1155, + mint721, + mint721s, + mintAndApprove1155, + mintAndApprove721, + mintAndApproveERC20, + set1155ApprovalForAll, + set721ApprovalForAll, + signOrder, + testERC1155, + testERC1155Two, + testERC20, + testERC721, + withBalanceChecks, + invalidContractOfferer, + invalidContractOffererRatifyOrder, + stubZone, + } = await seaportFixture(owner)); + }); + + let seller: Wallet; + let buyer: Wallet; + let zone: Wallet; + + async function setupFixture() { + // Setup basic buyer/seller wallets with ETH + const seller = new ethers.Wallet(randomHex(32), provider); + const buyer = new ethers.Wallet(randomHex(32), provider); + const zone = new ethers.Wallet(randomHex(32), provider); + for (const wallet of [seller, buyer, zone]) { + await faucet(wallet.address, provider); + } + + return { seller, buyer, zone }; + } + + beforeEach(async () => { + ({ seller, buyer, zone } = await loadFixture(setupFixture)); + }); + + describe("Contract Orders", async () => { + it("Contract Orders (standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (offer extended)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithoutOffer = JSON.parse(JSON.stringify(order)); + orderWithoutOffer.parameters.offer = []; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithoutOffer, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (offer amount increased)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithSmallerOfferAmount = JSON.parse(JSON.stringify(order)); + orderWithSmallerOfferAmount.parameters.offer[0].startAmount = + order.parameters.offer[0].startAmount.sub(1); + orderWithSmallerOfferAmount.parameters.offer[0].endAmount = + order.parameters.offer[0].endAmount.sub(1); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithSmallerOfferAmount, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (criteria-based offer item: ERC1155 wildcard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithCriteriaOffer = JSON.parse(JSON.stringify(order)); + orderWithCriteriaOffer.parameters.offer[0].itemType = 5; // Criteria-1155 + orderWithCriteriaOffer.parameters.offer[0].identifierOrCriteria = 0; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithCriteriaOffer, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (criteria-based offer item: ERC1155 non-wildcard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1 + ); + + const { root, proofs } = merkleTree([nftId, toBN(0xdead)]); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155WithCriteria(root, toBN(1), toBN(1)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + // ERC1155 criteria based item + offer[0].itemType = 5; + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activateWithCriteria(offer[0], consideration[0], nftId); + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4, // CONTRACT + criteriaResolvers + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // Change from Seller to offererContract + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.offer[0].identifier = nftId; + const orderWithCriteriaOffer = JSON.parse(JSON.stringify(order)); + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithCriteriaOffer, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + const orderStatus1 = await marketplaceContract.getOrderStatus( + orderHash + ); + + expect({ ...orderStatus1 }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + it("Contract Orders (offer extended with supplied offer)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithSmallerOffer = JSON.parse(JSON.stringify(order)); + orderWithSmallerOffer.parameters.offer[0].startAmount = + order.parameters.offer[0].startAmount.div(2); + orderWithSmallerOffer.parameters.offer[0].endAmount = + order.parameters.offer[0].endAmount.div(2); + + order.parameters.offer[0].startAmount = + order.parameters.offer[0].startAmount.div(2); + order.parameters.offer[0].endAmount = + order.parameters.offer[0].endAmount.div(2); + order.parameters.offer.push(order.parameters.offer[0]); + + await offererContract.connect(seller).extendAvailable(); + + // TODO: include balance checks + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithSmallerOffer, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + }); + it("Contract Orders (consideration reduced)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithExtraConsideration = JSON.parse(JSON.stringify(order)); + orderWithExtraConsideration.parameters.consideration.push( + JSON.parse( + JSON.stringify( + orderWithExtraConsideration.parameters.consideration[0] + ) + ) + ); + orderWithExtraConsideration.parameters.consideration[1].itemType = 1; + orderWithExtraConsideration.parameters.consideration[1].token = + "0x".padEnd(42, "1"); + orderWithExtraConsideration.parameters.totalOriginalConsiderationItems++; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithExtraConsideration, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (consideration amount reduced)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithIncreasedConsideration = JSON.parse(JSON.stringify(order)); + orderWithIncreasedConsideration.parameters.consideration[0].startAmount = + order.parameters.consideration[0].startAmount.add(1); + orderWithIncreasedConsideration.parameters.consideration[0].endAmount = + order.parameters.consideration[0].endAmount.add(1); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithIncreasedConsideration, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (criteria-based consideration item)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + // Buyer mints nft + const { nftId: buyerNftId, amount: buyerAmount } = + await mintAndApprove1155(buyer, marketplaceContract.address, 10000); + + const consideration = [ + getTestItem1155( + buyerNftId, + buyerAmount.mul(10), + buyerAmount.mul(10), + undefined, + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithCriteriaConsideration = JSON.parse(JSON.stringify(order)); + orderWithCriteriaConsideration.parameters.consideration[0].itemType = 5; // Criteria-1155 + orderWithCriteriaConsideration.parameters.consideration[0].identifierOrCriteria = 0; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithCriteriaConsideration, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Contract Orders (native token offer items)", async () => { + // Buyer deploys offererContract + const offererContract = await deployContract( + "TestContractOffererNativeToken", + owner, + marketplaceContract.address + ); + + await faucet(offererContract.address, provider); + + // Buyer mints nft + // Buyer will call fulfillAdvancedOrder and receive 10 eth offer item. + const nftId = await mintAndApprove721(buyer, marketplaceContract.address); + + const offer = [getItemETH(parseEther("10"), parseEther("10")) as any]; + + const consideration = [ + getTestItem721( + nftId, + toBN(1), + toBN(1), + offererContract.address, + testERC721.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Reverts on contract orders where offer startAmount doesn't equal offer endAmount", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + offer[0].startAmount = offer[0].endAmount.add(1); + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer endAmount is greater than offer amount (branch 2)", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + // endAmount should exceed amount to throw on branch 2 + offer[0].endAmount = offer[0].amount.add(1); + // startAmount and endAmount should be equal in order to reach branch 2 + offer[0].startAmount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer itemType is different from generated itemType", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.offer[0].itemType = 1; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer token address is different from generated token address", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.offer[0].token = testERC1155Two.address; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer identifier is different from generated identifier", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.offer[0].identifierOrCriteria = + offer[0].identifier.add(1); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration startAmount doesn't equal consideration endAmount", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + consideration[0].startAmount = consideration[0].endAmount.add(1); + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration itemType is different from generated itemType", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.consideration[0].itemType = 1; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration token address is different from generated token address", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.consideration[0].token = testERC1155Two.address; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration identifier is different from generated identifier", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.consideration[0].identifierOrCriteria = + consideration[0].identifier.add(1); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration recipient is different from generated recipient", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.consideration[0].recipient = owner.address; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration is omitted", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithNoConsideration = JSON.parse(JSON.stringify(order)); + orderWithNoConsideration.parameters.consideration = []; + orderWithNoConsideration.parameters.totalOriginalConsiderationItems = 0; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithNoConsideration, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer and consideration omitted", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithoutOfferOrConsideration = JSON.parse( + JSON.stringify(order) + ); + orderWithoutOfferOrConsideration.parameters.offer = []; + orderWithoutOfferOrConsideration.parameters.consideration = []; + orderWithoutOfferOrConsideration.parameters.totalOriginalConsiderationItems = 0; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithoutOfferOrConsideration, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer is reduced by contract offerer", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, secondNftId, secondAmount); + + await expect( + testERC1155Two + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithExtraOffer = JSON.parse(JSON.stringify(order)); + orderWithExtraOffer.parameters.offer.push( + JSON.parse(JSON.stringify(orderWithExtraOffer.parameters.offer[0])) + ); + orderWithExtraOffer.parameters.offer[1].token = testERC1155Two.address; + orderWithExtraOffer.parameters.offer[1].identifierOrCriteria = + secondNftId; + orderWithExtraOffer.parameters.offer[1].amount = secondAmount; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithExtraOffer, + [], + toKey(0), + buyer.address, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration is extended by contract offerer", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await offererContract.connect(seller).extendRequired(); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where offer amount is reduced by contract offerer", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, secondNftId, secondAmount); + + await expect( + testERC1155Two + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + order.parameters.offer[0].startAmount = + order.parameters.offer[0].startAmount.add(1); + order.parameters.offer[0].endAmount = + order.parameters.offer[0].startAmount.add(1); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration amount is increased by contract offerer", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + consideration[0].startAmount = consideration[0].startAmount.sub(1); + consideration[0].endAmount = consideration[0].endAmount.sub(1); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders that supply a bad fraction", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 2; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ).to.be.revertedWithCustomError(marketplaceContract, "BadFraction"); + }); + it("Reverts on contract orders that supply a bad fraction (numerator != 1)", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 2; + order.denominator = 2; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ).to.be.revertedWithCustomError(marketplaceContract, "BadFraction"); + }); + it("Reverts on contract orders where call to generateOrders throws and reverts aren't skipped", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + await set1155ApprovalForAll(seller, invalidContractOfferer.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + invalidContractOfferer.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await invalidContractOfferer + .connect(seller) + .activate(offer[0], consideration[0]); + + consideration[0].startAmount = consideration[0].startAmount.sub(1); + consideration[0].endAmount = consideration[0].endAmount.sub(1); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + invalidContractOfferer.address + ); + + const orderHash = + invalidContractOfferer.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = invalidContractOfferer.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where call to generateOrders throws and reverts are skipped", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract with invalid generateOrders function + // and approves it for 1155 token + const offererContract = await deployContract( + "TestInvalidContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + consideration[0].startAmount = consideration[0].startAmount.sub(1); + consideration[0].endAmount = consideration[0].endAmount.sub(1); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orders = [order, mirrorOrder]; + + const offerComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + orders, + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value, + } + ) + ).to.be.reverted; // TODO: look into this + }); + it("Reverts on contract orders where call to ratifyOrders returns incorrect magic value", async () => { + // Seller mints nfts + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + await set1155ApprovalForAll( + seller, + invalidContractOffererRatifyOrder.address, + true + ); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + invalidContractOffererRatifyOrder.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await invalidContractOffererRatifyOrder + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + invalidContractOffererRatifyOrder.address + ); + + const orderHash = + invalidContractOffererRatifyOrder.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = invalidContractOffererRatifyOrder.address; + order.numerator = 1; + order.denominator = 1; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Reverts on contract orders where consideration length does not match totalOriginalConsiderationItems", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContract and approves it for 1155 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount.mul(10), amount.mul(10)) as any, + ]; + + const consideration = [ + getItemETH( + amount.mul(1000), + amount.mul(1000), + offererContract.address + ) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(offer[0], consideration[0]); + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const orderWithMismatchedTotalOriginal = JSON.parse( + JSON.stringify(order) + ); + orderWithMismatchedTotalOriginal.parameters + .totalOriginalConsiderationItems++; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + orderWithMismatchedTotalOriginal, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "ConsiderationLengthNotEqualToTotalOriginal" + ); + }); + it("Can fulfill and aggregate contract orders via fulfillAvailableAdvancedOrders with failing orders", async () => { + // Seller mints nfts + const { nftId: nftIdOne, amount: amountOne } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + const { nftId: nftIdTwo, amount: amountTwo } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + const { nftId: nftIdThree, amount: amountThree } = + await mintAndApprove1155(seller, marketplaceContract.address, 10000); + const { nftId: nftIdFour, amount: amountFour } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // seller deploys offererContracts and approves them for 1155 token + const offererContractOne = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContractOne.address, true); + + const offererContractTwo = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContractTwo.address, true); + + const offererContractThree = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContractThree.address, true); + + const offererContractFour = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set1155ApprovalForAll(seller, offererContractFour.address, true); + + const offerOne = [ + getTestItem1155(nftIdOne, amountOne.mul(10), amountOne.mul(10)) as any, + ]; + + const considerationOne = [ + getItemETH( + amountOne.mul(1000), + amountOne.mul(1000), + offererContractOne.address + ) as any, + ]; + + offerOne[0].identifier = offerOne[0].identifierOrCriteria; + offerOne[0].amount = offerOne[0].endAmount; + + considerationOne[0].identifier = considerationOne[0].identifierOrCriteria; + considerationOne[0].amount = considerationOne[0].endAmount; + + await offererContractOne + .connect(seller) + .activate(offerOne[0], considerationOne[0]); + + const { order: orderOne, value } = await createOrder( + seller, + zone, + offerOne, + considerationOne, + 4 // CONTRACT + ); + + const contractOffererOneNonce = + await marketplaceContract.getContractOffererNonce( + offererContractOne.address + ); + + const orderHashOne = + offererContractOne.address.toLowerCase() + + contractOffererOneNonce.toHexString().slice(2).padStart(24, "0"); + + orderOne.parameters.offerer = offererContractOne.address; + orderOne.numerator = 1; + orderOne.denominator = 1; + orderOne.signature = "0x"; + + // test orderHashes + orderOne.extraData = ethers.utils.defaultAbiCoder.encode( + ["bytes32[]"], + [ + [ + orderHashOne, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ], + ] + ); + + expect((orderOne.extraData.length - 2) / 64).to.equal(6); + + // second order reverts when generating the order + const offerTwo = [ + getTestItem1155(nftIdTwo, amountTwo.mul(10), amountTwo.mul(10)) as any, + ]; + + const considerationTwo = [ + getItemETH( + amountTwo.mul(1000), + amountTwo.mul(1000), + offererContractTwo.address + ) as any, + ]; + + offerTwo[0].identifier = offerTwo[0].identifierOrCriteria; + offerTwo[0].amount = offerTwo[0].endAmount; + + considerationTwo[0].identifier = considerationTwo[0].identifierOrCriteria; + considerationTwo[0].amount = considerationTwo[0].endAmount; + + await offererContractTwo + .connect(seller) + .activate(offerTwo[0], considerationTwo[0]); + + const { order: orderTwo, value: valueTwo } = await createOrder( + seller, + zone, + offerTwo, + considerationTwo, + 4 // CONTRACT + ); + + orderTwo.parameters.offerer = offererContractTwo.address; + orderTwo.numerator = 1; + orderTwo.denominator = 1; + orderTwo.signature = "0x"; + orderTwo.extraData = "0x1234"; // causes call to revert + + // third order: generated order is missing expected offer items + const offerThree = [ + getTestItem1155( + nftIdThree, + amountThree.mul(10), + amountThree.mul(10) + ) as any, + getTestItem1155( + nftIdOne, + amountOne.mul(10), + amountThree.mul(10) + ) as any, + ]; + + const considerationThree = [ + getItemETH( + amountThree.mul(1000), + amountThree.mul(1000), + offererContractThree.address + ) as any, + ]; + + offerThree[0].identifier = offerThree[0].identifierOrCriteria; + offerThree[0].amount = offerThree[0].endAmount; + + offerThree[1].identifier = offerThree[1].identifierOrCriteria; + offerThree[1].amount = offerThree[1].endAmount; + + considerationThree[0].identifier = + considerationThree[0].identifierOrCriteria; + considerationThree[0].amount = considerationThree[0].endAmount; + + await offererContractThree + .connect(seller) + .activate(offerThree[0], considerationThree[0]); + + const { order: orderThree, value: valueThree } = await createOrder( + seller, + zone, + offerThree, + considerationThree, + 4 // CONTRACT + ); + + orderThree.parameters.offerer = offererContractThree.address; + orderThree.numerator = 1; + orderThree.denominator = 1; + orderThree.signature = "0x"; + + // fourth order: generated order exceeds expected consideration items + const offerFour = [ + getTestItem1155( + nftIdFour, + amountFour.mul(10), + amountFour.mul(10) + ) as any, + ]; + + const considerationFour = [ + getItemETH( + amountFour.mul(1000), + amountFour.mul(1000), + offererContractFour.address + ) as any, + ]; + + offerFour[0].identifier = offerFour[0].identifierOrCriteria; + offerFour[0].amount = offerFour[0].endAmount; + + considerationFour[0].identifier = + considerationFour[0].identifierOrCriteria; + considerationFour[0].amount = considerationFour[0].endAmount; + + await offererContractFour + .connect(seller) + .activate(offerFour[0], considerationFour[0]); + + const { order: orderFour, value: valueFour } = await createOrder( + seller, + zone, + offerFour, + considerationFour, + 4 // CONTRACT + ); + + orderFour.parameters.offerer = offererContractFour.address; + orderFour.numerator = 1; + orderFour.denominator = 1; + orderFour.signature = "0x"; + orderFour.parameters.consideration[0].startAmount = + orderFour.parameters.consideration[0].startAmount.sub(1); + orderFour.parameters.consideration[0].endAmount = + orderFour.parameters.consideration[0].endAmount.sub(1); + + const offerComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + [{ orderIndex: 2, itemIndex: 0 }], + [{ orderIndex: 3, itemIndex: 0 }], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + [{ orderIndex: 2, itemIndex: 0 }], + [{ orderIndex: 3, itemIndex: 0 }], + ]; + + await withBalanceChecks([orderOne], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo, orderThree, orderFour], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.add(valueTwo).add(valueThree).add(valueFour).mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + }); + + describe("Partial fills", async () => { + it("Partial fills (standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 2, 10) + ); + + order.numerator = 1; // fill one half + order.denominator = 2; // fill one half + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 14, 20) + ); + + // Fill remaining; only 3/10ths will be fillable + order.numerator = 1; // fill one half + order.denominator = 2; // fill one half + + const ordersClone = [{ ...order }] as AdvancedOrder[]; + for (const [, clonedOrder] of Object.entries(ordersClone)) { + clonedOrder.parameters.startTime = order.parameters.startTime; + clonedOrder.parameters.endTime = order.parameters.endTime; + + for (const [j, offerItem] of Object.entries( + clonedOrder.parameters.offer + )) { + offerItem.startAmount = order.parameters.offer[+j].startAmount; + offerItem.endAmount = order.parameters.offer[+j].endAmount; + } + + for (const [j, considerationItem] of Object.entries( + clonedOrder.parameters.consideration + )) { + considerationItem.startAmount = + order.parameters.consideration[+j].startAmount; + considerationItem.endAmount = + order.parameters.consideration[+j].endAmount; + } + } + + ordersClone[0].numerator = 3; + ordersClone[0].denominator = 10; + + await withBalanceChecks(ordersClone, 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: ordersClone[0], + orderHash, + fulfiller: buyer.address, + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 40, 40) + ); + }); + it("Partial fills (standard, additional permutations)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 2, 10) + ); + + order.numerator = 1; // fill one tenth + order.denominator = 10; // fill one tenth + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 3, 10) + ); + + // Fill all available; only 7/10ths will be fillable + order.numerator = 1; // fill all available + order.denominator = 1; // fill all available + + const ordersClone = [{ ...order }] as AdvancedOrder[]; + for (const [, clonedOrder] of Object.entries(ordersClone)) { + clonedOrder.parameters.startTime = order.parameters.startTime; + clonedOrder.parameters.endTime = order.parameters.endTime; + + for (const [j, offerItem] of Object.entries( + clonedOrder.parameters.offer + )) { + offerItem.startAmount = order.parameters.offer[+j].startAmount; + offerItem.endAmount = order.parameters.offer[+j].endAmount; + } + + for (const [j, considerationItem] of Object.entries( + clonedOrder.parameters.consideration + )) { + considerationItem.startAmount = + order.parameters.consideration[+j].startAmount; + considerationItem.endAmount = + order.parameters.consideration[+j].endAmount; + } + } + + ordersClone[0].numerator = 7; + ordersClone[0].denominator = 10; + + await withBalanceChecks(ordersClone, 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: ordersClone[0], + orderHash, + fulfiller: buyer.address, + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 10, 10) + ); + }); + it("Partial fills (match)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + let mirrorObject; + mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + let executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorObject.mirrorOrder], + [], // no criteria resolvers + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract.connect(owner).matchAdvancedOrders( + [order, mirrorObject.mirrorOrder], + [], // no criteria resolvers + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorObject.mirrorOrder, + orderHash: mirrorObject.mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 2, 10) + ); + + order.numerator = 1; // fill one tenth + order.denominator = 10; // fill one tenth + + mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); + + executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorObject.mirrorOrder], + [], // no criteria resolvers + fulfillments, + owner, + value + ); + + const tx2 = marketplaceContract.connect(owner).matchAdvancedOrders( + [order, mirrorObject.mirrorOrder], + [], // no criteria resolvers + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt2 = await (await tx2).wait(); + await checkExpectedEvents( + tx2, + receipt2, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorObject.mirrorOrder, + orderHash: mirrorObject.mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 3, 10) + ); + + // Fill all available; only 7/10ths will be fillable + order.numerator = 7; // fill all available + order.denominator = 10; // fill all available + + mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); + + executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorObject.mirrorOrder], + [], // no criteria resolvers + fulfillments, + owner, + value + ); + + const tx3 = await marketplaceContract.connect(owner).matchAdvancedOrders( + [order, mirrorObject.mirrorOrder], + [], // no criteria resolvers + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt3 = await tx3.wait(); + await checkExpectedEvents( + tx3, + receipt3, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorObject.mirrorOrder, + orderHash: mirrorObject.mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 10, 10) + ); + }); + + it("Simplifies fraction when numerator/denominator would overflow", async () => { + const numer1 = toBN(2).pow(100); + const denom1 = toBN(2).pow(101); + const numer2 = toBN(2).pow(20); + const denom2 = toBN(2).pow(22); + const amt = 8; + await mintAndApproveERC20(buyer, marketplaceContract.address, amt); + // Seller mints nft + const { nftId } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000, + undefined, + amt + ); + + const offer = [getTestItem1155(nftId, amt, amt)]; + + const consideration = [getTestItem20(amt, amt, seller.address)]; + const { order, orderHash, value } = await createOrder( + seller, + undefined, + offer, + consideration, + 1, // PARTIAL_OPEN + undefined, + undefined, + undefined, + undefined, + undefined, + true + ); + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // 1/2 + order.numerator = numer1 as any; // would error here if cast to number (due to overflow) + order.denominator = denom1 as any; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, numer1, denom1) + ); + + order.numerator = +numer2; + order.denominator = +denom2; + + await marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, toBN(3), toBN(4)) + ); + }); + + it("Reverts when numerator/denominator overflow", async () => { + const prime1 = toBN(2).pow(7).sub(1); + const prime2 = toBN(2).pow(61).sub(1); + const prime3 = toBN(2).pow(107).sub(1); + const amt = prime1.mul(prime2).mul(prime3); + await mintAndApproveERC20(buyer, marketplaceContract.address, amt); + // Seller mints nft + const { nftId } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000, + undefined, + amt + ); + + const offer = [getTestItem1155(nftId, amt, amt)]; + + const consideration = [getTestItem20(amt, amt, seller.address)]; + const { order, orderHash, value } = await createOrder( + seller, + undefined, + offer, + consideration, + 1, // PARTIAL_OPEN + undefined, + undefined, + undefined, + undefined, + undefined, + true + ); + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // 1/2 + order.numerator = 1; + order.denominator = prime2 as any; // would error here if cast to number (due to overflow) + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, toBN(1), prime2) + ); + + order.numerator = prime1 as any; // would error here if cast to number (due to overflow) + order.denominator = prime3 as any; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value, + }) + ).to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW); + }); + }); + + describe("Criteria-based orders", async () => { + it("Criteria-based offer item ERC721 (standard)", async () => { + // Seller mints nfts + const [nftId, secondNFTId, thirdNFTId] = await mint721s(seller, 3); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + it("Criteria-based offer item ERC1155 (standard)", async () => { + // Seller mints nfts + const { nftId } = await mint1155(seller); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [getTestItem1155WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + it("Criteria-based offer item (standard, collection-level)", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + getTestItem721WithCriteria(ethers.constants.HashZero, toBN(1), toBN(1)), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [buildResolver(0, 0, 0, nftId, [])]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + it("Criteria-based offer item ERC721 (match)", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[1, 0]]], + [[[1, 1]], [[0, 1]]], + [[[1, 2]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions, + criteriaResolvers + ); + return receipt; + }); + it("Criteria-based offer item ERC1155 (match)", async () => { + // Seller mints nfts + const { nftId } = await mint1155(seller); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [getTestItem1155WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[1, 0]]], + [[[1, 1]], [[0, 1]]], + [[[1, 2]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions, + criteriaResolvers + ); + return receipt; + }); + it("Criteria-based offer item (match, collection-level)", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + getTestItem721WithCriteria(ethers.constants.HashZero, toBN(1), toBN(1)), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [buildResolver(0, 0, 0, nftId, [])]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[1, 0]]], + [[[1, 1]], [[0, 1]]], + [[[1, 2]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions, + criteriaResolvers + ); + return receipt; + }); + it("Criteria-based consideration item (standard)", async () => { + // buyer mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(buyer.address, nftId); + await testERC721.mint(buyer.address, secondNFTId); + await testERC721.mint(buyer.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem721WithCriteria(root, toBN(1), toBN(1), seller.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await withBalanceChecks( + [order], + value.mul(-1), + criteriaResolvers, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + } + ); + }); + it("Criteria-based consideration item ERC1155 (standard)", async () => { + // buyer mints nfts + const { nftId } = await mint1155(buyer); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(buyer, marketplaceContract.address, true); + + const { root, proofs } = merkleTree([nftId]); + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem1155WithCriteria(root, toBN(1), toBN(1), seller.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await withBalanceChecks( + [order], + value.mul(-1), + criteriaResolvers, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + } + ); + }); + it("Criteria-based wildcard consideration item (standard)", async () => { + // buyer mints nft + const nftId = await mintAndApprove721(buyer, marketplaceContract.address); + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem721WithCriteria( + ethers.constants.HashZero, + toBN(1), + toBN(1), + seller.address + ), + ]; + + const criteriaResolvers = [buildResolver(0, 1, 0, nftId, [])]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await withBalanceChecks( + [order], + value.mul(-1), + criteriaResolvers, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + } + ); + }); + it("Criteria-based consideration item ERC721 (match)", async () => { + // Fulfiller mints nft + const nftId = await mint721(buyer); + const tokenAmount = minRandom(100); + + // Fulfiller approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Offerer mints ERC20 + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Fulfiller mints ERC20 + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [ + // Offerer (Seller) + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + // Fulfiller (Buyer) + { + itemType: 4, // ERC721WithCriteria + token: testERC721.address, + identifierOrCriteria: toBN(root), + startAmount: toBN(1), + endAmount: toBN(1), + recipient: seller.address, + }, + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions, + criteriaResolvers + ); + return receipt; + }); + it("Criteria-based consideration item ERC1155 (match)", async () => { + // Fulfiller mints nft + const { nftId } = await mint1155(buyer); + const tokenAmount = minRandom(100); + + // Fulfiller approves marketplace contract to transfer NFT + await set1155ApprovalForAll(buyer, marketplaceContract.address, true); + + // Offerer mints ERC20 + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Fulfiller mints ERC20 + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [ + // Offerer (Seller) + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + // Fulfiller (Buyer) + { + itemType: 5, // ERC1155_WITH_CRITERIA + token: testERC1155.address, + identifierOrCriteria: toBN(root), + startAmount: toBN(1), + endAmount: toBN(1), + recipient: seller.address, + }, + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions, + criteriaResolvers + ); + + return receipt; + }); + it("Criteria-based offer item with junk criteria proof", async () => { + // Seller mints nfts + const { nftId } = await mint1155(seller); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [getTestItem1155WithCriteria(toBN(0), toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, [ + "0xDEAFBEEF00000000000000000000000000000000000000000000000000000000", + ]), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidProof"); + }); + }); + + describe("Bulk Signature", async () => { + it("Can sign for a bulk signature", async () => { + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + true + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + + it("Can sign for a bulk signature with index 9 in 32 node tree", async () => { + // let n = x - 99 + + // equal(n, and(n, mask)) + // n < 738 & (n & 31) < 2 + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + true, + 9, + 5 + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + + it("Can sign for a compact bulk signature", async () => { + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1, + undefined, + undefined, + undefined, + undefined, + undefined, + true, + true + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + }); + + describe("Ascending / Descending amounts", async () => { + it("Ascending offer amount (standard)", async () => { + // Seller mints nft + const nftId = randomBN(); + const startAmount = toBN(randomBN(2)); + const endAmount = startAmount.mul(2); + await testERC1155.mint(seller.address, nftId, endAmount.mul(10)); + + // Seller approves marketplace contract to transfer NFTs + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [getTestItem1155(nftId, startAmount, endAmount, undefined)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Ascending consideration amount (standard)", async () => { + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Buyer mints nft + const nftId = randomBN(); + const startAmount = toBN(randomBN(2)); + const endAmount = startAmount.mul(2); + await testERC1155.mint(buyer.address, nftId, endAmount.mul(10)); + + // Buyer approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(buyer, marketplaceContract.address, true); + + // Buyer needs to approve marketplace to transfer ERC20 tokens too (as it's a standard fulfillment) + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem1155( + nftId, + startAmount, + endAmount, + undefined, + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Ascending offer amount (match)", async () => { + // Seller mints nft + const nftId = randomBN(); + const startAmount = toBN(randomBN(2)); + const endAmount = startAmount.mul(2); + await testERC1155.mint(seller.address, nftId, endAmount.mul(10)); + + // Seller approves marketplace contract to transfer NFTs + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [getTestItem1155(nftId, startAmount, endAmount, undefined)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + }); + + describe("Sequenced Orders", async () => { + it("Match A => B => C => A", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + const secondNFTId = await mintAndApprove721( + buyer, + marketplaceContract.address + ); + const thirdNFTId = await mintAndApprove721( + owner, + marketplaceContract.address + ); + + const offerOne = [ + getTestItem721(nftId, toBN(1), toBN(1), undefined, testERC721.address), + ]; + + const considerationOne = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + seller.address, + testERC721.address + ), + ]; + + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zone, + offerOne, + considerationOne, + 0 // FULL_OPEN + ); + + const offerTwo = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + + const considerationTwo = [ + getTestItem721( + thirdNFTId, + toBN(1), + toBN(1), + buyer.address, + testERC721.address + ), + ]; + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + buyer, + zone, + offerTwo, + considerationTwo, + 0 // FULL_OPEN + ); + + const offerThree = [ + getTestItem721( + thirdNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + + const considerationThree = [ + getTestItem721( + nftId, + toBN(1), + toBN(1), + owner.address, + testERC721.address + ), + ]; + + const { order: orderThree, orderHash: orderHashThree } = + await createOrder( + owner, + zone, + offerThree, + considerationThree, + 0 // FULL_OPEN + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[2, 0]]], + [[[2, 0]], [[1, 0]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [orderOne, orderTwo, orderThree], + [], // no criteria resolvers + fulfillments, + owner, + 0 // no value + ); + + expect(executions.length).to.equal(fulfillments.length); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [orderOne, orderTwo, orderThree], + [], + fulfillments, + ethers.constants.AddressZero, + { + value: 0, + } + ); + const receipt = await (await tx).wait(); + + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: owner.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: owner.address, + }, + { + order: orderThree, + orderHash: orderHashThree, + fulfiller: owner.address, + }, + ], + executions + ); + }); + it("Match with fewer executions when one party has multiple orders that coincide", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + const secondNFTId = await mintAndApprove721( + buyer, + marketplaceContract.address + ); + + const offerOne = [ + getTestItem721(nftId, toBN(1), toBN(1), undefined, testERC721.address), + ]; + + const considerationOne = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zone, + offerOne, + considerationOne, + 0 // FULL_OPEN + ); + + const offerTwo = [getItemETH(parseEther("10"), parseEther("10"))]; + + const considerationTwo = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + seller.address, + testERC721.address + ), + ]; + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offerTwo, + considerationTwo, + 0 // FULL_OPEN + ); + + const offerThree = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + + const considerationThree = [ + getTestItem721( + nftId, + toBN(1), + toBN(1), + buyer.address, + testERC721.address + ), + ]; + + const { order: orderThree, orderHash: orderHashThree } = + await createOrder( + buyer, + zone, + offerThree, + considerationThree, + 0 // FULL_OPEN + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[2, 0]]], + [[[2, 0]], [[1, 0]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [orderOne, orderTwo, orderThree], + [], // no criteria resolvers + fulfillments, + owner, + 0 // no value + ); + + expect(executions.length).to.equal(fulfillments.length - 1); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [orderOne, orderTwo, orderThree], + [], + fulfillments, + ethers.constants.AddressZero, + { + value: 0, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: owner.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: owner.address, + }, + { + order: orderThree, + orderHash: orderHashThree, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + }); + + describe("Order groups", async () => { + it("Multiple offer components at once", async () => { + // Seller mints NFTs + const { nftId, amount } = await mint1155(seller, 2); + + // Seller approves marketplace contract to transfer NFT + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + // Buyer mints ERC20s + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount.mul(2) + ); + + const offerOne = [getTestItem1155(nftId, amount, amount)]; + + const considerationOne = [ + getTestItem20(tokenAmount, tokenAmount, seller.address), + ]; + + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zone, + offerOne, + considerationOne, + 0 // FULL_OPEN + ); + + const offerTwo = [getTestItem1155(nftId, amount, amount)]; + + const considerationTwo = [ + getTestItem20(tokenAmount, tokenAmount, seller.address), + ]; + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offerTwo, + considerationTwo, + 0 // FULL_OPEN + ); + + const offerThree = [ + getTestItem20(tokenAmount.mul(2), tokenAmount.mul(2)), + ]; + + const considerationThree = [ + getTestItem1155( + nftId, + amount.mul(2), + amount.mul(2), + undefined, + buyer.address + ), + ]; + + const { order: orderThree, orderHash: orderHashThree } = + await createOrder( + buyer, + zone, + offerThree, + considerationThree, + 0 // FULL_OPEN + ); + + const fulfillments = [ + [ + [ + [0, 0], + [1, 0], + ], + [[2, 0]], + ], + [[[2, 0]], [[0, 0]]], + [[[2, 0]], [[1, 0]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [orderOne, orderTwo, orderThree], + [], // no criteria resolvers + fulfillments, + owner, + 0 // no value + ); + + expect(executions.length).to.equal(fulfillments.length); + + const tx = marketplaceContract + .connect(buyer) + .matchAdvancedOrders( + [orderOne, orderTwo, orderThree], + [], + fulfillments, + ethers.constants.AddressZero, + { + value: 0, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + { + order: orderThree, + orderHash: orderHashThree, + fulfiller: buyer.address, + }, + ], + executions, + [], + true + ); + + // Check ERC1155 event `TransferSingle` amount is amount.mul(2) + expect( + toBN( + "0x" + + receipt.events + ?.find( + (x) => + x.topics[0] === + testERC1155.interface.getEventTopic("TransferSingle") + ) + ?.data.slice(66) + ).toString() + ).to.equal(amount.mul(2).toString()); + + return receipt; + }); + it("Multiple consideration components at once", async () => { + // Seller mints NFTs + const { nftId, amount } = await mint1155(seller, 2); + + // Seller approves marketplace contract to transfer NFT + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + // Buyer mints ERC20s + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount.mul(2) + ); + + const offerOne = [ + getTestItem1155(nftId, amount.mul(2), amount.mul(2), undefined), + ]; + + const considerationOne = [ + getTestItem20(tokenAmount.mul(2), tokenAmount.mul(2), seller.address), + ]; + + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zone, + offerOne, + considerationOne, + 0 // FULL_OPEN + ); + + const offerTwo = [getTestItem20(tokenAmount, tokenAmount)]; + + const considerationTwo = [ + getTestItem1155(nftId, amount, amount, undefined, buyer.address), + ]; + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + buyer, + zone, + offerTwo, + considerationTwo, + 0 // FULL_OPEN + ); + + const offerThree = [getTestItem20(tokenAmount, tokenAmount)]; + + const considerationThree = [ + getTestItem1155(nftId, amount, amount, undefined, buyer.address), + ]; + + const { order: orderThree, orderHash: orderHashThree } = + await createOrder( + buyer, + zone, + offerThree, + considerationThree, + 0 // FULL_OPEN + ); + + const fulfillments = [ + [ + [[0, 0]], + [ + [1, 0], + [2, 0], + ], + ], + [[[1, 0]], [[0, 0]]], + [[[2, 0]], [[0, 0]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [orderOne, orderTwo, orderThree], + [], // no criteria resolvers + fulfillments, + owner, + 0 // no value + ); + + expect(executions.length).to.equal(fulfillments.length); + + const tx = marketplaceContract + .connect(buyer) + .matchAdvancedOrders( + [orderOne, orderTwo, orderThree], + [], + fulfillments, + ethers.constants.AddressZero, + { + value: 0, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + { + order: orderThree, + orderHash: orderHashThree, + fulfiller: buyer.address, + }, + ], + executions, + [], + true + ); + + // TODO: include balance checks on the duplicate ERC20 transfers + + return receipt; + }); + it("Multiple consideration components at once, various OOR offer/consideration items", async () => { + // Seller mints NFTs + const { nftId, amount } = await mint1155(seller, 2); + + // Seller approves marketplace contract to transfer NFT + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + // Buyer mints ERC20s + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount.mul(2) + ); + + const offerOne = [ + getTestItem1155(nftId, amount.mul(2), amount.mul(2), undefined), + ]; + + const considerationOne = [ + getTestItem20(tokenAmount.mul(2), tokenAmount.mul(2), seller.address), + ]; + + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zone, + offerOne, + considerationOne, + 0 // FULL_OPEN + ); + + const offerTwo = [getTestItem20(tokenAmount, tokenAmount)]; + + const considerationTwo = [ + getTestItem1155(nftId, amount, amount, undefined, buyer.address), + ]; + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + buyer, + zone, + offerTwo, + considerationTwo, + 0 // FULL_OPEN + ); + + const offerThree = [getTestItem20(tokenAmount, tokenAmount)]; + + const considerationThree = [ + getTestItem1155(nftId, amount, amount, undefined, buyer.address), + ]; + + const { order: orderThree, orderHash: orderHashThree } = + await createOrder( + buyer, + zone, + offerThree, + considerationThree, + 0 // FULL_OPEN + ); + + const fulfillments = [ + [ + [ + [0, 5], // OOR first offer item + [0, 0], + ], + [ + [1, 0], + [2, 0], + ], + ], + [ + [[1, 0]], + [ + [0, 5], // OOR first consideration item + [0, 0], + ], + ], + [ + [ + [2, 0], + [0, 5], // OOR last offer item + ], + [ + [0, 0], + [0, 5], // OOR last consideration item + ], + ], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [orderOne, orderTwo, orderThree], + [], // no criteria resolvers + fulfillments, + owner, + 0 // no value + ); + + expect(executions.length).to.equal(fulfillments.length); + + const tx = marketplaceContract + .connect(buyer) + .matchAdvancedOrders( + [orderOne, orderTwo, orderThree], + [], + fulfillments, + ethers.constants.AddressZero, + { + value: 0, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + { + order: orderThree, + orderHash: orderHashThree, + fulfiller: buyer.address, + }, + ], + executions, + [], + true + ); + + // TODO: include balance checks on the duplicate ERC20 transfers + + return receipt; + }); + }); + + describe("Complex ERC1155 transfers", async () => { + it("ERC1155 <=> ETH (match)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = + await mintAndApprove1155(seller, marketplaceContract.address); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(5); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, three items)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = + await mintAndApprove1155(seller, marketplaceContract.address); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(6); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, initial OOR offer/consideration items skipped)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = + await mintAndApprove1155(seller, marketplaceContract.address); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [ + [ + [0, 5], // OOR offer item + [0, 0], + ], + [ + [0, 5], // OOR consideration itemx + [1, 0], + ], + ], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(6); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, subsequent OOR offer/consideration items skipped)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = + await mintAndApprove1155(seller, marketplaceContract.address); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [ + [ + [0, 0], + [0, 5], + ], + [ + [1, 0], + [0, 5], + ], + ], + [[[0, 0]], [[0, 5]]], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(6); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, offer aggregation skipped if no available consideration items)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = + await mintAndApprove1155(seller, marketplaceContract.address); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + // Order index invalid for offer, but will be skipped because no available considerations + [[[5, 5]], [[0, 5]]], + [[[0, 0]], [[1, 0]]], + [[[0, 0]], [[0, 5]]], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(6); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match via conduit)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = + await mintAndApprove1155(seller, conduitOne.address); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(5); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, single item)", async () => { + // Seller mints first nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration: ConsiderationItem[] = []; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [toFulfillment([[0, 0]], [[1, 0]])]; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(1); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, single 1155)", async () => { + // Seller mints first nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match, two different 1155 contracts)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, secondNftId, secondAmount); + + // Seller approves marketplace contract to transfer NFTs + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + await expect( + testERC1155Two + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155( + secondNftId, + secondAmount, + secondAmount, + testERC1155Two.address + ), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(5); + + await marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + }); + it("ERC1155 <=> ETH (match, one single and one with two 1155's)", async () => { + // Seller mints first nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, secondNftId, secondAmount); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = await mint1155(seller); + + // Seller approves marketplace contract to transfer NFTs + + await expect( + testERC1155Two + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155( + secondNftId, + secondAmount, + secondAmount, + testERC1155Two.address + ), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(6); + + await marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + }); + it("ERC1155 <=> ETH (match, two different groups of 1155's)", async () => { + // Seller mints first nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, secondNftId, secondAmount); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = await mint1155(seller); + + // Seller mints fourth nft + const fourthNftId = toBN(randomBN(4)); + const fourthAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, fourthNftId, fourthAmount); + + // Seller approves marketplace contract to transfer NFTs + + await expect( + testERC1155Two + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155( + secondNftId, + secondAmount, + secondAmount, + testERC1155Two.address + ), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + getTestItem1155( + fourthNftId, + fourthAmount, + fourthAmount, + testERC1155Two.address + ), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[0, 3]], [[1, 3]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(7); + + await marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + }); + }); + + describe("Fulfill Available Orders", async () => { + it("Can fulfill a single order via fulfillAvailableOrders", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address, + 10 + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [toFulfillmentComponents([[0, 0]])]; + + const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]].map( + toFulfillmentComponents + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [order], + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Can fulfill a single order via fulfillAvailableAdvancedOrders", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address, + 11 + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [[{ orderIndex: 0, itemIndex: 0 }]]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + ]; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Can fulfill a single order via fulfillAvailableAdvancedOrders with recipient specified", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [[{ orderIndex: 0, itemIndex: 0 }]]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + ]; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + owner.address, + 100, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + recipient: owner.address, + }, + ]); + + return receipt; + }); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableOrders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 1, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + toFulfillmentComponents([ + [0, 0], + [1, 0], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [0, 2], + [1, 2], + ], + ].map(toFulfillmentComponents); + + await withBalanceChecks( + [orderOne, orderTwo], + 0, + undefined, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [orderOne, orderTwo], + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + ], + [], + [], + false, + 2 + ); + return receipt; + }, + 2 + ); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders including restricted orders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 1, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // test orderHashes + orderOne.extraData = ethers.utils.defaultAbiCoder.encode( + ["bytes32[]"], + [[orderHashOne, orderHashTwo]] + ); + + expect((orderOne.extraData.length - 2) / 64).to.equal(4); + + const offerComponents = [ + toFulfillmentComponents([ + [0, 0], + [1, 0], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [0, 2], + [1, 2], + ], + ].map(toFulfillmentComponents); + + await withBalanceChecks( + [orderOne, orderTwo], + 0, + undefined, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + ], + [], + [], + false, + 2 + ); + return receipt; + }, + 2 + ); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableOrders with OOR offer / consideration items", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 1, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + toFulfillmentComponents([ + [0, 0], + [1, 0], + [1, 5], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [0, 2], + [1, 2], + [1, 5], + ], + ].map(toFulfillmentComponents); + + await withBalanceChecks( + [orderOne, orderTwo], + 0, + undefined, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [orderOne, orderTwo], + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + ], + [], + [], + false, + 2 + ); + return receipt; + }, + 2 + ); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableOrders with OOR initial offer / consideration items", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 1, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + toFulfillmentComponents([ + [1, 5], + [0, 0], + [1, 0], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [1, 5], + [0, 2], + [1, 2], + ], + ].map(toFulfillmentComponents); + + await withBalanceChecks( + [orderOne, orderTwo], + 0, + undefined, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [orderOne, orderTwo], + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + ], + [], + [], + false, + 2 + ); + return receipt; + }, + 2 + ); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 2, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + toFulfillmentComponents([ + [0, 0], + [1, 0], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [0, 2], + [1, 2], + ], + ].map(toFulfillmentComponents); + + await withBalanceChecks( + [orderOne, orderTwo], + 0, + undefined, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + { + order: orderTwo, + orderHash: orderHashTwo, + fulfiller: buyer.address, + }, + ], + [], + [], + false, + 2 + ); + return receipt; + }, + 2 + ); + }); + it("Can fulfill and aggregate a max number of multiple orders via fulfillAvailableOrders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 3, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + ]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + ], + [ + { orderIndex: 0, itemIndex: 1 }, + { orderIndex: 1, itemIndex: 1 }, + ], + [ + { orderIndex: 0, itemIndex: 2 }, + { orderIndex: 1, itemIndex: 2 }, + ], + ]; + + await withBalanceChecks( + [orderOne], + 0, + undefined, + async () => { + const { executions } = await marketplaceContract + .connect(buyer) + .callStatic.fulfillAvailableOrders( + [orderOne, orderTwo], + offerComponents, + considerationComponents, + toKey(0), + 1, + { + value: value.mul(2), + } + ); + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [orderOne, orderTwo], + offerComponents, + considerationComponents, + toKey(0), + 1, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + ], + executions + ); + + return receipt; + }, + 1 + ); + }); + it("Can fulfill and aggregate a max number of multiple orders via fulfillAvailableAdvancedOrders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 4, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + ], + [ + { orderIndex: 0, itemIndex: 1 }, + { orderIndex: 1, itemIndex: 1 }, + ], + [ + { orderIndex: 0, itemIndex: 2 }, + { orderIndex: 1, itemIndex: 2 }, + ], + ]; + + await withBalanceChecks( + [orderOne], + 0, + undefined, + async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 1, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + ], + [], + [], + false, + 1 + ); + + return receipt; + }, + 1 + ); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableOrders with failing orders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 5, + 100000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // second order is expired + const { order: orderTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "EXPIRED" + ); + + // third order will be cancelled + const { + order: orderThree, + orderHash: orderHashThree, + orderComponents, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // can cancel it + await expect( + marketplaceContract.connect(seller).cancel([orderComponents]) + ) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHashThree, seller.address, zone.address); + + // fourth order will be filled + const { order: orderFour, orderHash: orderHashFour } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // can fill it + await withBalanceChecks([orderFour], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(orderFour, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderFour, + orderHash: orderHashFour, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + + const offerComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + { orderIndex: 3, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + { orderIndex: 3, itemIndex: 0 }, + ], + [ + { orderIndex: 0, itemIndex: 1 }, + { orderIndex: 1, itemIndex: 1 }, + { orderIndex: 2, itemIndex: 1 }, + { orderIndex: 3, itemIndex: 1 }, + ], + [ + { orderIndex: 0, itemIndex: 2 }, + { orderIndex: 1, itemIndex: 2 }, + { orderIndex: 2, itemIndex: 2 }, + { orderIndex: 3, itemIndex: 2 }, + ], + ]; + + await withBalanceChecks([orderOne], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [orderOne, orderTwo, orderThree, orderFour], + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value: value.mul(4), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders with failing orders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 6, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // second order is expired + const { order: orderTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "EXPIRED" + ); + + // third order will be cancelled + const { + order: orderThree, + orderHash: orderHashThree, + orderComponents, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // can cancel it + await expect( + marketplaceContract.connect(seller).cancel([orderComponents]) + ) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHashThree, seller.address, zone.address); + + // fourth order will be filled + const { order: orderFour, orderHash: orderHashFour } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // can fill it + await withBalanceChecks([orderFour], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(orderFour, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderFour, + orderHash: orderHashFour, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + + const offerComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + { orderIndex: 3, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + { orderIndex: 3, itemIndex: 0 }, + ], + [ + { orderIndex: 0, itemIndex: 1 }, + { orderIndex: 1, itemIndex: 1 }, + { orderIndex: 2, itemIndex: 1 }, + { orderIndex: 3, itemIndex: 1 }, + ], + [ + { orderIndex: 0, itemIndex: 2 }, + { orderIndex: 1, itemIndex: 2 }, + { orderIndex: 2, itemIndex: 2 }, + { orderIndex: 3, itemIndex: 2 }, + ], + ]; + + await withBalanceChecks([orderOne], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo, orderThree, orderFour], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.mul(4), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders with failing components including criteria", async () => { + // Seller mints first nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 1, + 7, + 10000 + ); + + // Seller mints second nft + + // Seller mints nfts for criteria-based item + const criteriaNftId = randomBN(); + const secondCriteriaNFTId = randomBN(); + const thirdCriteriaNFTId = randomBN(); + + await testERC721.mint(seller.address, criteriaNftId); + await testERC721.mint(seller.address, secondCriteriaNFTId); + await testERC721.mint(seller.address, thirdCriteriaNFTId); + + const tokenIds = [criteriaNftId, secondCriteriaNFTId, thirdCriteriaNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const offerTwo = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(1, 0, 0, criteriaNftId, proofs[criteriaNftId.toString()]), + ]; + + const { + order: orderOne, + orderHash: orderHashOne, + value, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // second order is expired + const { order: orderTwo } = await createOrder( + seller, + zone, + offerTwo, + consideration, + 0, // FULL_OPEN + criteriaResolvers, + "EXPIRED" + ); + + const offerComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + ]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + ], + [ + { orderIndex: 0, itemIndex: 1 }, + { orderIndex: 1, itemIndex: 1 }, + ], + [ + { orderIndex: 0, itemIndex: 2 }, + { orderIndex: 1, itemIndex: 2 }, + ], + ]; + + await withBalanceChecks([orderOne], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo], + criteriaResolvers, + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderOne, + orderHash: orderHashOne, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + }); + + describe("Match Orders (send excess offer items to recipient)", async () => { + it("Return unspent offer items to caller", async () => { + // Mint 1155 tokens + const { nftId, amount } = await mint1155(seller); + + // Generate random amount of tokens not to spend. + const unspentAmount = toBN(randomBN(2)); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const counter = await marketplaceContract.getCounter(buyer.address); + + const mirrorOrderParameters = { + offerer: buyer.address, + zone: zone.address, + offer: [getItemETH(parseEther("12"), parseEther("12"))], + consideration: [ + getTestItem1155( + nftId, + amount.sub(unspentAmount), + amount.sub(unspentAmount), + undefined, + buyer.address + ), + ], + totalOriginalConsiderationItems: 1, + orderType: order.parameters.orderType, // FULL_OPEN + zoneHash: "0x".padEnd(66, "0"), + salt: randomHex(), + conduitKey: constants.HashZero, + startTime: order.parameters.startTime, + endTime: order.parameters.endTime, + }; + + const mirrorOrderComponents = { + ...mirrorOrderParameters, + counter, + }; + + const flatSig = await signOrder(mirrorOrderComponents, buyer); + + const mirrorOrder = { + parameters: mirrorOrderParameters, + signature: flatSig, + numerator: order.numerator, // only used for advanced orders + denominator: order.denominator, // only used for advanced orders + extraData: "0x", // only used for advanced orders + }; + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + + // Check that unspent offer items are sent back to caller. + expect(await testERC1155.balanceOf(owner.address, nftId)).to.equal( + unspentAmount + ); + + return receipt; + }); + + it("Return unspent offer items to specified recipient", async () => { + // Mint 1155 tokens + const { nftId, amount } = await mint1155(seller); + + // Generate random amount of tokens not to spend. + const unspentAmount = toBN(randomBN(2)); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const counter = await marketplaceContract.getCounter(buyer.address); + + const mirrorOrderParameters = { + offerer: buyer.address, + zone: zone.address, + offer: [getItemETH(parseEther("12"), parseEther("12"))], + consideration: [ + getTestItem1155( + nftId, + amount.sub(unspentAmount), + amount.sub(unspentAmount), + undefined, + buyer.address + ), + ], + totalOriginalConsiderationItems: 1, + orderType: order.parameters.orderType, // FULL_OPEN + zoneHash: "0x".padEnd(66, "0"), + salt: randomHex(), + conduitKey: constants.HashZero, + startTime: order.parameters.startTime, + endTime: order.parameters.endTime, + }; + + const mirrorOrderComponents = { + ...mirrorOrderParameters, + counter, + }; + + const flatSig = await signOrder(mirrorOrderComponents, buyer); + + const mirrorOrder = { + parameters: mirrorOrderParameters, + signature: flatSig, + numerator: order.numerator, // only used for advanced orders + denominator: order.denominator, // only used for advanced orders + extraData: "0x", // only used for advanced orders + }; + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + [], + fulfillments, + seller.address, + { + value, + } + ); + const receipt = await (await tx).wait(); + + // Check that unspent offer items are sent back to caller. + expect(await testERC1155.balanceOf(seller.address, nftId)).to.equal( + unspentAmount + ); + + return receipt; + }); + }); +}); diff --git a/test/basic.spec.ts b/test/basic.spec.ts new file mode 100644 index 000000000..ccd939394 --- /dev/null +++ b/test/basic.spec.ts @@ -0,0 +1,4387 @@ +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers, network } from "hardhat"; + +import { deployContract } from "./utils/contracts"; +import { + convertSignatureToEIP2098, + defaultAcceptOfferMirrorFulfillment, + defaultBuyNowMirrorFulfillment, + getBasicOrderExecutions, + getBasicOrderParameters, + getItemETH, + random128, + randomBN, + randomHex, + toAddress, + toBN, + toKey, +} from "./utils/encoding"; +import { faucet } from "./utils/faucet"; +import { seaportFixture } from "./utils/fixtures"; +import { + VERSION, + minRandom, + simulateAdvancedMatchOrders, + simulateMatchOrders, +} from "./utils/helpers"; + +import type { + ConduitInterface, + ConsiderationInterface, + EIP1271Wallet, + EIP1271Wallet__factory, + TestERC20, + TestERC721, + TestPostExecution, + TestZone, +} from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { Wallet } from "ethers"; + +const { parseEther, keccak256 } = ethers.utils; + +/** + * Buy now or accept offer for a single ERC721 or ERC1155 in exchange for + * ETH, WETH or ERC20 + */ +describe(`Basic buy now or accept offer flows (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let conduitKeyOne: string; + let conduitOne: ConduitInterface; + let EIP1271WalletFactory: EIP1271Wallet__factory; + let marketplaceContract: ConsiderationInterface; + let stubZone: TestZone; + let postExecutionZone: TestPostExecution; + let testERC20: TestERC20; + let testERC721: TestERC721; + + let checkExpectedEvents: SeaportFixtures["checkExpectedEvents"]; + let createMirrorAcceptOfferOrder: SeaportFixtures["createMirrorAcceptOfferOrder"]; + let createMirrorBuyNowOrder: SeaportFixtures["createMirrorBuyNowOrder"]; + let createOrder: SeaportFixtures["createOrder"]; + let getTestItem1155: SeaportFixtures["getTestItem1155"]; + let getTestItem20: SeaportFixtures["getTestItem20"]; + let getTestItem721: SeaportFixtures["getTestItem721"]; + let mint721: SeaportFixtures["mint721"]; + let mintAndApprove1155: SeaportFixtures["mintAndApprove1155"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; + let withBalanceChecks: SeaportFixtures["withBalanceChecks"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + checkExpectedEvents, + conduitKeyOne, + conduitOne, + createMirrorAcceptOfferOrder, + createMirrorBuyNowOrder, + createOrder, + EIP1271WalletFactory, + getTestItem1155, + getTestItem20, + getTestItem721, + marketplaceContract, + mint721, + mintAndApprove1155, + mintAndApprove721, + mintAndApproveERC20, + set721ApprovalForAll, + stubZone, + postExecutionZone, + testERC20, + testERC721, + withBalanceChecks, + } = await seaportFixture(owner)); + }); + + let seller: Wallet; + let buyer: Wallet; + let zone: Wallet; + + let sellerContract: EIP1271Wallet; + + async function setupFixture() { + // Setup basic buyer/seller wallets with ETH + const seller = new ethers.Wallet(randomHex(32), provider); + const buyer = new ethers.Wallet(randomHex(32), provider); + const zone = new ethers.Wallet(randomHex(32), provider); + + const sellerContract = await EIP1271WalletFactory.deploy(seller.address); + + for (const wallet of [seller, buyer, zone, sellerContract]) { + await faucet(wallet.address, provider); + } + + return { seller, buyer, zone, sellerContract }; + } + + beforeEach(async () => { + ({ seller, buyer, zone, sellerContract } = await loadFixture(setupFixture)); + }); + + describe("A single ERC721 is to be transferred", async () => { + describe("[Buy now] User fulfills a sell order for a single ERC721", async () => { + it("ERC721 <=> ETH (standard)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + return receipt; + }); + }); + it("ERC721 <=> ETH (standard via conduit)", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (standard with tip)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // Add a tip + order.parameters.consideration.push( + getItemETH(parseEther("1"), parseEther("1"), owner.address) + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value: value.add(parseEther("1")), + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (standard with restricted order)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (standard with restricted order and extra data)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + order.extraData = "0x1234"; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (standard with restricted order, specified recipient and extra data)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + order.extraData = "0x1234"; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), owner.address, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + recipient: owner.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic, minimal and listed off-chain)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic, minimal and listed off-chain, efficient endpoint)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder_efficient_6GL6yc(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic, minimal and verified on-chain)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + // Validate the order from any account + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal( + suppliedConsideration.token + ); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic, minimal and verified on-chain, efficient endpoint)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + // Validate the order from any account + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal( + suppliedConsideration.token + ); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder_efficient_6GL6yc(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (standard, minimal and listed off-chain)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (standard, minimal and verified on-chain)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(toBN(1), toBN(1), ethers.constants.AddressZero), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + // Validate the order from any account + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal( + suppliedConsideration.token + ); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (advanced, minimal and listed off-chain)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (advanced, minimal and verified on-chain)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; + + const { order, orderHash, value } = await createOrder( + seller, + ethers.constants.AddressZero, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + ethers.constants.HashZero, + true // extraCheap + ); + + // Validate the order from any account + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal( + suppliedConsideration.token + ); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic with tips)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order, + false, + [ + { + amount: parseEther("2"), + recipient: `0x0000000000000000000000000000000000000001`, + }, + { + amount: parseEther("3"), + recipient: `0x0000000000000000000000000000000000000002`, + }, + { + amount: parseEther("4"), + recipient: `0x0000000000000000000000000000000000000003`, + }, + ] + ); + + order.parameters.consideration.push( + getItemETH( + parseEther("2"), + parseEther("2"), + "0x0000000000000000000000000000000000000001" + ) + ); + + order.parameters.consideration.push( + getItemETH( + parseEther("3"), + parseEther("3"), + "0x0000000000000000000000000000000000000002" + ) + ); + + order.parameters.consideration.push( + getItemETH( + parseEther("4"), + parseEther("4"), + "0x0000000000000000000000000000000000000003" + ) + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: value.add(parseEther("9")), + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic via conduit)", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic with restricted order)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic with partial restricted order)", async () => { + // Seller mints nft + const nftId = randomBN(); + await testERC721.mint(seller.address, nftId); + + // Seller approves marketplace contract to transfer NFT + await expect( + testERC721 + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + { + itemType: 0, // ETH + token: ethers.constants.AddressZero, + identifierOrCriteria: toBN(0), // ignored for ETH + startAmount: ethers.utils.parseEther("10"), + endAmount: ethers.utils.parseEther("10"), + recipient: seller.address, + }, + { + itemType: 0, // ETH + token: ethers.constants.AddressZero, + identifierOrCriteria: toBN(0), // ignored for ETH + startAmount: ethers.utils.parseEther("1"), + endAmount: ethers.utils.parseEther("1"), + recipient: zone.address, + }, + { + itemType: 0, // ETH + token: ethers.constants.AddressZero, + identifierOrCriteria: toBN(0), // ignored for ETH + startAmount: ethers.utils.parseEther("1"), + endAmount: ethers.utils.parseEther("1"), + recipient: owner.address, + }, + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 3 // PARTIAL_RESTRICTED + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { value }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { order, orderHash, fulfiller: buyer.address }, + ]); + + return receipt; + }); + }); + + it("ERC721 <=> ETH (basic, already validated)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // Validate the order from any account + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal( + suppliedConsideration.token + ); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic, EIP-2098 signature)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // Convert signature to EIP 2098 + expect(order.signature.length).to.equal(132); + order.signature = convertSignatureToEIP2098(order.signature); + expect(order.signature.length).to.equal(130); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic, extra ether supplied and returned to caller)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: value.add(1), + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (match)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC721 <=> ETH (match via conduit)", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC721 <=> ETH (match, extra eth supplied and returned to caller)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value: value.add(101), + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC721 <=> ERC20 (standard)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + return receipt; + }); + }); + it("ERC721 <=> ERC20 (standard via conduit)", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (basic)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (basic via conduit)", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (basic, EIP-1271 signature)", async () => { + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (EIP-1271 signature on non-ECDSA 64 bytes)", async () => { + const sellerContract = await deployContract( + "EIP1271Wallet", + seller, + seller.address + ); + + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller + ); + + const signature = `0x`.padEnd(130, "f"); + + const basicOrderParameters = { + ...getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ), + signature, + }; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (EIP-1271 signature on non-ECDSA 65 bytes)", async () => { + const sellerContract = await deployContract( + "EIP1271Wallet", + seller, + seller.address + ); + + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller + ); + + // Compute the digest based on the order hash + const { domainSeparator } = await marketplaceContract.information(); + const digest = keccak256( + `0x1901${domainSeparator.slice(2)}${orderHash.slice(2)}` + ); + + await sellerContract.registerDigest(digest, true); + + const signature = `0x`.padEnd(132, "f"); + + const basicOrderParameters = { + ...getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ), + signature, + }; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + + await sellerContract.registerDigest(digest, false); + }); + it("ERC721 <=> ERC20 (basic, EIP-1271 signature w/ non-standard length)", async () => { + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller + ); + + const basicOrderParameters = { + ...getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ), + signature: "0x", + }; + + // Fails before seller contract approves the digest (note that any + // non-standard signature length is treated as a contract signature) + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.revertedWithCustomError( + marketplaceContract, + "BadContractSignature" + ); + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.reverted; + } + + // Compute the digest based on the order hash + const { domainSeparator } = await marketplaceContract.information(); + const digest = keccak256( + `0x1901${domainSeparator.slice(2)}${orderHash.slice(2)}` + ); + + // Seller approves the digest + await sellerContract.connect(seller).registerDigest(digest, true); + + // Now it succeeds + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (match)", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC721 <=> ERC20 (match via conduit)", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + }); + describe("[Accept offer] User accepts a buy offer on a single ERC721", async () => { + // Note: ETH is not a possible case + it("ERC721 <=> ERC20 (standard)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Buyer approves marketplace contract to transfer ERC20 tokens too + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ETH (restricted order checked post-execution)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Buyer approves marketplace contract to transfer ERC20 tokens too + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + postExecutionZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + order.extraData = "0x1234"; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (standard, via conduit)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20(seller, conduitOne.address, tokenAmount); + + // Buyer approves marketplace contract to transfer ERC20 tokens + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (standard, fulfilled via conduit)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves conduit contract to transfer NFT + await set721ApprovalForAll(buyer, conduitOne.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Buyer approves conduit to transfer ERC20 tokens + await expect( + testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, conduitOne.address, tokenAmount); + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, conduitKeyOne); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: conduitKeyOne, + }, + ]); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (basic)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic simple with restricted order checked post-execution)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [getTestItem721(nftId, 1, 1, seller.address)]; + + const { order, orderHash } = await createOrder( + seller, + postExecutionZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic with restricted order checked post-execution and tips)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [getTestItem721(nftId, 1, 1, seller.address)]; + + const { order, orderHash } = await createOrder( + seller, + postExecutionZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + order.parameters.consideration.push( + getTestItem20(50, 50, zone.address) + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order + ); + + basicOrderParameters.totalOriginalAdditionalRecipients = toBN(0); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC721 <=> ETH (basic with restricted order checked post-execution)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + postExecutionZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (basic, many via conduit)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20(seller, conduitOne.address, tokenAmount); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(1, 1, zone.address), + ]; + + for (let i = 1; i <= 50; ++i) { + consideration.push(getTestItem20(i, i, toAddress(i + 10000))); + } + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (basic, fulfilled via conduit)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves conduit contract to transfer NFT + await set721ApprovalForAll(buyer, conduitOne.address, true); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order, + conduitKeyOne + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: conduitKeyOne, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC721 <=> ERC20 (match)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder(buyer, zone, order); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC721 <=> ERC20 (restriced match checked post-execution)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves marketplace contract to transfer NFT + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + postExecutionZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + order.extraData = "0x1234"; + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder(buyer, zone, order); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateAdvancedMatchOrders( + marketplaceContract, + [order, mirrorOrder], + [], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + [], + fulfillments, + ethers.constants.AddressZero + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC721 <=> ERC20 (match via conduit)", async () => { + // Buyer mints nft + const nftId = await mint721(buyer); + + // Buyer approves conduit contract to transfer NFT + await set721ApprovalForAll(buyer, conduitOne.address, true); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + [], + conduitKeyOne + ); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + }); + }); + + describe("A single ERC1155 is to be transferred", async () => { + describe("[Buy now] User fulfills a sell order for a single ERC1155", async () => { + it("ERC1155 <=> ETH (standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ETH (standard via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + conduitOne.address + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ETH (basic)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 1, // EthForERC1155 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ETH (basic via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + conduitOne.address + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const basicOrderParameters = getBasicOrderParameters( + 1, // EthForERC1155 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ETH (match)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ETH (match via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + conduitOne.address + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ERC20 (standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (standard via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + conduitOne.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (basic)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 3, // ERC20ForERC1155 + order + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, conduitKeyOne) + ); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (basic via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + conduitOne.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const basicOrderParameters = getBasicOrderParameters(3, order); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (match)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ERC20 (match via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + conduitOne.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem1155(nftId, amount, amount)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + }); + describe("[Accept offer] User accepts a buy offer on a single ERC1155", async () => { + // Note: ETH is not a possible case + it("ERC1155 <=> ERC20 (standard)", async () => { + // Buyer mints nft + const { nftId, amount } = await mintAndApprove1155( + buyer, + marketplaceContract.address + ); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Buyer approves marketplace contract to transfer ERC20 tokens too + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem1155(nftId, amount, amount, undefined, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0)); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (standard, fulfilled via conduit)", async () => { + // Buyer mints nft + const { nftId, amount } = await mintAndApprove1155( + buyer, + conduitOne.address + ); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // Buyer approves conduit to transfer ERC20 tokens + await expect( + testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, conduitOne.address, tokenAmount); + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem1155(nftId, amount, amount, undefined, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, conduitKeyOne); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: conduitKeyOne, + }, + ]); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (basic)", async () => { + // Buyer mints nft + const { nftId, amount } = await mintAndApprove1155( + buyer, + marketplaceContract.address + ); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem1155(nftId, amount, amount, undefined, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 5, // ERC1155ForERC20 + order + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ], + getBasicOrderExecutions(order, buyer.address, "") + ); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (basic, fulfilled via conduit)", async () => { + // Buyer mints nft + const { nftId, amount } = await mintAndApprove1155( + buyer, + conduitOne.address + ); + + // Seller mints ERC20 + const tokenAmount = toBN(random128()); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem1155(nftId, amount, amount, undefined, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 5, // ERC1155ForERC20 + order, + conduitKeyOne + ); + + await withBalanceChecks([order], toBN(0), undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters); + + const executions = getBasicOrderExecutions( + order, + buyer.address, + conduitKeyOne + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: conduitKeyOne, + }, + ], + executions + ); + + return receipt; + }); + }); + it("ERC1155 <=> ERC20 (match)", async () => { + // Buyer mints nft + const { nftId, amount } = await mintAndApprove1155( + buyer, + marketplaceContract.address + ); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem1155(nftId, amount, amount, undefined, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder(buyer, zone, order); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("ERC1155 <=> ERC20 (match via conduit)", async () => { + // Buyer mints nft + const { nftId, amount } = await mintAndApprove1155( + buyer, + conduitOne.address + ); + + // Seller mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + seller, + marketplaceContract.address, + tokenAmount + ); + + // NOTE: Buyer does not need to approve marketplace for ERC20 tokens + + const offer = [ + getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), + ]; + + const consideration = [ + getTestItem1155(nftId, amount, amount, undefined, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = + await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + [], + conduitKeyOne + ); + + const fulfillments = defaultAcceptOfferMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + }); + }); +}); diff --git a/test/conduit.spec.ts b/test/conduit.spec.ts new file mode 100644 index 000000000..960ec4787 --- /dev/null +++ b/test/conduit.spec.ts @@ -0,0 +1,1479 @@ +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { randomInt } from "crypto"; +import { ethers, network } from "hardhat"; + +import { deployContract } from "./utils/contracts"; +import { + getItemETH, + random128, + randomBN, + randomHex, + toAddress, + toBN, + toFulfillment, +} from "./utils/encoding"; +import { faucet } from "./utils/faucet"; +import { + fixtureERC1155, + fixtureERC20, + fixtureERC721, + seaportFixture, +} from "./utils/fixtures"; +import { + VERSION, + getCustomRevertSelector, + minRandom, + simulateMatchOrders, +} from "./utils/helpers"; + +import type { + ConduitControllerInterface, + ConduitInterface, + Conduit__factory, + ConsiderationInterface, + TestERC1155, + TestERC20, + TestERC721, +} from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { Wallet } from "ethers"; + +const { parseEther } = ethers.utils; + +describe(`Conduit tests (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let conduitController: ConduitControllerInterface; + let conduitImplementation: Conduit__factory; + let conduitKeyOne: string; + let conduitOne: ConduitInterface; + let marketplaceContract: ConsiderationInterface; + let testERC1155: TestERC1155; + let testERC1155Two: TestERC1155; + let testERC20: TestERC20; + let testERC721: TestERC721; + + let createMirrorBuyNowOrder: SeaportFixtures["createMirrorBuyNowOrder"]; + let createOrder: SeaportFixtures["createOrder"]; + let createTransferWithApproval: SeaportFixtures["createTransferWithApproval"]; + let deployNewConduit: SeaportFixtures["deployNewConduit"]; + let getTestItem1155: SeaportFixtures["getTestItem1155"]; + let mint1155: SeaportFixtures["mint1155"]; + let mint721: SeaportFixtures["mint721"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + let set1155ApprovalForAll: SeaportFixtures["set1155ApprovalForAll"]; + let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + conduitController, + conduitImplementation, + conduitKeyOne, + conduitOne, + createMirrorBuyNowOrder, + createOrder, + createTransferWithApproval, + deployNewConduit, + getTestItem1155, + marketplaceContract, + mint1155, + mint721, + mintAndApproveERC20, + set1155ApprovalForAll, + set721ApprovalForAll, + testERC1155, + testERC1155Two, + testERC20, + testERC721, + } = await seaportFixture(owner)); + }); + + let seller: Wallet; + let buyer: Wallet; + let zone: Wallet; + + let tempConduit: ConduitInterface; + + async function setupFixture() { + // Setup basic buyer/seller wallets with ETH + const seller = new ethers.Wallet(randomHex(32), provider); + const buyer = new ethers.Wallet(randomHex(32), provider); + const zone = new ethers.Wallet(randomHex(32), provider); + + // Deploy a new conduit + const tempConduit = await deployNewConduit(owner); + + for (const wallet of [seller, buyer, zone]) { + await faucet(wallet.address, provider); + } + + return { seller, buyer, zone, tempConduit }; + } + + beforeEach(async () => { + ({ seller, buyer, zone, tempConduit } = await loadFixture(setupFixture)); + }); + + it("Adds a channel, and executes transfers (ERC1155 with batch)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + + await testERC1155.mint(seller.address, nftId, amount.mul(2)); + await testERC1155.mint(seller.address, secondNftId, secondAmount.mul(2)); + await set1155ApprovalForAll(seller, tempConduit.address, true); + + await tempConduit.connect(seller).executeWithBatch1155( + [], + [ + { + token: testERC1155.address, + from: seller.address, + to: buyer.address, + ids: [nftId, secondNftId], + amounts: [amount, secondAmount], + }, + { + token: testERC1155.address, + from: seller.address, + to: buyer.address, + ids: [secondNftId, nftId], + amounts: [secondAmount, amount], + }, + ] + ); + }); + + it("Adds a channel, and executes only batch transfers (ERC1155 with batch)", async () => { + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + + await testERC1155.mint(seller.address, nftId, amount.mul(2)); + await testERC1155.mint(seller.address, secondNftId, secondAmount.mul(2)); + await set1155ApprovalForAll(seller, tempConduit.address, true); + + await tempConduit.connect(seller).executeBatch1155([ + { + token: testERC1155.address, + from: seller.address, + to: buyer.address, + ids: [nftId, secondNftId], + amounts: [amount, secondAmount], + }, + { + token: testERC1155.address, + from: seller.address, + to: buyer.address, + ids: [secondNftId, nftId], + amounts: [secondAmount, amount], + }, + ]); + }); + + it("Adds a channel, and executes transfers (ERC721)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + // Seller mints nft + const nftId = randomBN(); + await testERC721.mint(seller.address, nftId); + + const secondNftId = randomBN(); + await testERC721.mint(seller.address, secondNftId); + + // Check ownership + expect(await testERC721.ownerOf(nftId)).to.equal(seller.address); + expect(await testERC721.ownerOf(secondNftId)).to.equal(seller.address); + + await expect( + testERC721.connect(seller).setApprovalForAll(tempConduit.address, true) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(seller.address, tempConduit.address, true); + + await tempConduit.connect(seller).execute([ + { + itemType: 2, // ERC721 + token: testERC721.address, + from: seller.address, + to: buyer.address, + identifier: nftId, + amount: ethers.BigNumber.from(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + from: seller.address, + to: buyer.address, + identifier: secondNftId, + amount: ethers.BigNumber.from(1), + }, + ]); + + // Check ownership + expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address); + expect(await testERC721.ownerOf(secondNftId)).to.equal(buyer.address); + }); + + it("Adds a channel, and executes transfers (ERC721 + ERC20)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + // Seller mints nft + const nftId = randomBN(); + await testERC721.mint(seller.address, nftId); + + // Check ownership + expect(await testERC721.ownerOf(nftId)).to.equal(seller.address); + + // Set approval of nft + await expect( + testERC721.connect(seller).setApprovalForAll(tempConduit.address, true) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(seller.address, tempConduit.address, true); + + const tokenAmount = minRandom(100); + await testERC20.mint(seller.address, tokenAmount); + + // Check balance + expect(await testERC20.balanceOf(seller.address)).to.equal(tokenAmount); + + // Seller approves conduit contract to transfer tokens + await expect( + testERC20.connect(seller).approve(tempConduit.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(seller.address, tempConduit.address, tokenAmount); + + // Send an ERC721 and (token amount - 100) ERC20 tokens + await tempConduit.connect(seller).execute([ + { + itemType: 2, // ERC721 + token: testERC721.address, + from: seller.address, + to: buyer.address, + identifier: nftId, + amount: ethers.BigNumber.from(1), + }, + { + itemType: 1, // ERC20 + token: testERC20.address, + from: seller.address, + to: buyer.address, + identifier: 0, + amount: tokenAmount.sub(100), + }, + ]); + + // Check ownership + expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address); + // Check balance + expect(await testERC20.balanceOf(seller.address)).to.equal(100); + expect(await testERC20.balanceOf(buyer.address)).to.equal( + tokenAmount.sub(100) + ); + }); + + it("Adds a channel, and executes transfers (ERC721 + ERC1155)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + // Seller mints nft + const nftId = randomBN(); + await testERC721.mint(seller.address, nftId); + + // Check ownership + expect(await testERC721.ownerOf(nftId)).to.equal(seller.address); + + // Set approval of nft + await expect( + testERC721.connect(seller).setApprovalForAll(tempConduit.address, true) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(seller.address, tempConduit.address, true); + + const secondNftId = random128(); + const amount = random128().add(1); + await testERC1155.mint(seller.address, secondNftId, amount); + + await expect( + testERC1155.connect(seller).setApprovalForAll(tempConduit.address, true) + ) + .to.emit(testERC1155, "ApprovalForAll") + .withArgs(seller.address, tempConduit.address, true); + + // Check ownership + expect(await testERC1155.balanceOf(seller.address, secondNftId)).to.equal( + amount + ); + + // Send an ERC721 and ERC1155 + await tempConduit.connect(seller).execute([ + { + itemType: 2, // ERC721 + token: testERC721.address, + from: seller.address, + to: buyer.address, + identifier: nftId, + amount: ethers.BigNumber.from(1), + }, + { + itemType: 3, // ERC1155 + token: testERC1155.address, + from: seller.address, + to: buyer.address, + identifier: secondNftId, + amount: amount.sub(10), + }, + ]); + + // Check ownership + expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address); + // Check balance + expect(await testERC1155.balanceOf(seller.address, secondNftId)).to.equal( + 10 + ); + expect(await testERC1155.balanceOf(buyer.address, secondNftId)).to.equal( + amount.sub(10) + ); + }); + + it("Adds a channel, and executes transfers (ERC20 + ERC1155)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + // Seller mints nft + const tokenAmount = minRandom(100).div(100); + await testERC20.mint(seller.address, tokenAmount); + + // Check balance + expect(await testERC20.balanceOf(seller.address)).to.equal(tokenAmount); + + // Seller approves conduit contract to transfer tokens + await expect( + testERC20.connect(seller).approve(tempConduit.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(seller.address, tempConduit.address, tokenAmount); + + const nftId = random128(); + const erc1155amount = random128().add(1); + await testERC1155.mint(seller.address, nftId, erc1155amount); + + await expect( + testERC1155.connect(seller).setApprovalForAll(tempConduit.address, true) + ) + .to.emit(testERC1155, "ApprovalForAll") + .withArgs(seller.address, tempConduit.address, true); + + // Check ownership + expect(await testERC1155.balanceOf(seller.address, nftId)).to.equal( + erc1155amount + ); + + // Send an ERC20 and ERC1155 + await tempConduit.connect(seller).execute([ + { + itemType: 1, // ERC20 + token: testERC20.address, + from: seller.address, + to: buyer.address, + identifier: 0, + amount: tokenAmount.sub(100), + }, + { + itemType: 3, // ERC1155 + token: testERC1155.address, + from: seller.address, + to: buyer.address, + identifier: nftId, + amount: erc1155amount.sub(10), + }, + ]); + + // Check balance + expect(await testERC20.balanceOf(seller.address)).to.equal(100); + expect(await testERC20.balanceOf(buyer.address)).to.equal( + tokenAmount.sub(100) + ); + expect(await testERC1155.balanceOf(seller.address, nftId)).to.equal(10); + expect(await testERC1155.balanceOf(buyer.address, nftId)).to.equal( + erc1155amount.sub(10) + ); + }); + + it("Adds a channel, and executes transfers (ERC20 + ERC721 + ERC1155)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + // Create/Approve X amount of ERC20s + const erc20Transfer = await createTransferWithApproval( + testERC20, + seller, + 1, + tempConduit.address, + seller.address, + buyer.address + ); + + // Create/Approve Y amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + testERC721, + seller, + 2, + tempConduit.address, + seller.address, + buyer.address + ); + + // Create/Approve Z amount of ERC1155s + const erc1155Transfer = await createTransferWithApproval( + testERC1155, + seller, + 3, + tempConduit.address, + seller.address, + buyer.address + ); + + // Send an ERC20, ERC721, and ERC1155 + await tempConduit + .connect(seller) + .execute([erc20Transfer, erc721Transfer, erc1155Transfer]); + + // Check ownership + expect(await testERC721.ownerOf(erc721Transfer.identifier)).to.equal( + buyer.address + ); + // Check balance + expect(await testERC20.balanceOf(seller.address)).to.equal(0); + expect(await testERC20.balanceOf(buyer.address)).to.equal( + erc20Transfer.amount + ); + expect( + await testERC1155.balanceOf(seller.address, erc1155Transfer.identifier) + ).to.equal(0); + expect( + await testERC1155.balanceOf(buyer.address, erc1155Transfer.identifier) + ).to.equal(erc1155Transfer.amount); + }); + + it("Adds a channel, and executes transfers (many token types)", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + // Get 3 numbers whose value adds to Item Amount and minimum 1. + const itemsToCreate = 64; + const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); + const numERC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); + const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numERC721s); + + const erc20Contracts = []; + const erc20Transfers = []; + + const erc721Contracts = []; + const erc721Transfers = []; + + const erc1155Contracts = []; + const erc1155Transfers = []; + + // Create numERC20s amount of ERC20 objects + for (let i = 0; i < numERC20s; i++) { + // Deploy Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + // Create/Approve X amount of ERC20s + const erc20Transfer = await createTransferWithApproval( + tempERC20Contract, + seller, + 1, + tempConduit.address, + seller.address, + buyer.address + ); + erc20Contracts[i] = tempERC20Contract; + erc20Transfers[i] = erc20Transfer; + } + + // Create numERC721s amount of ERC20 objects + for (let i = 0; i < numERC721s; i++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + seller, + 2, + tempConduit.address, + seller.address, + buyer.address + ); + erc721Contracts[i] = tempERC721Contract; + erc721Transfers[i] = erc721Transfer; + } + + // Create numERC1155s amount of ERC1155 objects + for (let i = 0; i < numERC1155s; i++) { + // Deploy Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155(owner); + // Create/Approve numERC1155s amount of ERC1155s + const erc1155Transfer = await createTransferWithApproval( + tempERC1155Contract, + seller, + 3, + tempConduit.address, + seller.address, + buyer.address + ); + erc1155Contracts[i] = tempERC1155Contract; + erc1155Transfers[i] = erc1155Transfer; + } + + const transfers = [ + ...erc20Transfers, + ...erc721Transfers, + ...erc1155Transfers, + ]; + const contracts = [ + ...erc20Contracts, + ...erc721Contracts, + ...erc1155Contracts, + ]; + // Send the transfers + await tempConduit.connect(seller).execute(transfers); + + // Loop through all transfer to do ownership/balance checks + for (let i = 0; i < transfers.length; i++) { + // Get itemType, token, from, to, amount, identifier + const itemType = transfers[i].itemType; + const token = contracts[i]; + const from = transfers[i].from; + const to = transfers[i].to; + const amount = transfers[i].amount; + const identifier = transfers[i].identifier; + + switch (itemType) { + case 1: // ERC20 + // Check balance + expect( + await (token as typeof erc20Contracts[0]).balanceOf(from) + ).to.equal(0); + expect( + await (token as typeof erc20Contracts[0]).balanceOf(to) + ).to.equal(amount); + break; + case 2: // ERC721 + case 4: // ERC721_WITH_CRITERIA + expect( + await (token as typeof erc721Contracts[0]).ownerOf(identifier) + ).to.equal(to); + break; + case 3: // ERC1155 + case 5: // ERC1155_WITH_CRITERIA + // Check balance + expect(await token.balanceOf(from, identifier)).to.equal(0); + expect(await token.balanceOf(to, identifier)).to.equal(amount); + break; + } + } + }); + + it("Reverts on calls to batch transfer 1155 items with no contract on a conduit", async () => { + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, owner.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + + await set1155ApprovalForAll(owner, tempConduit.address, true); + + await expect( + tempConduit.connect(owner).executeWithBatch1155( + [], + [ + { + token: ethers.constants.AddressZero, + from: owner.address, + to: buyer.address, + ids: [nftId, secondNftId], + amounts: [amount, secondAmount], + }, + ] + ) + ).to.be.revertedWithCustomError(tempConduit, "NoContract"); + }); + + it("Reverts on calls to only batch transfer 1155 items with no contract on a conduit", async () => { + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, owner.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + + await set1155ApprovalForAll(owner, tempConduit.address, true); + + await expect( + tempConduit.connect(owner).executeBatch1155([ + { + token: ethers.constants.AddressZero, + from: owner.address, + to: buyer.address, + ids: [nftId, secondNftId], + amounts: [amount, secondAmount], + }, + ]) + ).to.be.revertedWithCustomError(tempConduit, "NoContract"); + }); + + it("ERC1155 batch transfer reverts with revert data if it has sufficient gas", async () => { + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + await expect( + tempConduit.connect(seller).executeWithBatch1155( + [], + [ + { + token: testERC1155.address, + from: seller.address, + to: buyer.address, + ids: [1], + amounts: [1], + }, + ] + ) + ).to.be.revertedWith("NOT_AUTHORIZED"); + }); + if (!process.env.REFERENCE) { + it("ERC1155 batch transfer sends no data", async () => { + const receiver = await deployContract("ERC1155BatchRecipient", owner); + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + const { nftId: thirdNftId, amount: thirdAmount } = await mint1155( + owner, + 2 + ); + + await testERC1155.mint(seller.address, nftId, amount.mul(2)); + await testERC1155.mint(seller.address, secondNftId, secondAmount.mul(2)); + await testERC1155.mint(seller.address, thirdNftId, thirdAmount.mul(2)); + await set1155ApprovalForAll(seller, tempConduit.address, true); + + await tempConduit.connect(seller).executeWithBatch1155( + [], + [ + { + token: testERC1155.address, + from: seller.address, + to: receiver.address, + ids: [nftId, secondNftId, thirdNftId], + amounts: [amount, secondAmount, thirdAmount], + }, + { + token: testERC1155.address, + from: seller.address, + to: receiver.address, + ids: [secondNftId, nftId], + amounts: [secondAmount, amount], + }, + ] + ); + }); + + it("ERC1155 batch transfer reverts with generic error if it has insufficient gas to copy revert data", async () => { + const receiver = await deployContract("ExcessReturnDataRecipient", owner); + // Owner updates conduit channel to allow seller access + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, seller.address, true); + + await expect( + tempConduit.connect(seller).executeWithBatch1155( + [], + [ + { + token: receiver.address, + from: seller.address, + to: receiver.address, + ids: [1], + amounts: [1], + }, + ] + ) + ) + .to.be.revertedWithCustomError( + tempConduit, + "ERC1155BatchTransferGenericFailure" + ) + .withArgs(receiver.address, seller.address, receiver.address, [1], [1]); + }); + } + + it("Makes batch transfer 1155 items through a conduit", async () => { + const tempConduitKey = owner.address + "ff00000000000000000000f1"; + + const { conduit: tempConduitAddress } = await conduitController.getConduit( + tempConduitKey + ); + + await conduitController + .connect(owner) + .createConduit(tempConduitKey, owner.address); + + const tempConduit = conduitImplementation.attach(tempConduitAddress); + + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, owner.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + + const { nftId: thirdNftId, amount: thirdAmount } = await mint1155(owner, 2); + + const { nftId: nftId4, amount: amount4 } = await mint1155(owner, 2); + + const { nftId: nftId5, amount: amount5 } = await mint1155(owner, 2); + + const { nftId: nftId6, amount: amount6 } = await mint1155(owner, 2); + + const { nftId: nftId7, amount: amount7 } = await mint1155(owner, 2); + + const { nftId: nftId8, amount: amount8 } = await mint1155(owner, 2); + + const { nftId: nftId9, amount: amount9 } = await mint1155(owner, 2); + + const { nftId: nftId10, amount: amount10 } = await mint1155(owner, 2); + + await set1155ApprovalForAll(owner, tempConduit.address, true); + + await tempConduit.connect(owner).executeWithBatch1155( + [], + [ + { + token: testERC1155.address, + from: owner.address, + to: buyer.address, + ids: [ + nftId, + secondNftId, + thirdNftId, + nftId4, + nftId5, + nftId6, + nftId7, + nftId8, + nftId9, + nftId10, + ], + amounts: [ + amount, + secondAmount, + thirdAmount, + amount4, + amount5, + amount6, + amount7, + amount8, + amount9, + amount10, + ], + }, + ] + ); + }); + + it("Performs complex batch transfer through a conduit", async () => { + const tempConduitKey = owner.address + "f100000000000000000000f1"; + + const { conduit: tempConduitAddress } = await conduitController.getConduit( + tempConduitKey + ); + + await conduitController + .connect(owner) + .createConduit(tempConduitKey, owner.address); + + const tempConduit = conduitImplementation.attach(tempConduitAddress); + + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, owner.address, true); + + const { nftId, amount } = await mint1155(owner, 2); + + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + owner, + 2 + ); + + const { nftId: thirdNftId, amount: thirdAmount } = await mint1155(owner, 2); + + const { nftId: nftId4, amount: amount4 } = await mint1155(owner, 2); + + const { nftId: nftId5, amount: amount5 } = await mint1155( + owner, + 2, + testERC1155Two + ); + + const { nftId: nftId6, amount: amount6 } = await mint1155( + owner, + 2, + testERC1155Two + ); + + const { nftId: nftId7, amount: amount7 } = await mint1155( + owner, + 2, + testERC1155Two + ); + + const { nftId: nftId8, amount: amount8 } = await mint1155( + owner, + 2, + testERC1155Two + ); + + const amount9 = toBN(randomBN(4)).add(1); + await mintAndApproveERC20(owner, tempConduit.address, amount9.mul(2)); + + const nftId10 = await mint721(owner); + + await set1155ApprovalForAll(owner, tempConduit.address, true); + + await expect( + testERC1155Two.connect(owner).setApprovalForAll(tempConduit.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(owner.address, tempConduit.address, true); + + await set721ApprovalForAll(owner, tempConduit.address, true); + + const newAddress = toAddress(12345); + + await tempConduit.connect(owner).executeWithBatch1155( + [ + { + itemType: 1, + token: testERC20.address, + from: owner.address, + to: newAddress, + identifier: toBN(0), + amount: amount9, + }, + { + itemType: 2, + token: testERC721.address, + from: owner.address, + to: newAddress, + identifier: nftId10, + amount: toBN(1), + }, + ], + [ + { + token: testERC1155.address, + from: owner.address, + to: newAddress, + ids: [nftId, secondNftId, thirdNftId, nftId4], + amounts: [amount, secondAmount, thirdAmount, amount4], + }, + { + token: testERC1155Two.address, + from: owner.address, + to: newAddress, + ids: [nftId5, nftId6, nftId7, nftId8], + amounts: [amount5, amount6, amount7, amount8], + }, + ] + ); + + expect(await testERC1155.balanceOf(newAddress, nftId)).to.equal(amount); + expect(await testERC1155.balanceOf(newAddress, secondNftId)).to.equal( + secondAmount + ); + expect(await testERC1155.balanceOf(newAddress, thirdNftId)).to.equal( + thirdAmount + ); + expect(await testERC1155.balanceOf(newAddress, nftId4)).to.equal(amount4); + + expect(await testERC1155Two.balanceOf(newAddress, nftId5)).to.equal( + amount5 + ); + expect(await testERC1155Two.balanceOf(newAddress, nftId6)).to.equal( + amount6 + ); + expect(await testERC1155Two.balanceOf(newAddress, nftId7)).to.equal( + amount7 + ); + expect(await testERC1155Two.balanceOf(newAddress, nftId8)).to.equal( + amount8 + ); + + expect(await testERC20.balanceOf(newAddress)).to.equal(amount9); + expect(await testERC721.ownerOf(nftId10)).to.equal(newAddress); + }); + + it("ERC1155 <=> ETH (match, two different groups of 1155's)", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, secondNftId, secondAmount); + + // Seller mints third nft + const { nftId: thirdNftId, amount: thirdAmount } = await mint1155(seller); + + // Seller mints fourth nft + const fourthNftId = toBN(randomBN(4)); + const fourthAmount = toBN(randomBN(4)); + await testERC1155Two.mint(seller.address, fourthNftId, fourthAmount); + + // Seller approves marketplace contract to transfer NFTs + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + await expect( + testERC1155Two + .connect(seller) + .setApprovalForAll(marketplaceContract.address, true) + ) + .to.emit(testERC1155Two, "ApprovalForAll") + .withArgs(seller.address, marketplaceContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount, amount), + getTestItem1155( + secondNftId, + secondAmount, + secondAmount, + testERC1155Two.address + ), + getTestItem1155(thirdNftId, thirdAmount, thirdAmount), + getTestItem1155( + fourthNftId, + fourthAmount, + fourthAmount, + testERC1155Two.address + ), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[0, 2]], [[1, 2]]], + [[[0, 3]], [[1, 3]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(7); + + await marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + }); + + it("Reverts when attempting to update a conduit channel when call is not from controller", async () => { + await expect( + conduitOne + .connect(owner) + .updateChannel(ethers.constants.AddressZero, true) + ).to.be.revertedWithCustomError(conduitOne, "InvalidController"); + }); + + it("Reverts when attempting to execute transfers on a conduit when not called from a channel", async () => { + const expectedRevertReason = + getCustomRevertSelector("ChannelClosed(address)") + + owner.address.slice(2).padStart(64, "0").toLowerCase(); + + const tx = await conduitOne.connect(owner).populateTransaction.execute([]); + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect(conduitOne.connect(owner).execute([])).to.be.reverted; + }); + + it("Reverts when attempting to execute with 1155 transfers on a conduit when not called from a channel", async () => { + await expect( + conduitOne.connect(owner).executeWithBatch1155([], []) + ).to.be.revertedWithCustomError(conduitOne, "ChannelClosed"); + }); + + it("Reverts when attempting to execute batch 1155 transfers on a conduit when not called from a channel", async () => { + await expect( + conduitOne.connect(owner).executeBatch1155([]) + ).to.be.revertedWithCustomError(conduitOne, "ChannelClosed"); + }); + + it("Retrieves the owner of a conduit", async () => { + const ownerOf = await conduitController.ownerOf(conduitOne.address); + expect(ownerOf).to.equal(owner.address); + + await expect( + conduitController.connect(owner).ownerOf(buyer.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + }); + + it("Retrieves the key of a conduit", async () => { + const key = await conduitController.getKey(conduitOne.address); + expect(key.toLowerCase()).to.equal(conduitKeyOne.toLowerCase()); + + await expect( + conduitController.connect(owner).getKey(buyer.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + }); + + it("Retrieves the status of a conduit channel", async () => { + let isOpen = await conduitController.getChannelStatus( + conduitOne.address, + marketplaceContract.address + ); + expect(isOpen).to.be.true; + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + seller.address + ); + expect(isOpen).to.be.false; + + await expect( + conduitController + .connect(owner) + .getChannelStatus(buyer.address, seller.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + }); + + it("Retrieves conduit channels from the controller", async () => { + const totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(1); + + await expect( + conduitController.connect(owner).getTotalChannels(buyer.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + + const firstChannel = await conduitController.getChannel( + conduitOne.address, + 0 + ); + expect(firstChannel).to.equal(marketplaceContract.address); + + await expect( + conduitController + .connect(owner) + .getChannel(buyer.address, +totalChannels - 1) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + + await expect( + conduitController.connect(owner).getChannel(conduitOne.address, 1) + ).to.be.revertedWithCustomError(conduitController, "ChannelOutOfRange"); + + await expect( + conduitController.connect(owner).getChannel(conduitOne.address, 2) + ).to.be.revertedWithCustomError(conduitController, "ChannelOutOfRange"); + + const channels = await conduitController.getChannels(conduitOne.address); + expect(channels.length).to.equal(1); + expect(channels[0]).to.equal(marketplaceContract.address); + + await expect( + conduitController.connect(owner).getChannels(buyer.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + }); + + it("Adds and removes channels", async () => { + // Get number of open channels + let totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(1); + + let isOpen = await conduitController.getChannelStatus( + conduitOne.address, + marketplaceContract.address + ); + expect(isOpen).to.be.true; + + // No-op + await expect( + conduitController + .connect(owner) + .updateChannel(conduitOne.address, marketplaceContract.address, true) + ).to.be.reverted; // ChannelStatusAlreadySet + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + marketplaceContract.address + ); + expect(isOpen).to.be.true; + + // Get number of open channels + totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(1); + + await conduitController + .connect(owner) + .updateChannel(conduitOne.address, seller.address, true); + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + seller.address + ); + expect(isOpen).to.be.true; + + // Get number of open channels + totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(2); + + await conduitController + .connect(owner) + .updateChannel(conduitOne.address, marketplaceContract.address, false); + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + marketplaceContract.address + ); + expect(isOpen).to.be.false; + + // Test a specific branch in ConduitController.updateChannel + // when !isOpen && !channelPreviouslyOpen + await faucet(conduitController.address, provider); + + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [conduitController.address], + }); + + const conduitControllerSigner = await ethers.getSigner( + conduitController.address + ); + + await conduitOne + .connect(conduitControllerSigner) + .updateChannel(marketplaceContract.address, true); + + await network.provider.request({ + method: "hardhat_stopImpersonatingAccount", + params: [conduitController.address], + }); + + await conduitController + .connect(owner) + .updateChannel(conduitOne.address, marketplaceContract.address, false); + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + marketplaceContract.address + ); + expect(isOpen).to.be.false; + + // Get number of open channels + totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(1); + + await conduitController + .connect(owner) + .updateChannel(conduitOne.address, seller.address, false); + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + seller.address + ); + expect(isOpen).to.be.false; + + // Get number of open channels + totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(0); + + await conduitController + .connect(owner) + .updateChannel(conduitOne.address, marketplaceContract.address, true); + + isOpen = await conduitController.getChannelStatus( + conduitOne.address, + marketplaceContract.address + ); + expect(isOpen).to.be.true; + + // Get number of open channels + totalChannels = await conduitController.getTotalChannels( + conduitOne.address + ); + expect(totalChannels).to.equal(1); + }); + + it("Reverts on an attempt to move an unsupported item", async () => { + await conduitController + .connect(owner) + .updateChannel(conduitOne.address, seller.address, true); + + const isOpen = await conduitController.getChannelStatus( + conduitOne.address, + seller.address + ); + expect(isOpen).to.be.true; + + await expect( + conduitOne.connect(seller).executeWithBatch1155( + [ + { + itemType: 0, // NATIVE (invalid) + token: ethers.constants.AddressZero, + from: conduitOne.address, + to: seller.address, + identifier: 0, + amount: 0, + }, + ], + [] + ) + ).to.be.revertedWithCustomError(conduitOne, "InvalidItemType"); + }); + + it("Reverts when attempting to create a conduit not scoped to the creator", async () => { + await expect( + conduitController + .connect(owner) + .createConduit(ethers.constants.HashZero, owner.address) + ).to.be.revertedWithCustomError(conduitController, "InvalidCreator"); + }); + + it("Reverts when attempting to create a conduit that already exists", async () => { + await expect( + conduitController + .connect(owner) + .createConduit(conduitKeyOne, owner.address) + ) + .to.be.revertedWithCustomError(conduitController, "ConduitAlreadyExists") + .withArgs(conduitOne.address); + }); + + it("Reverts when attempting to update a channel for an unowned conduit", async () => { + await expect( + conduitController + .connect(buyer) + .updateChannel(conduitOne.address, buyer.address, true) + ) + .to.be.revertedWithCustomError(conduitController, "CallerIsNotOwner") + .withArgs(conduitOne.address); + }); + + it("Retrieves no initial potential owner for new conduit", async () => { + const potentialOwner = await conduitController.getPotentialOwner( + conduitOne.address + ); + expect(potentialOwner).to.equal(ethers.constants.AddressZero); + + await expect( + conduitController.connect(owner).getPotentialOwner(buyer.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + }); + + it("Lets the owner transfer ownership via a two-stage process", async () => { + await expect( + conduitController + .connect(buyer) + .transferOwnership(conduitOne.address, buyer.address) + ).to.be.revertedWithCustomError(conduitController, "CallerIsNotOwner"); + + await expect( + conduitController + .connect(owner) + .transferOwnership(conduitOne.address, ethers.constants.AddressZero) + ).to.be.revertedWithCustomError( + conduitController, + "NewPotentialOwnerIsZeroAddress" + ); + + await expect( + conduitController + .connect(owner) + .transferOwnership(seller.address, buyer.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + + let potentialOwner = await conduitController.getPotentialOwner( + conduitOne.address + ); + expect(potentialOwner).to.equal(ethers.constants.AddressZero); + + await conduitController.transferOwnership( + conduitOne.address, + buyer.address + ); + + potentialOwner = await conduitController.getPotentialOwner( + conduitOne.address + ); + expect(potentialOwner).to.equal(buyer.address); + + await expect( + conduitController + .connect(owner) + .transferOwnership(conduitOne.address, buyer.address) + ).to.be.revertedWithCustomError( + conduitController, + "NewPotentialOwnerAlreadySet" + ); + + await expect( + conduitController + .connect(buyer) + .cancelOwnershipTransfer(conduitOne.address) + ).to.be.revertedWithCustomError(conduitController, "CallerIsNotOwner"); + + await expect( + conduitController.connect(owner).cancelOwnershipTransfer(seller.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + + await conduitController.cancelOwnershipTransfer(conduitOne.address); + + potentialOwner = await conduitController.getPotentialOwner( + conduitOne.address + ); + expect(potentialOwner).to.equal(ethers.constants.AddressZero); + + await expect( + conduitController + .connect(owner) + .cancelOwnershipTransfer(conduitOne.address) + ).to.be.revertedWithCustomError( + conduitController, + "NoPotentialOwnerCurrentlySet" + ); + + await conduitController.transferOwnership( + conduitOne.address, + buyer.address + ); + + potentialOwner = await conduitController.getPotentialOwner( + conduitOne.address + ); + expect(potentialOwner).to.equal(buyer.address); + + await expect( + conduitController.connect(buyer).acceptOwnership(seller.address) + ).to.be.revertedWithCustomError(conduitController, "NoConduit"); + + await expect( + conduitController.connect(seller).acceptOwnership(conduitOne.address) + ).to.be.revertedWithCustomError( + conduitController, + "CallerIsNotNewPotentialOwner" + ); + + await conduitController.connect(buyer).acceptOwnership(conduitOne.address); + + potentialOwner = await conduitController.getPotentialOwner( + conduitOne.address + ); + expect(potentialOwner).to.equal(ethers.constants.AddressZero); + + const ownerOf = await conduitController.ownerOf(conduitOne.address); + expect(ownerOf).to.equal(buyer.address); + }); +}); diff --git a/test/counter.spec.ts b/test/counter.spec.ts new file mode 100644 index 000000000..dd9c246bc --- /dev/null +++ b/test/counter.spec.ts @@ -0,0 +1,1552 @@ +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers, network } from "hardhat"; + +import { deployContract } from "./utils/contracts"; +import { + buildOrderStatus, + getItemETH, + randomBN, + randomHex, + toBN, + toKey, +} from "./utils/encoding"; +import { faucet } from "./utils/faucet"; +import { seaportFixture } from "./utils/fixtures"; +import { VERSION, getCustomRevertSelector } from "./utils/helpers"; + +import type { ConsiderationInterface } from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { Wallet } from "ethers"; + +const { parseEther } = ethers.utils; + +describe(`Validate, cancel, and increment counter flows (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let marketplaceContract: ConsiderationInterface; + + let checkExpectedEvents: SeaportFixtures["checkExpectedEvents"]; + let createOrder: SeaportFixtures["createOrder"]; + let getTestItem721: SeaportFixtures["getTestItem721"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; + let withBalanceChecks: SeaportFixtures["withBalanceChecks"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + checkExpectedEvents, + createOrder, + getTestItem721, + marketplaceContract, + mintAndApprove721, + set721ApprovalForAll, + withBalanceChecks, + } = await seaportFixture(owner)); + }); + + let seller: Wallet; + let buyer: Wallet; + let zone: Wallet; + + async function setupFixture() { + // Setup basic buyer/seller wallets with ETH + const seller = new ethers.Wallet(randomHex(32), provider); + const buyer = new ethers.Wallet(randomHex(32), provider); + const zone = new ethers.Wallet(randomHex(32), provider); + + for (const wallet of [seller, buyer, zone]) { + await faucet(wallet.address, provider); + } + + return { seller, buyer, zone }; + } + + beforeEach(async () => { + ({ seller, buyer, zone } = await loadFixture(setupFixture)); + }); + + describe("Validate", async () => { + it("Validate signed order and fill it with no signature", async () => { + // Seller mints an nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const signature = order.signature; + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.eq( + buildOrderStatus(false, false, 0, 0) + ); + + // cannot fill it with no signature yet + order.signature = "0x"; + + if (!process.env.REFERENCE) { + const expectedRevertReason = + getCustomRevertSelector("InvalidSignature()"); + + let tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + let returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + + // cannot validate it with no signature from a random account + await expect(marketplaceContract.connect(owner).validate([order])).to.be + .reverted; + + tx = await marketplaceContract + .connect(owner) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + } else { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + + // cannot validate it with no signature from a random account + await expect(marketplaceContract.connect(owner).validate([order])).to.be + .reverted; + } + + // can validate it once you add the signature back + order.signature = signature; + + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.eq(buildOrderStatus(true, false, 0, 0)); + + // Can validate it repeatedly, but no event after the first time + await marketplaceContract.connect(owner).validate([order, order]); + + // Fulfill the order without a signature + order.signature = "0x"; + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + + const finalStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...finalStatus }).to.deep.eq( + buildOrderStatus(true, false, 1, 1) + ); + + // cannot validate it once it's been fully filled + await expect( + marketplaceContract.connect(owner).validate([order]) + ).to.be.revertedWithCustomError( + marketplaceContract, + "OrderAlreadyFilled" + ); + }); + it("Validate unsigned order from offerer and fill it with no signature", async () => { + // Seller mints an nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + order.signature = "0x"; + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.eq( + buildOrderStatus(false, false, 0, 0) + ); + + if (!process.env.REFERENCE) { + // cannot fill it with no signature yet + const expectedRevertReason = + getCustomRevertSelector("InvalidSignature()"); + + let tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + let returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + + // cannot validate it with no signature from a random account + tx = await marketplaceContract + .connect(owner) + .populateTransaction.validate([order]); + returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect(marketplaceContract.connect(owner).validate([order])).to.be + .reverted; + } else { + // cannot fill it with no signature yet + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + + // cannot validate it with no signature from a random account + await expect(marketplaceContract.connect(owner).validate([order])).to.be + .reverted; + } + + // can validate it from the seller + const tx = await marketplaceContract.connect(seller).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.eq(buildOrderStatus(true, false, 0, 0)); + + // Fulfill the order without a signature + order.signature = "0x"; + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + + const finalStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...finalStatus }).to.deep.eq( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Cannot validate a cancelled order", async () => { + // Seller mints an nft + const nftId = randomBN(); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const signature = order.signature; + + order.signature = "0x"; + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.eq( + buildOrderStatus(false, false, 0, 0) + ); + + if (!process.env.REFERENCE) { + // cannot fill it with no signature yet + const expectedRevertReason = + getCustomRevertSelector("InvalidSignature()"); + + let tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + let returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + + tx = await marketplaceContract + .connect(owner) + .populateTransaction.validate([order]); + returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + // cannot validate it with no signature from a random account + await expect(marketplaceContract.connect(owner).validate([order])).to.be + .reverted; + } else { + // cannot fill it with no signature yet + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + + // cannot validate it with no signature from a random account + await expect(marketplaceContract.connect(owner).validate([order])).to.be + .reverted; + } + + // can cancel it + await expect( + marketplaceContract.connect(seller).cancel([orderComponents]) + ) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHash, seller.address, zone.address); + + // cannot validate it from the seller + await expect(marketplaceContract.connect(seller).validate([order])) + .to.be.revertedWithCustomError(marketplaceContract, "OrderIsCancelled") + .withArgs(orderHash); + + // cannot validate it with a signature either + order.signature = signature; + await expect(marketplaceContract.connect(owner).validate([order])) + .to.be.revertedWithCustomError(marketplaceContract, "OrderIsCancelled") + .withArgs(orderHash); + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.eq(buildOrderStatus(false, true, 0, 0)); + }); + + it("Skip validation for contract order", async () => { + // Seller mints an nft (FULL_OPEN order) + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.eq( + buildOrderStatus(false, false, 0, 0) + ); + + // Seller mints nft (CONTRACT order) + const contractOrderNftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // seller deploys offererContract and approves it for 721 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set721ApprovalForAll(seller, offererContract.address, true); + + const contractOrderOffer = [getTestItem721(contractOrderNftId) as any]; + + const contractOrderConsideration = [ + getItemETH( + parseEther("10"), + parseEther("10"), + offererContract.address + ) as any, + ]; + + contractOrderOffer[0].identifier = + contractOrderOffer[0].identifierOrCriteria; + contractOrderOffer[0].amount = contractOrderOffer[0].endAmount; + + contractOrderConsideration[0].identifier = + contractOrderConsideration[0].identifierOrCriteria; + contractOrderConsideration[0].amount = + contractOrderConsideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(contractOrderOffer[0], contractOrderOffer[0]); + + const { order: contractOrder } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const contractOrderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus( + contractOrderHash + ); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // can validate it from the seller + const tx = await marketplaceContract + .connect(seller) + .validate([order, contractOrder]); + + const receipt = await tx.wait(); + + // should only validate the FULL_OPEN order + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + }); + + it("Reverts on validate when consideration array length doesn't match totalOriginalConsiderationItems", async () => { + // Seller mints an nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + const offer = [getTestItem721(nftId)]; + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + order.parameters.totalOriginalConsiderationItems = 2; + + // cannot validate when consideration array length is different than total + // original consideration items value + await expect( + marketplaceContract.connect(seller).validate([order]) + ).to.be.revertedWithCustomError( + marketplaceContract, + "ConsiderationLengthNotEqualToTotalOriginal" + ); + }); + }); + + describe("Cancel", async () => { + it("Can cancel an order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // cannot cancel it from a random account + await expect( + marketplaceContract.connect(owner).cancel([orderComponents]) + ).to.be.revertedWithCustomError(marketplaceContract, "CannotCancelOrder"); + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.eq( + buildOrderStatus(false, false, 0, 0) + ); + + // can cancel it + await expect( + marketplaceContract.connect(seller).cancel([orderComponents]) + ) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHash, seller.address, zone.address); + + // cannot fill the order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "OrderIsCancelled") + .withArgs(orderHash); + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.eq(buildOrderStatus(false, true, 0, 0)); + }); + it("Can cancel a validated order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // cannot cancel it from a random account + await expect( + marketplaceContract.connect(owner).cancel([orderComponents]) + ).to.be.revertedWithCustomError(marketplaceContract, "CannotCancelOrder"); + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // Can validate it + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.equal( + buildOrderStatus(true, false, 0, 0) + ); + + // can cancel it + await expect( + marketplaceContract.connect(seller).cancel([orderComponents]) + ) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHash, seller.address, zone.address); + + // cannot fill the order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "OrderIsCancelled") + .withArgs(orderHash); + + const finalStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...finalStatus }).to.deep.equal( + buildOrderStatus(false, true, 0, 0) + ); + }); + it("Can cancel an order from the zone", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // cannot cancel it from a random account + await expect( + marketplaceContract.connect(owner).cancel([orderComponents]) + ).to.be.revertedWithCustomError(marketplaceContract, "CannotCancelOrder"); + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // can cancel it from the zone + await expect(marketplaceContract.connect(zone).cancel([orderComponents])) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHash, seller.address, zone.address); + + // cannot fill the order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "OrderIsCancelled") + .withArgs(orderHash); + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.equal( + buildOrderStatus(false, true, 0, 0) + ); + }); + it("Can cancel a validated order from a zone", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const initialStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...initialStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // Can validate it + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + // cannot cancel it from a random account + await expect( + marketplaceContract.connect(owner).cancel([orderComponents]) + ).to.be.revertedWithCustomError(marketplaceContract, "CannotCancelOrder"); + + const newStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...newStatus }).to.deep.equal( + buildOrderStatus(true, false, 0, 0) + ); + + // can cancel it from the zone + await expect(marketplaceContract.connect(zone).cancel([orderComponents])) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHash, seller.address, zone.address); + + // cannot fill the order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "OrderIsCancelled") + .withArgs(orderHash); + + const finalStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...finalStatus }).to.deep.equal( + buildOrderStatus(false, true, 0, 0) + ); + }); + it("Reverts when trying to cancel contract order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // Seller mints nft (CONTRACT order) + const contractOrderNftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // seller deploys offererContract and approves it for 721 token + const offererContract = await deployContract( + "TestContractOfferer", + owner, + marketplaceContract.address + ); + + await set721ApprovalForAll(seller, offererContract.address, true); + + const contractOrderOffer = [getTestItem721(contractOrderNftId) as any]; + + const contractOrderConsideration = [ + getItemETH( + parseEther("10"), + parseEther("10"), + offererContract.address + ) as any, + ]; + + contractOrderOffer[0].identifier = + contractOrderOffer[0].identifierOrCriteria; + contractOrderOffer[0].amount = contractOrderOffer[0].endAmount; + + contractOrderConsideration[0].identifier = + contractOrderConsideration[0].identifierOrCriteria; + contractOrderConsideration[0].amount = + contractOrderConsideration[0].endAmount; + + await offererContract + .connect(seller) + .activate(contractOrderOffer[0], contractOrderOffer[0]); + + const { orderComponents: contractOrderComponents } = await createOrder( + seller, + zone, + contractOrderOffer, + contractOrderConsideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const contractOrderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus( + contractOrderHash + ); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract.connect(seller).cancel([contractOrderComponents]) + ).to.be.revertedWithCustomError(marketplaceContract, "CannotCancelOrder"); + + const contractOrderStatus = await marketplaceContract.getOrderStatus( + contractOrderHash + ); + expect({ ...contractOrderStatus }).to.deep.eq( + buildOrderStatus(false, false, 0, 0) + ); + }); + }); + + describe("Increment Counter", async () => { + it("Can increment the counter", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + let { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const counter = await marketplaceContract.getCounter(seller.address); + expect(counter).to.equal(0); + expect(orderComponents.counter).to.equal(counter); + + const latestBlockHash = (await provider.getBlock("latest")).hash; + const quasiRandomNumber = toBN(latestBlockHash).shr(128); + + // can increment the counter + await expect(marketplaceContract.connect(seller).incrementCounter()) + .to.emit(marketplaceContract, "CounterIncremented") + .withArgs(quasiRandomNumber, seller.address); + + const newCounter = await marketplaceContract.getCounter(seller.address); + expect(newCounter).to.equal(quasiRandomNumber); + + if (!process.env.REFERENCE) { + // Cannot fill order anymore + const expectedRevertReason = getCustomRevertSelector("InvalidSigner()"); + + const tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } else { + // Cannot fill order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } + + const newOrderDetails = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + order = newOrderDetails.order; + orderHash = newOrderDetails.orderHash; + value = newOrderDetails.value; + orderComponents = newOrderDetails.orderComponents; + + expect(orderComponents.counter).to.equal(newCounter); + + // Can fill order with new counter + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("Can increment the counter and implicitly cancel a validated order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + let { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const counter = await marketplaceContract.getCounter(seller.address); + expect(counter).to.equal(0); + expect(orderComponents.counter).to.equal(counter); + + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const latestBlockHash = (await provider.getBlock("latest")).hash; + const quasiRandomNumber = toBN(latestBlockHash).shr(128); + + // can increment the counter + await expect(marketplaceContract.connect(seller).incrementCounter()) + .to.emit(marketplaceContract, "CounterIncremented") + .withArgs(quasiRandomNumber, seller.address); + + const newCounter = await marketplaceContract.getCounter(seller.address); + expect(newCounter).to.equal(quasiRandomNumber); + + if (!process.env.REFERENCE) { + // Cannot fill order anymore + const expectedRevertReason = getCustomRevertSelector("InvalidSigner()"); + + const tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } else { + // Cannot fill order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } + + const newOrderDetails = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + order = newOrderDetails.order; + orderHash = newOrderDetails.orderHash; + value = newOrderDetails.value; + orderComponents = newOrderDetails.orderComponents; + + expect(orderComponents.counter).to.equal(newCounter); + + // Can fill order with new counter + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + it("Can increment the counter as the offerer and implicitly cancel a validated order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + let { order, orderHash, value, orderComponents } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const counter = await marketplaceContract.getCounter(seller.address); + expect(counter).to.equal(0); + expect(orderComponents.counter).to.equal(counter); + + const tx = await marketplaceContract.connect(owner).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } + + const latestBlockHash = (await provider.getBlock("latest")).hash; + const quasiRandomNumber = toBN(latestBlockHash).shr(128); + + // can increment the counter as the offerer + await expect(marketplaceContract.connect(seller).incrementCounter()) + .to.emit(marketplaceContract, "CounterIncremented") + .withArgs(quasiRandomNumber, seller.address); + + const newCounter = await marketplaceContract.getCounter(seller.address); + expect(newCounter).to.equal(quasiRandomNumber); + + if (!process.env.REFERENCE) { + // Cannot fill order anymore + const expectedRevertReason = getCustomRevertSelector("InvalidSigner()"); + + const tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillOrder(order, toKey(0), { + value, + }); + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } else { + // Cannot fill order anymore + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } + + const newOrderDetails = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + order = newOrderDetails.order; + orderHash = newOrderDetails.orderHash; + value = newOrderDetails.value; + orderComponents = newOrderDetails.orderComponents; + + expect(orderComponents.counter).to.equal(newCounter); + + // Can fill order with new counter + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + + return receipt; + }); + }); + }); +}); diff --git a/test/findings/AdditionalRecipientsOffByOne.spec.ts b/test/findings/AdditionalRecipientsOffByOne.spec.ts index 3a26f1110..3e89ee365 100644 --- a/test/findings/AdditionalRecipientsOffByOne.spec.ts +++ b/test/findings/AdditionalRecipientsOffByOne.spec.ts @@ -1,17 +1,21 @@ import { expect } from "chai"; -import { constants, Wallet } from "ethers"; -import { +import { constants } from "ethers"; +import { hexZeroPad } from "ethers/lib/utils"; +import { network } from "hardhat"; +import { getScuffedContract } from "scuffed-abi"; + +import { buildOrderStatus, getBasicOrderParameters } from "../utils/encoding"; +import { getWalletWithEther } from "../utils/faucet"; +import { seaportFixture } from "../utils/fixtures"; + +import type { ConsiderationInterface, TestERC20, TestERC721, } from "../../typechain-types"; -import { buildOrderStatus, getBasicOrderParameters } from "../utils/encoding"; -import { seaportFixture, SeaportFixtures } from "../utils/fixtures"; -import { getWalletWithEther } from "../utils/impersonate"; -import { AdvancedOrder, ConsiderationItem } from "../utils/types"; -import { getScuffedContract } from "scuffed-abi"; -import { hexZeroPad } from "ethers/lib/utils"; -import { network } from "hardhat"; +import type { SeaportFixtures } from "../utils/fixtures"; +import type { AdvancedOrder, ConsiderationItem } from "../utils/types"; +import type { Wallet } from "ethers"; const IS_FIXED = true; @@ -19,16 +23,19 @@ describe("Additional recipients off by one error allows skipping second consider let alice: Wallet; let bob: Wallet; let carol: Wallet; + let order: AdvancedOrder; let orderHash: string; + + let marketplaceContract: ConsiderationInterface; let testERC20: TestERC20; let testERC721: TestERC721; - let marketplaceContract: ConsiderationInterface; - let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; - let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + + let createOrder: SeaportFixtures["createOrder"]; let getTestItem20: SeaportFixtures["getTestItem20"]; let getTestItem721: SeaportFixtures["getTestItem721"]; - let createOrder: SeaportFixtures["createOrder"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; after(async () => { await network.provider.request({ @@ -40,18 +47,21 @@ describe("Additional recipients off by one error allows skipping second consider alice = await getWalletWithEther(); bob = await getWalletWithEther(); carol = await getWalletWithEther(); + ({ - mintAndApprove721, - mintAndApproveERC20, - marketplaceContract, + createOrder, getTestItem20, getTestItem721, - createOrder, + marketplaceContract, + mintAndApprove721, + mintAndApproveERC20, testERC20, testERC721, } = await seaportFixture(await getWalletWithEther())); + // ERC721 with ID = 1 await mintAndApprove721(alice, marketplaceContract.address, 1); + // ERC20 with amount = 1100 await mintAndApproveERC20(bob, marketplaceContract.address, 1100); }); @@ -87,9 +97,72 @@ describe("Additional recipients off by one error allows skipping second consider ); // Bob validates the order - await expect(marketplaceContract.connect(bob).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, alice.address, constants.AddressZero); + const tx = await marketplaceContract.connect(bob).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } // OrderStatus is validated orderStatus = await marketplaceContract.getOrderStatus(orderHash); @@ -150,8 +223,12 @@ describe("Additional recipients off by one error allows skipping second consider bob.sendTransaction({ to: marketplaceContract.address, data: maliciousCallData, + gasLimit: 29_999_999, }) - ).to.be.revertedWith("MissingOriginalConsiderationItems"); + ).to.be.revertedWithCustomError( + marketplaceContract, + "MissingOriginalConsiderationItems" + ); }); } }); diff --git a/test/findings/CriteriaResolverUnhashedLeaves.spec.ts b/test/findings/CriteriaResolverUnhashedLeaves.spec.ts index cf0ecc633..80d8bf477 100644 --- a/test/findings/CriteriaResolverUnhashedLeaves.spec.ts +++ b/test/findings/CriteriaResolverUnhashedLeaves.spec.ts @@ -1,16 +1,20 @@ import { expect } from "chai"; -import { BigNumber, constants, Wallet } from "ethers"; +import { constants } from "ethers"; import { network } from "hardhat"; -import { + +import { merkleTree } from "../utils/criteria"; +import { buildResolver, toBN, toKey } from "../utils/encoding"; +import { getWalletWithEther } from "../utils/faucet"; +import { seaportFixture } from "../utils/fixtures"; + +import type { ConsiderationInterface, TestERC20, TestERC721, } from "../../typechain-types"; -import { buildResolver, toBN, toKey } from "../utils/encoding"; -import { seaportFixture, SeaportFixtures } from "../utils/fixtures"; -import { getWalletWithEther } from "../utils/impersonate"; -import { AdvancedOrder } from "../utils/types"; -const { merkleTree } = require("../utils/criteria"); +import type { SeaportFixtures } from "../utils/fixtures"; +import type { AdvancedOrder } from "../utils/types"; +import type { BigNumber, Wallet } from "ethers"; const IS_FIXED = true; @@ -18,16 +22,20 @@ describe("Criteria resolver allows root hash to be given as a leaf", async () => let alice: Wallet; let bob: Wallet; let carol: Wallet; + let order: AdvancedOrder; + + let marketplaceContract: ConsiderationInterface; let testERC20: TestERC20; let testERC721: TestERC721; - let marketplaceContract: ConsiderationInterface; - let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; - let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + + let createOrder: SeaportFixtures["createOrder"]; let getTestItem20: SeaportFixtures["getTestItem20"]; let getTestItem721WithCriteria: SeaportFixtures["getTestItem721WithCriteria"]; - let createOrder: SeaportFixtures["createOrder"]; let mint721s: SeaportFixtures["mint721s"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; + let tokenIds: BigNumber[]; let root: string; @@ -44,20 +52,23 @@ describe("Criteria resolver allows root hash to be given as a leaf", async () => alice = await getWalletWithEther(); bob = await getWalletWithEther(); carol = await getWalletWithEther(); + ({ - mintAndApproveERC20, - marketplaceContract, + createOrder, getTestItem20, getTestItem721WithCriteria, + marketplaceContract, + mint721s, + mintAndApproveERC20, set721ApprovalForAll, - createOrder, testERC20, testERC721, - mint721s, } = await seaportFixture(await getWalletWithEther())); + await mintAndApproveERC20(alice, marketplaceContract.address, 1000); await set721ApprovalForAll(bob, marketplaceContract.address); await set721ApprovalForAll(carol, marketplaceContract.address); + tokenIds = await mint721s(bob, 10); ({ root } = merkleTree(tokenIds)); }); @@ -98,7 +109,7 @@ describe("Criteria resolver allows root hash to be given as a leaf", async () => .fulfillAdvancedOrder( order, [criteriaResolver], - toKey(false), + toKey(0), carol.address ); }); @@ -120,10 +131,10 @@ describe("Criteria resolver allows root hash to be given as a leaf", async () => .fulfillAdvancedOrder( order, [criteriaResolver], - toKey(false), + toKey(0), carol.address ) - ).to.be.revertedWith("InvalidProof"); + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidProof"); }); } }); diff --git a/test/findings/FulfillmentOverflowWithMissingItems.spec.ts b/test/findings/FulfillmentOverflowWithMissingItems.spec.ts index d009539a6..6b2193f79 100644 --- a/test/findings/FulfillmentOverflowWithMissingItems.spec.ts +++ b/test/findings/FulfillmentOverflowWithMissingItems.spec.ts @@ -1,31 +1,39 @@ +import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic"; import { expect } from "chai"; -import { constants, Wallet } from "ethers"; +import { constants } from "ethers"; import { network } from "hardhat"; -import { + +import { toFulfillment } from "../utils/encoding"; +import { getWalletWithEther } from "../utils/faucet"; +import { seaportFixture } from "../utils/fixtures"; + +import type { ConsiderationInterface, TestERC20, TestERC721, } from "../../typechain-types"; -import { toFulfillment } from "../utils/encoding"; -import { seaportFixture, SeaportFixtures } from "../utils/fixtures"; -import { getWalletWithEther } from "../utils/impersonate"; -import { AdvancedOrder, OfferItem } from "../utils/types"; +import type { SeaportFixtures } from "../utils/fixtures"; +import type { AdvancedOrder, OfferItem } from "../utils/types"; +import type { Wallet } from "ethers"; const IS_FIXED = true; describe("Fulfillment applier allows overflow when a missing item is provided", async () => { let alice: Wallet; let bob: Wallet; + let order: AdvancedOrder; let maliciousOrder: AdvancedOrder; + + let marketplaceContract: ConsiderationInterface; let testERC20: TestERC20; let testERC721: TestERC721; - let marketplaceContract: ConsiderationInterface; - let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; - let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + + let createOrder: SeaportFixtures["createOrder"]; let getTestItem20: SeaportFixtures["getTestItem20"]; let getTestItem721: SeaportFixtures["getTestItem721"]; - let createOrder: SeaportFixtures["createOrder"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; after(async () => { await network.provider.request({ @@ -37,20 +45,24 @@ describe("Fulfillment applier allows overflow when a missing item is provided", if (process.env.REFERENCE) { this.skip(); } + alice = await getWalletWithEther(); bob = await getWalletWithEther(); + ({ - mintAndApprove721, - mintAndApproveERC20, - marketplaceContract, + createOrder, getTestItem20, getTestItem721, - createOrder, + marketplaceContract, + mintAndApprove721, + mintAndApproveERC20, testERC20, testERC721, } = await seaportFixture(await getWalletWithEther())); + // ERC721 with ID = 1 await mintAndApprove721(alice, marketplaceContract.address, 1); + // ERC20 with amount = 1100 await mintAndApproveERC20(bob, marketplaceContract.address, 1); }); @@ -113,7 +125,12 @@ describe("Fulfillment applier allows overflow when a missing item is provided", it("Bob is able to match Alice's order with his malicious one", async () => { await marketplaceContract .connect(bob) - .matchAdvancedOrders([order, maliciousOrder], [], fulfillments); + .matchAdvancedOrders( + [order, maliciousOrder], + [], + fulfillments, + constants.AddressZero + ); }); it("Bob receives Alice's NFT, having paid 1 DAI", async () => { @@ -129,10 +146,13 @@ describe("Fulfillment applier allows overflow when a missing item is provided", await expect( marketplaceContract .connect(bob) - .matchAdvancedOrders([order, maliciousOrder], [], fulfillments) - ).to.be.revertedWith( - "panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)" - ); + .matchAdvancedOrders( + [order, maliciousOrder], + [], + fulfillments, + constants.AddressZero + ) + ).to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW); }); } }); diff --git a/test/findings/PartialFillFractionOverflow.spec.ts b/test/findings/PartialFillFractionOverflow.spec.ts index 5da4d3785..a47bfc698 100644 --- a/test/findings/PartialFillFractionOverflow.spec.ts +++ b/test/findings/PartialFillFractionOverflow.spec.ts @@ -1,15 +1,19 @@ import { expect } from "chai"; -import { constants, Wallet } from "ethers"; +import { constants } from "ethers"; import { network } from "hardhat"; -import { + +import { buildOrderStatus, toBN, toKey } from "../utils/encoding"; +import { getWalletWithEther } from "../utils/faucet"; +import { seaportFixture } from "../utils/fixtures"; + +import type { ConsiderationInterface, TestERC1155, TestERC20, } from "../../typechain-types"; -import { buildOrderStatus, toBN, toKey } from "../utils/encoding"; -import { seaportFixture, SeaportFixtures } from "../utils/fixtures"; -import { getWalletWithEther } from "../utils/impersonate"; -import { AdvancedOrder, ConsiderationItem } from "../utils/types"; +import type { SeaportFixtures } from "../utils/fixtures"; +import type { AdvancedOrder, ConsiderationItem } from "../utils/types"; +import type { Wallet } from "ethers"; const IS_FIXED = true; @@ -17,16 +21,19 @@ describe("Partial fill fractions can overflow to reset an order", async () => { let alice: Wallet; let bob: Wallet; let carol: Wallet; + let order: AdvancedOrder; let orderHash: string; - let testERC20: TestERC20; - let testERC1155: TestERC1155; + let marketplaceContract: ConsiderationInterface; + let testERC1155: TestERC1155; + let testERC20: TestERC20; + + let createOrder: SeaportFixtures["createOrder"]; + let getTestItem1155: SeaportFixtures["getTestItem1155"]; + let getTestItem20: SeaportFixtures["getTestItem20"]; let mintAndApprove1155: SeaportFixtures["mintAndApprove1155"]; let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; - let getTestItem20: SeaportFixtures["getTestItem20"]; - let getTestItem1155: SeaportFixtures["getTestItem1155"]; - let createOrder: SeaportFixtures["createOrder"]; after(async () => { await network.provider.request({ @@ -38,19 +45,22 @@ describe("Partial fill fractions can overflow to reset an order", async () => { if (process.env.REFERENCE) { this.skip(); } + alice = await getWalletWithEther(); bob = await getWalletWithEther(); carol = await getWalletWithEther(); + ({ + createOrder, + getTestItem1155, + getTestItem20, + marketplaceContract, mintAndApprove1155, mintAndApproveERC20, - marketplaceContract, - getTestItem20, - getTestItem1155, - createOrder, - testERC20, testERC1155, + testERC20, } = await seaportFixture(await getWalletWithEther())); + await mintAndApprove1155(alice, marketplaceContract.address, 1, 1, 10); await mintAndApproveERC20(bob, marketplaceContract.address, 500); await mintAndApproveERC20(carol, marketplaceContract.address, 4500); @@ -90,9 +100,72 @@ describe("Partial fill fractions can overflow to reset an order", async () => { ); // Bob validates the order - await expect(marketplaceContract.connect(bob).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, alice.address, constants.AddressZero); + const tx = await marketplaceContract.connect(bob).validate([order]); + + const receipt = await tx.wait(); + + expect(receipt.events?.length).to.equal(1); + + const event = receipt.events && receipt.events[0]; + + expect(event?.event).to.equal("OrderValidated"); + + expect(event?.args?.orderHash).to.equal(orderHash); + + const parameters = event && event.args && event.args.orderParameters; + + expect(parameters.offerer).to.equal(order.parameters.offerer); + expect(parameters.zone).to.equal(order.parameters.zone); + expect(parameters.orderType).to.equal(order.parameters.orderType); + expect(parameters.startTime).to.equal(order.parameters.startTime); + expect(parameters.endTime).to.equal(order.parameters.endTime); + expect(parameters.zoneHash).to.equal(order.parameters.zoneHash); + expect(parameters.salt).to.equal(order.parameters.salt); + expect(parameters.conduitKey).to.equal(order.parameters.conduitKey); + expect(parameters.totalOriginalConsiderationItems).to.equal( + order.parameters.totalOriginalConsiderationItems + ); + expect(parameters.totalOriginalConsiderationItems).to.equal( + parameters.consideration.length + ); + + expect(parameters.offer.length).to.equal(order.parameters.offer.length); + expect(parameters.consideration.length).to.equal( + order.parameters.consideration.length + ); + + for (let i = 0; i < parameters.offer.length; i++) { + const eventOffer = parameters.offer[i]; + const suppliedOffer = order.parameters.offer[i]; + expect(eventOffer.itemType).to.equal(suppliedOffer.itemType); + expect(eventOffer.token).to.equal(suppliedOffer.token); + expect(eventOffer.identifierOrCriteria).to.equal( + suppliedOffer.identifierOrCriteria + ); + expect(eventOffer.startAmount).to.equal(suppliedOffer.startAmount); + expect(eventOffer.endAmount).to.equal(suppliedOffer.endAmount); + } + + for (let i = 0; i < parameters.consideration.length; i++) { + const eventConsideration = parameters.consideration[i]; + const suppliedConsideration = order.parameters.consideration[i]; + expect(eventConsideration.itemType).to.equal( + suppliedConsideration.itemType + ); + expect(eventConsideration.token).to.equal(suppliedConsideration.token); + expect(eventConsideration.identifierOrCriteria).to.equal( + suppliedConsideration.identifierOrCriteria + ); + expect(eventConsideration.startAmount).to.equal( + suppliedConsideration.startAmount + ); + expect(eventConsideration.endAmount).to.equal( + suppliedConsideration.endAmount + ); + expect(eventConsideration.recipient).to.equal( + suppliedConsideration.recipient + ); + } // OrderStatus is validated orderStatus = await marketplaceContract.getOrderStatus(orderHash); @@ -107,7 +180,7 @@ describe("Partial fill fractions can overflow to reset an order", async () => { order.denominator = 2; await marketplaceContract .connect(bob) - .fulfillAdvancedOrder(order, [], toKey(false), bob.address); + .fulfillAdvancedOrder(order, [], toKey(0), bob.address); expect(await testERC1155.balanceOf(bob.address, 1)).to.eq(1); }); @@ -122,7 +195,7 @@ describe("Partial fill fractions can overflow to reset an order", async () => { order.denominator = toBN(2).pow(119); await marketplaceContract .connect(carol) - .fulfillAdvancedOrder(order, [], toKey(false), carol.address); + .fulfillAdvancedOrder(order, [], toKey(0), carol.address); }); it("Carol receives one 1155 token from Alice", async () => { @@ -149,12 +222,12 @@ describe("Partial fill fractions can overflow to reset an order", async () => { order.denominator = toBN(2).pow(2); await marketplaceContract .connect(carol) - .fulfillAdvancedOrder(order, [], toKey(false), carol.address); + .fulfillAdvancedOrder(order, [], toKey(0), carol.address); order.numerator = toBN(2).pow(118); order.denominator = toBN(2).pow(119); await marketplaceContract .connect(carol) - .fulfillAdvancedOrder(order, [], toKey(false), carol.address); + .fulfillAdvancedOrder(order, [], toKey(0), carol.address); } }); diff --git a/test/foundry/BulkSignature.t.sol b/test/foundry/BulkSignature.t.sol new file mode 100644 index 000000000..84459d669 --- /dev/null +++ b/test/foundry/BulkSignature.t.sol @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "./utils//BaseOrderTest.sol"; + +import { EIP712MerkleTree } from "./utils/EIP712MerkleTree.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + OrderComponents, + OrderParameters, + ConsiderationItem, + OfferItem, + Order, + OrderType +} from "../../contracts/lib/ConsiderationStructs.sol"; + +import { ItemType } from "../../contracts/lib/ConsiderationEnums.sol"; + +contract BulkSignatureTest is BaseOrderTest { + OrderComponents private _empty; + SparseArgs private _defaultArgs = SparseArgs({ height: 2, orderIndex: 0 }); + + struct Context { + ConsiderationInterface seaport; + bool useCompact2098; + SparseArgs args; + } + + struct SparseArgs { + uint8 height; + uint24 orderIndex; + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testBulkSignature() public { + test( + this.execBulkSignature, + Context({ + seaport: consideration, + args: _defaultArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignature, + Context({ + seaport: referenceConsideration, + args: _defaultArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignature, + Context({ + seaport: consideration, + args: _defaultArgs, + useCompact2098: true + }) + ); + test( + this.execBulkSignature, + Context({ + seaport: referenceConsideration, + args: _defaultArgs, + useCompact2098: true + }) + ); + } + + function execBulkSignature(Context memory context) external stateless { + string memory offerer = "offerer"; + (address addr, uint256 key) = makeAddrAndKey(offerer); + addErc721OfferItem(1); + test721_1.mint(address(addr), 1); + vm.prank(addr); + test721_1.setApprovalForAll(address(context.seaport), true); + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.NATIVE, + token: address(0), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1, + recipient: payable(addr) + }) + ); + configureOrderParameters(addr); + configureOrderComponents(context.seaport); + OrderComponents[] memory orderComponents = new OrderComponents[](3); + orderComponents[0] = baseOrderComponents; + // other order components can remain empty + + EIP712MerkleTree merkleTree = new EIP712MerkleTree(); + bytes memory bulkSignature = merkleTree.signBulkOrder( + context.seaport, + key, + orderComponents, + uint24(0), + context.useCompact2098 + ); + Order memory order = Order({ + parameters: baseOrderParameters, + signature: bulkSignature + }); + context.seaport.fulfillOrder{ value: 1 }(order, bytes32(0)); + + // merkleTree. + } + + function testBulkSignatureSparse() public { + test( + this.execBulkSignatureSparse, + Context({ + seaport: consideration, + args: _defaultArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignatureSparse, + Context({ + seaport: referenceConsideration, + args: _defaultArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignatureSparse, + Context({ + seaport: consideration, + args: _defaultArgs, + useCompact2098: true + }) + ); + test( + this.execBulkSignatureSparse, + Context({ + seaport: referenceConsideration, + args: _defaultArgs, + useCompact2098: true + }) + ); + } + + function execBulkSignatureSparse( + Context memory context + ) external stateless { + string memory offerer = "offerer"; + (address addr, uint256 key) = makeAddrAndKey(offerer); + addErc721OfferItem(1); + test721_1.mint(address(addr), 1); + vm.prank(addr); + test721_1.setApprovalForAll(address(context.seaport), true); + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.NATIVE, + token: address(0), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1, + recipient: payable(addr) + }) + ); + configureOrderParameters(addr); + configureOrderComponents(context.seaport); + OrderComponents[] memory orderComponents = new OrderComponents[](3); + orderComponents[0] = baseOrderComponents; + // other order components can remain empty + + EIP712MerkleTree merkleTree = new EIP712MerkleTree(); + bytes memory bulkSignature = merkleTree.signSparseBulkOrder( + context.seaport, + key, + baseOrderComponents, + context.args.height, + context.args.orderIndex, + context.useCompact2098 + ); + Order memory order = Order({ + parameters: baseOrderParameters, + signature: bulkSignature + }); + context.seaport.fulfillOrder{ value: 1 }(order, bytes32(0)); + } + + function testSparseBulkSignatureFuzz(SparseArgs memory sparseArgs) public { + sparseArgs.height = uint8(bound(sparseArgs.height, 1, 24)); + sparseArgs.orderIndex = uint24( + bound(sparseArgs.orderIndex, 0, 2 ** uint256(sparseArgs.height) - 1) + ); + + test( + this.execBulkSignatureSparse, + Context({ + seaport: consideration, + args: sparseArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignatureSparse, + Context({ + seaport: referenceConsideration, + args: sparseArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignatureSparse, + Context({ + seaport: consideration, + args: sparseArgs, + useCompact2098: true + }) + ); + test( + this.execBulkSignatureSparse, + Context({ + seaport: referenceConsideration, + args: sparseArgs, + useCompact2098: true + }) + ); + } + + function execBulkSignatureIndexOutOfBounds( + Context memory context + ) external stateless { + string memory offerer = "offerer"; + (address addr, uint256 key) = makeAddrAndKey(offerer); + addErc721OfferItem(1); + test721_1.mint(address(addr), 1); + test721_1.mint(address(addr), 2); + vm.prank(addr); + test721_1.setApprovalForAll(address(context.seaport), true); + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.NATIVE, + token: address(0), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1, + recipient: payable(addr) + }) + ); + configureOrderParameters(addr); + configureOrderComponents(context.seaport); + OrderComponents[] memory orderComponents = new OrderComponents[](3); + orderComponents[0] = baseOrderComponents; + + ( + OrderParameters memory secondOrderParameters, + OrderComponents memory secondOrderComponents + ) = setUpSecondOrder(context, addr); + + orderComponents[1] = secondOrderComponents; + + EIP712MerkleTree merkleTree = new EIP712MerkleTree(); + + // Get the signature for the second order. + bytes memory bulkSignature = merkleTree.signBulkOrder( + context.seaport, + key, + orderComponents, + 1, + context.useCompact2098 + ); + + Order memory order = Order({ + parameters: secondOrderParameters, + signature: bulkSignature + }); + + // Fulfill the second order. + context.seaport.fulfillOrder{ value: 1 }(order, bytes32(0)); + assertEq(test721_1.ownerOf(2), address(this)); + + // Get the signature for the first order. + bulkSignature = merkleTree.signBulkOrder( + context.seaport, + key, + orderComponents, + 0, + context.useCompact2098 + ); + + uint256 signatureLength = context.useCompact2098 ? 64 : 65; + + // Swap in a fake index. Here, we use 4 instead of 0. + assembly { + // mload(bulkSignature) := signatureLength + let indexAndProofDataPointer := add( + signatureLength, + add(bulkSignature, 0x20) + ) + let indexAndProofData := mload(indexAndProofDataPointer) + let maskedProofData := and( + indexAndProofData, + 0x000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + let fakeIndexAndProofData := or( + maskedProofData, + 0x0000040000000000000000000000000000000000000000000000000000000000 + ) + mstore(indexAndProofDataPointer, fakeIndexAndProofData) + } + + order = Order({ + parameters: baseOrderParameters, + signature: bulkSignature + }); + + // Fulfill the first order using th bulk signature with the out of + // bounds index.. + context.seaport.fulfillOrder{ value: 1 }(order, bytes32(0)); + + // Should wrap around and fulfill the first order, which is for token ID + // 1. + assertEq(test721_1.ownerOf(1), address(this)); + } + + function setUpSecondOrder( + Context memory context, + address addr + ) + public + view + returns ( + OrderParameters memory _secondOrderParameters, + OrderComponents memory _secondOrderComponents + ) + { + OfferItem memory secondOfferItem; + secondOfferItem = OfferItem( + ItemType.ERC721, + address(test721_1), + 2, + 1, + 1 + ); + + OfferItem[] memory offerItems = new OfferItem[](1); + offerItems[0] = secondOfferItem; + + OrderParameters memory secondOrderParameters = OrderParameters( + address(addr), + address(0), + offerItems, + considerationItems, + OrderType.FULL_OPEN, + block.timestamp, + block.timestamp + 1, + bytes32(0), + 0, + bytes32(0), + 1 + ); + + OrderComponents memory secondOrderComponents = getOrderComponents( + secondOrderParameters, + context.seaport.getCounter(addr) + ); + + return (secondOrderParameters, secondOrderComponents); + } + + function testExecBulkSignatureIndexOutOfBounds() public { + test( + this.execBulkSignatureIndexOutOfBounds, + Context({ + seaport: consideration, + args: _defaultArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignatureIndexOutOfBounds, + Context({ + seaport: referenceConsideration, + args: _defaultArgs, + useCompact2098: false + }) + ); + test( + this.execBulkSignatureIndexOutOfBounds, + Context({ + seaport: consideration, + args: _defaultArgs, + useCompact2098: true + }) + ); + test( + this.execBulkSignatureIndexOutOfBounds, + Context({ + seaport: referenceConsideration, + args: _defaultArgs, + useCompact2098: true + }) + ); + } +} diff --git a/test/foundry/CeilEquivalenceTest.t.sol b/test/foundry/CeilEquivalenceTest.t.sol index 168233ac6..463c21f8f 100644 --- a/test/foundry/CeilEquivalenceTest.t.sol +++ b/test/foundry/CeilEquivalenceTest.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; contract CeilEquivalenceTest { - function testCeilEquivalence(uint256 numerator, uint256 denominator) - public - pure - { + function testCeilEquivalence( + uint256 numerator, + uint256 denominator + ) public pure { // There is intermediate overflow for the unoptimized ceil // but for the sake of this test we'll ignore those cases. numerator %= type(uint128).max; diff --git a/test/foundry/ConsiderationErrors.t.sol b/test/foundry/ConsiderationErrors.t.sol new file mode 100644 index 000000000..35e375d52 --- /dev/null +++ b/test/foundry/ConsiderationErrors.t.sol @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + +import { + ConsiderationErrorsWrapper +} from "./utils/ConsiderationErrorsWrapper.sol"; + +import { Side } from "../../contracts/lib/ConsiderationEnums.sol"; + +contract ConsiderationErrors is BaseOrderTest, ConsiderationErrorsWrapper { + address someAddress; + bytes32 someBytes32; + + constructor() { + someAddress = makeAddr("someAddress"); + someBytes32 = keccak256(abi.encodePacked("someBytes32")); + } + + function test_revertBadFraction() public { + vm.expectRevert(abi.encodeWithSignature("BadFraction()")); + this.__revertBadFraction(); + } + + function test_revertConsiderationNotMet() public { + vm.expectRevert( + abi.encodeWithSignature( + "ConsiderationNotMet(uint256,uint256,uint256)", + 1, + 2, + 3 + ) + ); + this.__revertConsiderationNotMet(1, 2, 3); + } + + function test_revertCriteriaNotEnabledForItem() public { + vm.expectRevert(abi.encodeWithSignature("CriteriaNotEnabledForItem()")); + this.__revertCriteriaNotEnabledForItem(); + } + + function test_revertInsufficientNativeTokensSupplied() public { + vm.expectRevert( + abi.encodeWithSignature("InsufficientNativeTokensSupplied()") + ); + this.__revertInsufficientNativeTokensSupplied(); + } + + function test_revertInvalidBasicOrderParameterEncoding() public { + vm.expectRevert( + abi.encodeWithSignature("InvalidBasicOrderParameterEncoding()") + ); + this.__revertInvalidBasicOrderParameterEncoding(); + } + + function test_revertInvalidCallToConduit() public { + vm.expectRevert( + abi.encodeWithSignature( + "InvalidCallToConduit(address)", + someAddress + ) + ); + this.__revertInvalidCallToConduit(someAddress); + } + + function test_revertInvalidConduit() public { + vm.expectRevert( + abi.encodeWithSignature( + "InvalidConduit(bytes32,address)", + someBytes32, + someAddress + ) + ); + this.__revertInvalidConduit(someBytes32, someAddress); + } + + function test_revertInvalidERC721TransferAmount() public { + vm.expectRevert( + abi.encodeWithSignature("InvalidERC721TransferAmount(uint256)", 4) + ); + this.__revertInvalidERC721TransferAmount(4); + } + + function test_revertInvalidMsgValue() public { + vm.expectRevert(abi.encodeWithSignature("InvalidMsgValue(uint256)", 5)); + this.__revertInvalidMsgValue(5); + } + + function test_revertInvalidNativeOfferItem() public { + vm.expectRevert(abi.encodeWithSignature("InvalidNativeOfferItem()")); + this.__revertInvalidNativeOfferItem(); + } + + function test_revertInvalidProof() public { + vm.expectRevert(abi.encodeWithSignature("InvalidProof()")); + this.__revertInvalidProof(); + } + + function test_revertInvalidContractOrder() public { + vm.expectRevert( + abi.encodeWithSignature( + "InvalidContractOrder(bytes32)", + someBytes32 + ) + ); + this.__revertInvalidContractOrder(someBytes32); + } + + function test_revertInvalidTime() public { + vm.expectRevert( + abi.encodeWithSignature("InvalidTime(uint256,uint256)", 6, 7) + ); + this.__revertInvalidTime(6, 7); + } + + function test_revertMismatchedFulfillmentOfferAndConsiderationComponents() + public + { + vm.expectRevert( + abi.encodeWithSignature( + "MismatchedFulfillmentOfferAndConsiderationComponents(uint256)", + 8 + ) + ); + this.__revertMismatchedFulfillmentOfferAndConsiderationComponents(8); + } + + function test_revertMissingOriginalConsiderationItems() public { + vm.expectRevert( + abi.encodeWithSignature("MissingOriginalConsiderationItems()") + ); + this.__revertMissingOriginalConsiderationItems(); + } + + function test_revertNoReentrantCalls() public { + vm.expectRevert(abi.encodeWithSignature("NoReentrantCalls()")); + this.__revertNoReentrantCalls(); + } + + function test_revertNoSpecifiedOrdersAvailable() public { + vm.expectRevert( + abi.encodeWithSignature("NoSpecifiedOrdersAvailable()") + ); + this.__revertNoSpecifiedOrdersAvailable(); + } + + function test_revertOfferAndConsiderationRequiredOnFulfillment() public { + vm.expectRevert( + abi.encodeWithSignature( + "OfferAndConsiderationRequiredOnFulfillment()" + ) + ); + this.__revertOfferAndConsiderationRequiredOnFulfillment(); + } + + function test_revertOrderAlreadyFilled() public { + vm.expectRevert( + abi.encodeWithSignature("OrderAlreadyFilled(bytes32)", someBytes32) + ); + this.__revertOrderAlreadyFilled(someBytes32); + } + + function test_revertOrderCriteriaResolverOutOfRange() public { + vm.expectRevert( + abi.encodeWithSignature( + "OrderCriteriaResolverOutOfRange(uint8)", + Side.CONSIDERATION + ) + ); + this.__revertOrderCriteriaResolverOutOfRange(Side.CONSIDERATION); + } + + function test_revertOrderIsCancelled() public { + vm.expectRevert( + abi.encodeWithSignature("OrderIsCancelled(bytes32)", someBytes32) + ); + this.__revertOrderIsCancelled(someBytes32); + } + + function test_revertOrderPartiallyFilled() public { + vm.expectRevert( + abi.encodeWithSignature( + "OrderPartiallyFilled(bytes32)", + someBytes32 + ) + ); + this.__revertOrderPartiallyFilled(someBytes32); + } + + function test_revertPartialFillsNotEnabledForOrder() public { + vm.expectRevert( + abi.encodeWithSignature("PartialFillsNotEnabledForOrder()") + ); + this.__revertPartialFillsNotEnabledForOrder(); + } + + function test_revertUnresolvedConsiderationCriteria() public { + vm.expectRevert( + abi.encodeWithSignature( + "UnresolvedConsiderationCriteria(uint256,uint256)", + 9, + 10 + ) + ); + this.__revertUnresolvedConsiderationCriteria(9, 10); + } + + function test_revertUnresolvedOfferCriteria() public { + vm.expectRevert( + abi.encodeWithSignature( + "UnresolvedOfferCriteria(uint256,uint256)", + 11, + 12 + ) + ); + this.__revertUnresolvedOfferCriteria(11, 12); + } + + function test_revertUnusedItemParameters() public { + vm.expectRevert(abi.encodeWithSignature("UnusedItemParameters()")); + this.__revertUnusedItemParameters(); + } +} diff --git a/test/foundry/ConstantsTest.t.sol b/test/foundry/ConstantsTest.t.sol new file mode 100644 index 000000000..e3706856b --- /dev/null +++ b/test/foundry/ConstantsTest.t.sol @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.17; + +import { + BadContractSignature_error_selector, + BadFraction_error_selector, + BadSignatureV_error_selector, + CannotCancelOrder_error_selector, + ConsiderationCriteriaResolverOutOfRange_error_selector, + ConsiderationLengthNotEqualToTotalOriginal_error_selector, + ConsiderationNotMet_error_selector, + CriteriaNotEnabledForItem_error_selector, + InexactFraction_error_selector, + InsufficientNativeTokensSupplied_error_selector, + InvalidBasicOrderParameterEncoding_error_selector, + InvalidCallToConduit_error_selector, + InvalidConduit_error_selector, + InvalidContractOrder_error_selector, + InvalidERC721TransferAmount_error_selector, + InvalidFulfillmentComponentData_error_selector, + InvalidMsgValue_error_selector, + InvalidNativeOfferItem_error_selector, + InvalidProof_error_selector, + InvalidRestrictedOrder_error_selector, + InvalidSignature_error_selector, + InvalidSigner_error_selector, + InvalidTime_error_selector, + MismatchedOfferAndConsiderationComponents_error_selector, + MissingFulfillmentComponentOnAggregation_error_selector, + MissingItemAmount_error_selector, + MissingOriginalConsiderationItems_error_selector, + NativeTokenTransferGenericFailure_error_selector, + NoReentrantCalls_error_selector, + NoSpecifiedOrdersAvailable_error_selector, + OfferAndConsiderationRequiredOnFulfillment_error_selector, + OfferCriteriaResolverOutOfRange_error_selector, + OrderAlreadyFilled_error_selector, + OrderCriteriaResolverOutOfRange_error_selector, + OrderIsCancelled_error_selector, + OrderPartiallyFilled_error_selector, + Panic_error_selector, + PartialFillsNotEnabledForOrder_error_selector, + UnresolvedConsiderationCriteria_error_selector, + UnresolvedOfferCriteria_error_selector, + UnusedItemParameters_error_selector +} from "../../contracts/lib/ConsiderationErrorConstants.sol"; + +import { + BadReturnValueFromERC20OnTransfer_error_selector, + NoContract_error_selector, + TokenTransferGenericFailure_error_selector +} from "../../contracts/lib/TokenTransferrerConstants.sol"; + +import { + generateOrder_selector, + ratifyOrder_selector, + validateOrder_selector +} from "../../contracts/lib/ConsiderationConstants.sol"; + +import { BaseConsiderationTest } from "./utils/BaseConsiderationTest.sol"; + +import { + FulfillmentApplicationErrors +} from "../../contracts/interfaces/FulfillmentApplicationErrors.sol"; + +import { + AmountDerivationErrors +} from "../../contracts/interfaces/AmountDerivationErrors.sol"; + +import { + CriteriaResolutionErrors +} from "../../contracts/interfaces/CriteriaResolutionErrors.sol"; + +import { + ZoneInteractionErrors +} from "../../contracts/interfaces/ZoneInteractionErrors.sol"; + +import { + SignatureVerificationErrors +} from "../../contracts/interfaces/SignatureVerificationErrors.sol"; + +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; + +import { + ReentrancyErrors +} from "../../contracts/interfaces/ReentrancyErrors.sol"; + +import { + ConsiderationEventsAndErrors +} from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; + +import { + ContractOffererInterface +} from "../../contracts/interfaces/ContractOffererInterface.sol"; + +import { ZoneInterface } from "../../contracts/interfaces/ZoneInterface.sol"; + +contract ConstantsTest is BaseConsiderationTest { + function _test(uint256 _constant, bytes4 selector) public { + uint256 _selector = uint256(bytes32(selector) >> uint256(256 - 32)); + assertEq(_constant, _selector); + } + + function testMissingFulfillmentComponentOnAggregation_error_selector() + public + { + _test( + MissingFulfillmentComponentOnAggregation_error_selector, + FulfillmentApplicationErrors + .MissingFulfillmentComponentOnAggregation + .selector + ); + } + + function testOfferAndConsiderationRequiredOnFulfillment_error_selector() + public + { + _test( + OfferAndConsiderationRequiredOnFulfillment_error_selector, + FulfillmentApplicationErrors + .OfferAndConsiderationRequiredOnFulfillment + .selector + ); + } + + function testMismatchedFulfillmentOfferAndConsiderationComponents_error_selector() + public + { + _test( + MismatchedOfferAndConsiderationComponents_error_selector, + FulfillmentApplicationErrors + .MismatchedFulfillmentOfferAndConsiderationComponents + .selector + ); + } + + function testInvalidFulfillmentComponentData_error_selector() public { + _test( + InvalidFulfillmentComponentData_error_selector, + FulfillmentApplicationErrors + .InvalidFulfillmentComponentData + .selector + ); + } + + function testInexactFraction_error_selector() public { + _test( + InexactFraction_error_selector, + AmountDerivationErrors.InexactFraction.selector + ); + } + + function testOrderCriteriaResolverOutOfRange_error_selector() public { + _test( + OrderCriteriaResolverOutOfRange_error_selector, + CriteriaResolutionErrors.OrderCriteriaResolverOutOfRange.selector + ); + } + + function testUnresolvedOfferCriteria_error_selector() public { + _test( + UnresolvedOfferCriteria_error_selector, + CriteriaResolutionErrors.UnresolvedOfferCriteria.selector + ); + } + + function testUnresolvedConsiderationCriteria_error_selector() public { + _test( + UnresolvedConsiderationCriteria_error_selector, + CriteriaResolutionErrors.UnresolvedConsiderationCriteria.selector + ); + } + + function testOfferCriteriaResolverOutOfRange_error_selector() public { + _test( + OfferCriteriaResolverOutOfRange_error_selector, + CriteriaResolutionErrors.OfferCriteriaResolverOutOfRange.selector + ); + } + + function testConsiderationCriteriaResolverOutOfRange_error_selector() + public + { + _test( + ConsiderationCriteriaResolverOutOfRange_error_selector, + CriteriaResolutionErrors + .ConsiderationCriteriaResolverOutOfRange + .selector + ); + } + + function testCriteriaNotEnabledForItem_error_selector() public { + _test( + CriteriaNotEnabledForItem_error_selector, + CriteriaResolutionErrors.CriteriaNotEnabledForItem.selector + ); + } + + function testInvalidProof_error_selector() public { + _test( + InvalidProof_error_selector, + CriteriaResolutionErrors.InvalidProof.selector + ); + } + + function testInvalidRestrictedOrder_error_selector() public { + _test( + InvalidRestrictedOrder_error_selector, + ZoneInteractionErrors.InvalidRestrictedOrder.selector + ); + } + + function testInvalidContractOrder_error_selector() public { + _test( + InvalidContractOrder_error_selector, + ZoneInteractionErrors.InvalidContractOrder.selector + ); + } + + function testBadSignatureV_error_selector() public { + _test( + BadSignatureV_error_selector, + SignatureVerificationErrors.BadSignatureV.selector + ); + } + + function testInvalidSigner_error_selector() public { + _test( + InvalidSigner_error_selector, + SignatureVerificationErrors.InvalidSigner.selector + ); + } + + function testInvalidSignature_error_selector() public { + _test( + InvalidSignature_error_selector, + SignatureVerificationErrors.InvalidSignature.selector + ); + } + + function testBadContractSignature_error_selector() public { + _test( + BadContractSignature_error_selector, + SignatureVerificationErrors.BadContractSignature.selector + ); + } + + function testInvalidERC721TransferAmount_error_selector() public { + _test( + InvalidERC721TransferAmount_error_selector, + TokenTransferrerErrors.InvalidERC721TransferAmount.selector + ); + } + + function testMissingItemAmount_error_selector() public { + _test( + MissingItemAmount_error_selector, + TokenTransferrerErrors.MissingItemAmount.selector + ); + } + + function testUnusedItemParameters_error_selector() public { + _test( + UnusedItemParameters_error_selector, + TokenTransferrerErrors.UnusedItemParameters.selector + ); + } + + function testBadReturnValueFromERC20OnTransfer_error_selector() public { + _test( + BadReturnValueFromERC20OnTransfer_error_selector, + TokenTransferrerErrors.BadReturnValueFromERC20OnTransfer.selector + ); + } + + function testNoContract_error_selector() public { + _test( + NoContract_error_selector, + TokenTransferrerErrors.NoContract.selector + ); + } + + function testTokenTransferGenericFailure_error_selector() public { + _test( + TokenTransferGenericFailure_error_selector, + TokenTransferrerErrors.TokenTransferGenericFailure.selector + ); + } + + function testNoReentrantCalls_error_selector() public { + _test( + NoReentrantCalls_error_selector, + ReentrancyErrors.NoReentrantCalls.selector + ); + } + + function testOrderAlreadyFilled_error_selector() public { + _test( + OrderAlreadyFilled_error_selector, + ConsiderationEventsAndErrors.OrderAlreadyFilled.selector + ); + } + + function testInvalidTime_error_selector() public { + _test( + InvalidTime_error_selector, + ConsiderationEventsAndErrors.InvalidTime.selector + ); + } + + function testInvalidConduit_error_selector() public { + _test( + InvalidConduit_error_selector, + ConsiderationEventsAndErrors.InvalidConduit.selector + ); + } + + function testMissingOriginalConsiderationItems_error_selector() public { + _test( + MissingOriginalConsiderationItems_error_selector, + ConsiderationEventsAndErrors + .MissingOriginalConsiderationItems + .selector + ); + } + + function testInvalidCallToConduit_error_selector() public { + _test( + InvalidCallToConduit_error_selector, + ConsiderationEventsAndErrors.InvalidCallToConduit.selector + ); + } + + function testConsiderationNotMet_error_selector() public { + _test( + ConsiderationNotMet_error_selector, + ConsiderationEventsAndErrors.ConsiderationNotMet.selector + ); + } + + function testInsufficientNativeTokensSupplied_error_selector() public { + _test( + InsufficientNativeTokensSupplied_error_selector, + ConsiderationEventsAndErrors + .InsufficientNativeTokensSupplied + .selector + ); + } + + function testNativeTokenTransferGenericFailure_error_selector() public { + _test( + NativeTokenTransferGenericFailure_error_selector, + ConsiderationEventsAndErrors + .NativeTokenTransferGenericFailure + .selector + ); + } + + function testPartialFillsNotEnabledForOrder_error_selector() public { + _test( + PartialFillsNotEnabledForOrder_error_selector, + ConsiderationEventsAndErrors.PartialFillsNotEnabledForOrder.selector + ); + } + + function testOrderIsCancelled_error_selector() public { + _test( + OrderIsCancelled_error_selector, + ConsiderationEventsAndErrors.OrderIsCancelled.selector + ); + } + + function testOrderPartiallyFilled_error_selector() public { + _test( + OrderPartiallyFilled_error_selector, + ConsiderationEventsAndErrors.OrderPartiallyFilled.selector + ); + } + + function testCannotCancelOrder_error_selector() public { + _test( + CannotCancelOrder_error_selector, + ConsiderationEventsAndErrors.CannotCancelOrder.selector + ); + } + + function testBadFraction_error_selector() public { + _test( + BadFraction_error_selector, + ConsiderationEventsAndErrors.BadFraction.selector + ); + } + + function testInvalidMsgValue_error_selector() public { + _test( + InvalidMsgValue_error_selector, + ConsiderationEventsAndErrors.InvalidMsgValue.selector + ); + } + + function testInvalidBasicOrderParameterEncoding_error_selector() public { + _test( + InvalidBasicOrderParameterEncoding_error_selector, + ConsiderationEventsAndErrors + .InvalidBasicOrderParameterEncoding + .selector + ); + } + + function testNoSpecifiedOrdersAvailable_error_selector() public { + _test( + NoSpecifiedOrdersAvailable_error_selector, + ConsiderationEventsAndErrors.NoSpecifiedOrdersAvailable.selector + ); + } + + function testInvalidNativeOfferItem_error_selector() public { + _test( + InvalidNativeOfferItem_error_selector, + ConsiderationEventsAndErrors.InvalidNativeOfferItem.selector + ); + } + + function testConsiderationLengthNotEqualToTotalOriginal_error_selector() + public + { + _test( + ConsiderationLengthNotEqualToTotalOriginal_error_selector, + ConsiderationEventsAndErrors + .ConsiderationLengthNotEqualToTotalOriginal + .selector + ); + } + + function testPanic_error_selector() public { + bytes memory panicSig = abi.encodeWithSignature("Panic(uint256)", 0); + uint256 selector = uint256(bytes32(panicSig) >> uint256(256 - 32)); + + assertEq(Panic_error_selector, selector); + } + + function testGenerateOrder_selector() public { + _test( + generateOrder_selector, + ContractOffererInterface.generateOrder.selector + ); + } + + function testRatifyOrder_selector() public { + _test( + ratifyOrder_selector, + ContractOffererInterface.ratifyOrder.selector + ); + } + + function testValidateOrder_selector() public { + _test(validateOrder_selector, ZoneInterface.validateOrder.selector); + } +} diff --git a/test/foundry/FulfillAdvancedOrder.t.sol b/test/foundry/FulfillAdvancedOrder.t.sol index d606d6f46..2c45cdde0 100644 --- a/test/foundry/FulfillAdvancedOrder.t.sol +++ b/test/foundry/FulfillAdvancedOrder.t.sol @@ -1,14 +1,29 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + AdvancedOrder, + OrderParameters, + OrderComponents, + CriteriaResolver +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OneWord } from "../../contracts/lib/ConsiderationConstants.sol"; -import { OrderType, ItemType } from "../../contracts/lib/ConsiderationEnums.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { AdvancedOrder, OrderParameters, OrderComponents, CriteriaResolver } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { ERC1155Recipient } from "./utils/ERC1155Recipient.sol"; -import { ConsiderationEventsAndErrors } from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; + +import { + ConsiderationEventsAndErrors +} from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; contract FulfillAdvancedOrder is BaseOrderTest { @@ -49,7 +64,7 @@ contract FulfillAdvancedOrder is BaseOrderTest { vm.assume( args.paymentAmounts[0].add(args.paymentAmounts[1]).add( args.paymentAmounts[2] - ) <= 2**120 - 1 + ) <= 2 ** 120 - 1 ); _; } @@ -69,22 +84,23 @@ contract FulfillAdvancedOrder is BaseOrderTest { vm.assume( args.paymentAmounts[0].add(args.paymentAmounts[1]).add( args.paymentAmounts[2] - ) <= 2**120 - 1 + ) <= 2 ** 120 - 1 ); _; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } } - function testNoNativeOffersFulfillAdvanced(uint8[8] memory itemTypes) - public - { + function testNoNativeOffersFulfillAdvanced( + uint8[8] memory itemTypes + ) public { uint256 tokenId; for (uint256 i; i < 8; i++) { ItemType itemType = ItemType(itemTypes[i] % 4); @@ -115,13 +131,12 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); } - function noNativeOfferItemsFulfillAdvanced(Context memory context) - external - stateless - { + function noNativeOfferItemsFulfillAdvanced( + Context memory context + ) external stateless { configureOrderParameters(alice); uint256 counter = context.consideration.getCounter(alice); - _configureOrderComponents(counter); + configureOrderComponents(counter); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents ); @@ -132,7 +147,7 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); vm.expectRevert(abi.encodeWithSignature("InvalidNativeOfferItem()")); - context.consideration.fulfillAdvancedOrder( + context.consideration.fulfillAdvancedOrder{ value: 1 ether }( AdvancedOrder(baseOrderParameters, 1, 1, signature, ""), new CriteriaResolver[](0), bytes32(0), @@ -162,10 +177,9 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); } - function advancedPartialAscendingOfferAmount1155(Context memory context) - external - stateless - { + function advancedPartialAscendingOfferAmount1155( + Context memory context + ) external stateless { uint256 sumOfPaymentAmounts = (context.args.paymentAmounts[0].mul(2)) .add(context.args.paymentAmounts[1].mul(2)) .add(context.args.paymentAmounts[2].mul(2)); @@ -341,7 +355,9 @@ contract FulfillAdvancedOrder is BaseOrderTest { // set blockTimestamp to right before endTime and set insufficient value for transaction vm.warp(block.timestamp + 999); vm.expectRevert( - ConsiderationEventsAndErrors.InsufficientEtherSupplied.selector + ConsiderationEventsAndErrors + .InsufficientNativeTokensSupplied + .selector ); context.consideration.fulfillAdvancedOrder{ value: context @@ -457,10 +473,9 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); } - function partialFulfillEthTo1155DenominatorOverflow(Context memory context) - external - stateless - { + function partialFulfillEthTo1155DenominatorOverflow( + Context memory context + ) external stateless { // mint 100 tokens test1155_1.mint(alice, 1, 100); @@ -496,7 +511,13 @@ contract FulfillAdvancedOrder is BaseOrderTest { // Create an order to fulfill half of the original offer. context.consideration.fulfillAdvancedOrder{ value: 50 }( - AdvancedOrder(baseOrderParameters, 2**118, 2**119, signature, ""), + AdvancedOrder( + baseOrderParameters, + 2 ** 118, + 2 ** 119, + signature, + "" + ), new CriteriaResolver[](0), bytes32(0), address(0) @@ -577,8 +598,8 @@ contract FulfillAdvancedOrder is BaseOrderTest { AdvancedOrder memory advancedOrder = AdvancedOrder( baseOrderParameters, - 2**119, - 2**119, + 2 ** 119, + 2 ** 119, signature, "" ); @@ -588,39 +609,13 @@ contract FulfillAdvancedOrder is BaseOrderTest { mstore(add(0x40, advancedOrder), shl(120, 1)) } - bytes4 fulfillAdvancedOrderSelector = consideration - .fulfillAdvancedOrder - .selector; - bytes memory fulfillAdvancedOrderCalldata = abi.encodeWithSelector( - fulfillAdvancedOrderSelector, + vm.expectRevert(abi.encodeWithSignature("BadFraction()")); + context.consideration.fulfillAdvancedOrder( advancedOrder, new CriteriaResolver[](0), bytes32(0), address(0) ); - - address considerationAddress = address(consideration); - uint256 calldataLength = fulfillAdvancedOrderCalldata.length; - bool success; - - assembly { - // Call fulfillBasicOrders - success := call( - gas(), - considerationAddress, - 50, - // The fn signature and calldata starts after the - // first OneWord bytes, as those initial bytes just - // contain the length of fulfillAdvancedOrderCalldata - add(fulfillAdvancedOrderCalldata, OneWord), - calldataLength, - // Store output at empty storage location, - // identified using "free memory pointer". - mload(0x40), - OneWord - ) - } - vm.expectRevert(abi.encodeWithSignature("BadFraction()")); } function testPartialFulfillEthTo1155NumeratorOverflowToZero() public { @@ -672,8 +667,8 @@ contract FulfillAdvancedOrder is BaseOrderTest { AdvancedOrder memory advancedOrder = AdvancedOrder( baseOrderParameters, - 2**119, - 2**119, + 2 ** 119, + 2 ** 119, signature, "" ); @@ -683,39 +678,13 @@ contract FulfillAdvancedOrder is BaseOrderTest { mstore(add(0x20, advancedOrder), shl(120, 1)) } - bytes4 fulfillAdvancedOrderSelector = consideration - .fulfillAdvancedOrder - .selector; - bytes memory fulfillAdvancedOrderCalldata = abi.encodeWithSelector( - fulfillAdvancedOrderSelector, + vm.expectRevert(abi.encodeWithSignature("BadFraction()")); + context.consideration.fulfillAdvancedOrder( advancedOrder, new CriteriaResolver[](0), bytes32(0), address(0) ); - - address considerationAddress = address(consideration); - uint256 calldataLength = fulfillAdvancedOrderCalldata.length; - bool success; - - assembly { - // Call fulfillBasicOrders - success := call( - gas(), - considerationAddress, - 50, - // The fn signature and calldata starts after the - // first OneWord bytes, as those initial bytes just - // contain the length of fulfillAdvancedOrderCalldata - add(fulfillAdvancedOrderCalldata, OneWord), - calldataLength, - // Store output at empty storage location, - // identified using "free memory pointer". - mload(0x40), - OneWord - ) - } - vm.expectRevert(abi.encodeWithSignature("BadFraction()")); } function testPartialFulfillEthTo1155NumeratorDenominatorOverflowToZero() @@ -769,8 +738,8 @@ contract FulfillAdvancedOrder is BaseOrderTest { AdvancedOrder memory advancedOrder = AdvancedOrder( baseOrderParameters, - 2**119, - 2**119, + 2 ** 119, + 2 ** 119, signature, "" ); @@ -781,39 +750,13 @@ contract FulfillAdvancedOrder is BaseOrderTest { mstore(add(0x40, advancedOrder), shl(120, 1)) } - bytes4 fulfillAdvancedOrderSelector = consideration - .fulfillAdvancedOrder - .selector; - bytes memory fulfillAdvancedOrderCalldata = abi.encodeWithSelector( - fulfillAdvancedOrderSelector, + vm.expectRevert(abi.encodeWithSignature("BadFraction()")); + context.consideration.fulfillAdvancedOrder( advancedOrder, new CriteriaResolver[](0), bytes32(0), address(0) ); - - address considerationAddress = address(consideration); - uint256 calldataLength = fulfillAdvancedOrderCalldata.length; - bool success; - - assembly { - // Call fulfillBasicOrders - success := call( - gas(), - considerationAddress, - 50, - // The fn signature and calldata starts after the - // first OneWord bytes, as those initial bytes just - // contain the length of fulfillAdvancedOrderCalldata - add(fulfillAdvancedOrderCalldata, OneWord), - calldataLength, - // Store output at empty storage location, - // identified using "free memory pointer". - mload(0x40), - OneWord - ) - } - vm.expectRevert(abi.encodeWithSignature("BadFraction()")); } function testPartialFulfillEthTo1155NumeratorSetToZero() public { @@ -827,10 +770,9 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); } - function partialFulfillEthTo1155NumeratorSetToZero(Context memory context) - external - stateless - { + function partialFulfillEthTo1155NumeratorSetToZero( + Context memory context + ) external stateless { // mint 100 tokens test1155_1.mint(alice, 1, 100); @@ -885,10 +827,9 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); } - function partialFulfillEthTo1155DenominatorSetToZero(Context memory context) - external - stateless - { + function partialFulfillEthTo1155DenominatorSetToZero( + Context memory context + ) external stateless { // mint 100 tokens test1155_1.mint(alice, 1, 100); @@ -932,7 +873,7 @@ contract FulfillAdvancedOrder is BaseOrderTest { ); } - function testpartialFulfillEthTo1155NumeratorDenominatorSetToZero() public { + function testPartialFulfillEthTo1155NumeratorDenominatorSetToZero() public { test( this.partialFulfillEthTo1155NumeratorDenominatorSetToZero, Context(consideration, empty, 0, 0) @@ -988,4 +929,72 @@ contract FulfillAdvancedOrder is BaseOrderTest { address(0) ); } + + function testRevertPartialFulfillInexactFractionEthTo1155( + FuzzInputs memory inputs + ) public { + // ensure an inexact fraction is supplied. + vm.assume(inputs.numer > 0 && inputs.numer < 69); + + test( + this.revertPartialFulfillInexactFractionEthTo1155, + Context(consideration, inputs, 0, 0) + ); + test( + this.revertPartialFulfillInexactFractionEthTo1155, + Context(referenceConsideration, inputs, 0, 0) + ); + } + + function revertPartialFulfillInexactFractionEthTo1155( + Context memory context + ) external stateless { + // mint 100 tokens + test1155_1.mint(alice, 1, 100); + + addErc1155OfferItem(1, 100); + addEthConsiderationItem(alice, 100); + + _configureOrderParameters(alice, address(0), bytes32(0), 0, false); + baseOrderParameters.orderType = OrderType.PARTIAL_OPEN; + OrderComponents memory orderComponents = getOrderComponents( + baseOrderParameters, + context.consideration.getCounter(alice) + ); + bytes32 orderHash = context.consideration.getOrderHash(orderComponents); + + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + { + ( + bool isValidated, + bool isCancelled, + uint256 totalFilled, + uint256 totalSize + ) = context.consideration.getOrderStatus(orderHash); + assertFalse(isValidated); + assertFalse(isCancelled); + assertEq(totalFilled, 0); + assertEq(totalSize, 0); + } + + vm.expectRevert(abi.encodeWithSignature("InexactFraction()")); + // Call fulfillAdvancedOrder with an order with a numerator and denominator of 0. + context.consideration.fulfillAdvancedOrder{ value: 100 }( + AdvancedOrder( + baseOrderParameters, + context.args.numer, + 69, + signature, + "" + ), + new CriteriaResolver[](0), + bytes32(0), + address(0) + ); + } } diff --git a/test/foundry/FulfillAdvancedOrderCriteria.t.sol b/test/foundry/FulfillAdvancedOrderCriteria.t.sol index e8207b1b2..94a60c08f 100644 --- a/test/foundry/FulfillAdvancedOrderCriteria.t.sol +++ b/test/foundry/FulfillAdvancedOrderCriteria.t.sol @@ -1,11 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + import { Merkle } from "murky/Merkle.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { CriteriaResolver, OfferItem, OrderComponents, AdvancedOrder } from "../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + CriteriaResolver, + OfferItem, + OrderComponents, + AdvancedOrder +} from "../../contracts/lib/ConsiderationStructs.sol"; + import { ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; contract FulfillAdvancedOrderCriteria is BaseOrderTest { @@ -22,9 +33,10 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { FuzzArgs args; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } @@ -41,9 +53,9 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { ); } - function testFulfillAdvancedOrderWithCriteriaPreimage(FuzzArgs memory args) - public - { + function testFulfillAdvancedOrderWithCriteriaPreimage( + FuzzArgs memory args + ) public { test( this.fulfillAdvancedOrderWithCriteriaPreimage, Context(consideration, args) @@ -54,7 +66,9 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { ); } - function prepareCriteriaOfferOrder(Context memory context) + function prepareCriteriaOfferOrder( + Context memory context + ) internal returns ( bytes32[] memory hashedIdentifiers, @@ -92,10 +106,9 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { advancedOrder = AdvancedOrder(baseOrderParameters, 1, 1, signature, ""); } - function fulfillAdvancedOrderWithCriteria(Context memory context) - external - stateless - { + function fulfillAdvancedOrderWithCriteria( + Context memory context + ) external stateless { // pick a "random" index in the array of identifiers; use that identifier context.args.index = context.args.index % 8; uint256 identifier = context.args.identifiers[context.args.index]; @@ -125,10 +138,9 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { assertEq(address(this), test721_1.ownerOf(identifier)); } - function fulfillAdvancedOrderWithCriteriaPreimage(Context memory context) - external - stateless - { + function fulfillAdvancedOrderWithCriteriaPreimage( + Context memory context + ) external stateless { context.args.index = context.args.index % 8; ( bytes32[] memory hashedIdentifiers, @@ -170,9 +182,10 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { ); } - function addOfferItem721Criteria(address token, uint256 identifierHash) - internal - { + function addOfferItem721Criteria( + address token, + uint256 identifierHash + ) internal { addOfferItem721Criteria(token, identifierHash, 1, 1); } diff --git a/test/foundry/FulfillAvailableAdvancedOrder.t.sol b/test/foundry/FulfillAvailableAdvancedOrder.t.sol index ebed17425..0c036a99d 100644 --- a/test/foundry/FulfillAvailableAdvancedOrder.t.sol +++ b/test/foundry/FulfillAvailableAdvancedOrder.t.sol @@ -1,19 +1,30 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + AdvancedOrder, + OfferItem, + OrderParameters, + ConsiderationItem, + OrderComponents, + FulfillmentComponent, + CriteriaResolver +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OrderType, BasicOrderType, ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -import { AdditionalRecipient } from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { Order, AdvancedOrder, OfferItem, OrderParameters, ConsiderationItem, OrderComponents, BasicOrderParameters, FulfillmentComponent, CriteriaResolver } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { TestERC721 } from "../../contracts/test/TestERC721.sol"; -import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; -import { TestERC20 } from "../../contracts/test/TestERC20.sol"; -import { ProxyRegistry } from "./interfaces/ProxyRegistry.sol"; -import { OwnableDelegateProxy } from "./interfaces/OwnableDelegateProxy.sol"; -import { ERC1155Recipient } from "./utils/ERC1155Recipient.sol"; + import { stdError } from "forge-std/Test.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; contract FulfillAvailableAdvancedOrder is BaseOrderTest { @@ -52,7 +63,7 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { vm.assume( inputs.paymentAmts[0].add(inputs.paymentAmts[1]).add( inputs.paymentAmts[2] - ) <= 2**128 - 1 + ) <= 2 ** 128 - 1 ); _; } @@ -68,14 +79,15 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { inputs.paymentAmts[0].mul(inputs.denom) + inputs.paymentAmts[1].mul(inputs.denom) + inputs.paymentAmts[2].mul(inputs.denom) <= - 2**128 - 1 + 2 ** 128 - 1 ); _; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } @@ -116,13 +128,12 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { ); } - function noNativeOfferItemsFulfillAvailableAdvanced(Context memory context) - external - stateless - { + function noNativeOfferItemsFulfillAvailableAdvanced( + Context memory context + ) external stateless { configureOrderParameters(alice); uint256 counter = context.consideration.getCounter(alice); - _configureOrderComponents(counter); + configureOrderComponents(counter); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents ); @@ -147,7 +158,7 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { addEthConsiderationItem(alice, 1); configureOrderParameters(alice); counter = context.consideration.getCounter(alice); - _configureOrderComponents(counter); + configureOrderComponents(counter); bytes32 orderHash2 = context.consideration.getOrderHash( baseOrderComponents ); @@ -192,23 +203,65 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { } } - function testFulfillAvailableAdvancedOrderMissingItemAmount() public { + function testFulfillAvailableAdvancedOrderPanic() public { + for (uint256 i; i < 4; ++i) { + // skip 721s + if (i == 2) { + continue; + } + test( + this.fulfillAvailableAdvancedOrdersPanic, + Context(consideration, empty, ItemType(i)) + ); + test( + this.fulfillAvailableAdvancedOrdersPanic, + Context(referenceConsideration, empty, ItemType(i)) + ); + } + } + + function testFulfillAvailableAdvancedOrdersAggregateMissingOfferItemAmounts() + public + { for (uint256 i; i < 4; ++i) { // skip 721s if (i == 2) { continue; } test( - this.fulfillAvailableAdvancedOrdersMissingItemAmount, + this + .fulfillAvailableAdvancedOrdersAggregateMissingOfferItemAmounts, Context(consideration, empty, ItemType(i)) ); test( - this.fulfillAvailableAdvancedOrdersMissingItemAmount, + this + .fulfillAvailableAdvancedOrdersAggregateMissingOfferItemAmounts, Context(referenceConsideration, empty, ItemType(i)) ); } } + function testFulfillAvailableAdvancedOrdersAggregateMissingConsiderationItemAmounts( + FuzzInputs memory args + ) public validateInputs(args) { + for (uint256 i; i < 4; ++i) { + // skip 721s + if (i == 2) { + continue; + } + test( + this + .fulfillAvailableAdvancedOrdersAggregateMissingConsiderationItemAmounts, + Context(consideration, args, ItemType(i)) + ); + test( + this + .fulfillAvailableAdvancedOrdersAggregateMissingConsiderationItemAmounts, + Context(referenceConsideration, args, ItemType(i)) + ); + } + } + function testFulfillSingleOrderViaFulfillAvailableAdvancedOrdersEthToErc1155( FuzzInputs memory args ) @@ -254,10 +307,9 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { ); } - function fulfillAvailableAdvancedOrdersOverflow(Context memory context) - external - stateless - { + function fulfillAvailableAdvancedOrdersOverflow( + Context memory context + ) external stateless { test721_1.mint(alice, 1); addErc721OfferItem(1); addConsiderationItem(alice, context.itemType, 1, 100); @@ -361,7 +413,7 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { ); } - function fulfillAvailableAdvancedOrdersMissingItemAmount( + function fulfillAvailableAdvancedOrdersPanic( Context memory context ) external stateless { test721_1.mint(alice, 1); @@ -469,6 +521,225 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { ); } + function fulfillAvailableAdvancedOrdersAggregateMissingOfferItemAmounts( + Context memory context + ) external stateless { + // add zero-amount 1155 offer item + addErc1155OfferItem(context.args.id, 0); + addConsiderationItem(alice, context.itemType, 1, 100); + + OrderParameters memory orderParameters = OrderParameters( + address(alice), + address(0), + offerItems, + considerationItems, + OrderType.FULL_OPEN, + block.timestamp, + block.timestamp + 1, + bytes32(0), + 0, + bytes32(0), + considerationItems.length + ); + + OrderComponents memory firstOrderComponents = getOrderComponents( + orderParameters, + context.consideration.getCounter(alice) + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + context.consideration.getOrderHash(firstOrderComponents) + ); + + delete offerItems; + delete considerationItems; + + addErc1155OfferItem(context.args.id, 0); + addConsiderationItem(alice, context.itemType, 1, 100); + + OrderParameters memory secondOrderParameters = OrderParameters( + address(bob), + address(0), + offerItems, + considerationItems, + OrderType.FULL_OPEN, + block.timestamp, + block.timestamp + 1, + bytes32(0), + 0, + bytes32(0), + considerationItems.length + ); + + OrderComponents memory secondOrderComponents = getOrderComponents( + secondOrderParameters, + context.consideration.getCounter(bob) + ); + bytes memory secondSignature = signOrder( + context.consideration, + bobPk, + context.consideration.getOrderHash(secondOrderComponents) + ); + + AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2); + advancedOrders[0] = AdvancedOrder( + orderParameters, + uint120(1), + uint120(1), + signature, + "0x" + ); + advancedOrders[1] = AdvancedOrder( + secondOrderParameters, + uint120(1), + uint120(1), + secondSignature, + "0x" + ); + + offerComponents.push(FulfillmentComponent(0, 0)); + offerComponentsArray.push(offerComponents); + delete offerComponents; + offerComponents.push(FulfillmentComponent(1, 0)); + offerComponentsArray.push(offerComponents); + delete offerComponents; + + // aggregate eth considerations together + considerationComponents.push(FulfillmentComponent(0, 0)); + considerationComponents.push(FulfillmentComponent(1, 0)); + considerationComponentsArray.push(considerationComponents); + delete considerationComponents; + + CriteriaResolver[] memory criteriaResolvers; + + vm.expectRevert(abi.encodeWithSignature("MissingItemAmount()")); + context.consideration.fulfillAvailableAdvancedOrders{ value: 99 }( + advancedOrders, + criteriaResolvers, + offerComponentsArray, + considerationComponentsArray, + bytes32(0), + address(0), + 100 + ); + } + + function fulfillAvailableAdvancedOrdersAggregateMissingConsiderationItemAmounts( + Context memory context + ) external stateless { + // add zero-amount 1155 offer item + addErc1155OfferItem(context.args.id, 100); + if (context.itemType == ItemType.ERC721) { + addConsiderationItem( + alice, + ItemType.ERC721, + address(test721_1), + 0, + 0, + 0 + ); + } else { + addConsiderationItem(alice, context.itemType, 0, 0); + } + + OrderParameters memory orderParameters = OrderParameters( + address(alice), + address(0), + offerItems, + considerationItems, + OrderType.FULL_OPEN, + block.timestamp, + block.timestamp + 1, + bytes32(0), + 0, + bytes32(0), + considerationItems.length + ); + + OrderComponents memory firstOrderComponents = getOrderComponents( + orderParameters, + context.consideration.getCounter(alice) + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + context.consideration.getOrderHash(firstOrderComponents) + ); + + delete offerItems; + delete considerationItems; + + addErc1155OfferItem(context.args.id, 100); + addConsiderationItem(alice, context.itemType, 0, 0); + + OrderParameters memory secondOrderParameters = OrderParameters( + address(bob), + address(0), + offerItems, + considerationItems, + OrderType.FULL_OPEN, + block.timestamp, + block.timestamp + 1, + bytes32(0), + 0, + bytes32(0), + considerationItems.length + ); + + OrderComponents memory secondOrderComponents = getOrderComponents( + secondOrderParameters, + context.consideration.getCounter(bob) + ); + bytes memory secondSignature = signOrder( + context.consideration, + bobPk, + context.consideration.getOrderHash(secondOrderComponents) + ); + + AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2); + advancedOrders[0] = AdvancedOrder( + orderParameters, + uint120(1), + uint120(1), + signature, + "0x" + ); + advancedOrders[1] = AdvancedOrder( + secondOrderParameters, + uint120(1), + uint120(1), + secondSignature, + "0x" + ); + + offerComponents.push(FulfillmentComponent(0, 0)); + offerComponentsArray.push(offerComponents); + delete offerComponents; + offerComponents.push(FulfillmentComponent(1, 0)); + offerComponentsArray.push(offerComponents); + delete offerComponents; + + // aggregate zero-amount considerations together + considerationComponents.push(FulfillmentComponent(0, 0)); + considerationComponents.push(FulfillmentComponent(1, 0)); + considerationComponentsArray.push(considerationComponents); + delete considerationComponents; + + CriteriaResolver[] memory criteriaResolvers; + + vm.expectRevert(abi.encodeWithSignature("MissingItemAmount()")); + context.consideration.fulfillAvailableAdvancedOrders{ value: 99 }( + advancedOrders, + criteriaResolvers, + offerComponentsArray, + considerationComponentsArray, + bytes32(0), + address(0), + 100 + ); + } + function fulfillSingleOrderViaFulfillAvailableAdvancedOrdersEthToErc1155( Context memory context ) external stateless { @@ -779,8 +1050,8 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { AdvancedOrder[] memory orders = new AdvancedOrder[](2); orders[0] = AdvancedOrder( baseOrderParameters, - 2**118, - 2**119, + 2 ** 118, + 2 ** 119, signature, "" ); @@ -879,8 +1150,8 @@ contract FulfillAvailableAdvancedOrder is BaseOrderTest { AdvancedOrder[] memory orders = new AdvancedOrder[](2); orders[0] = AdvancedOrder( baseOrderParameters, - 2**118, - 2**119, + 2 ** 118, + 2 ** 119, signature, "" ); diff --git a/test/foundry/FulfillAvailableAdvancedOrderCriteria.t.sol b/test/foundry/FulfillAvailableAdvancedOrderCriteria.t.sol index 79f7abe38..7b77befd4 100644 --- a/test/foundry/FulfillAvailableAdvancedOrderCriteria.t.sol +++ b/test/foundry/FulfillAvailableAdvancedOrderCriteria.t.sol @@ -1,11 +1,23 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + import { Merkle } from "murky/Merkle.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { CriteriaResolver, OfferItem, OrderComponents, AdvancedOrder, FulfillmentComponent } from "../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + CriteriaResolver, + OfferItem, + OrderComponents, + AdvancedOrder, + FulfillmentComponent +} from "../../contracts/lib/ConsiderationStructs.sol"; + import { ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; contract FulfillAdvancedOrderCriteria is BaseOrderTest { @@ -22,15 +34,18 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { FuzzArgs args; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } } - function prepareCriteriaOfferOrder(Context memory context) + function prepareCriteriaOfferOrder( + Context memory context + ) internal returns ( bytes32[] memory hashedIdentifiers, @@ -68,9 +83,10 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { advancedOrder = AdvancedOrder(baseOrderParameters, 1, 1, signature, ""); } - function addOfferItem721Criteria(address token, uint256 identifierHash) - internal - { + function addOfferItem721Criteria( + address token, + uint256 identifierHash + ) internal { addOfferItem721Criteria(token, identifierHash, 1, 1); } @@ -112,10 +128,9 @@ contract FulfillAdvancedOrderCriteria is BaseOrderTest { ); } - function fulfillAvailableAdvancedOrdersWithCriteria(Context memory context) - external - stateless - { + function fulfillAvailableAdvancedOrdersWithCriteria( + Context memory context + ) external stateless { // pick a "random" index in the array of identifiers; use that identifier context.args.index = context.args.index % 8; uint256 identifier = context.args.identifiers[context.args.index]; diff --git a/test/foundry/FulfillBasicOrderTest.t.sol b/test/foundry/FulfillBasicOrderTest.t.sol index c5373756b..b6e308d0b 100644 --- a/test/foundry/FulfillBasicOrderTest.t.sol +++ b/test/foundry/FulfillBasicOrderTest.t.sol @@ -1,29 +1,48 @@ // SPDX-License-Identifier: MIT -//Author: CupOJoseph +pragma solidity ^0.8.17; -pragma solidity >=0.8.13; +import { + OrderType, + BasicOrderType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + AdditionalRecipient, + Order, + OrderComponents, + BasicOrderParameters +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OrderType, BasicOrderType, ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -import { AdditionalRecipient, Order } from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { OfferItem, ConsiderationItem, OrderComponents, BasicOrderParameters } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; +import { + InvalidEthRecipient +} from "../../contracts/test/InvalidEthRecipient.sol"; + import { TestERC721 } from "../../contracts/test/TestERC721.sol"; import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; import { TestERC20 } from "../../contracts/test/TestERC20.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; -import { OrderParameters } from "./utils/reentrancy/ReentrantStructs.sol"; +import { + ConsiderationEventsAndErrors +} from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; -contract FulfillBasicOrderTest is BaseOrderTest { +contract FulfillBasicOrderTest is BaseOrderTest, ConsiderationEventsAndErrors { using ArithmeticUtil for uint128; uint256 badIdentifier; address badToken; BasicOrderParameters basicOrderParameters; + address payable invalidRecipientAddress; + FuzzInputsCommon empty; struct FuzzInputsCommon { address zone; @@ -31,6 +50,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { uint128 paymentAmount; bytes32 zoneHash; uint256 salt; + bool useConduit; } struct Context { ConsiderationInterface consideration; @@ -50,18 +70,18 @@ contract FulfillBasicOrderTest is BaseOrderTest { _; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } } - function testBasicEthTo721(FuzzInputsCommon memory inputs) - public - validateInputs(Context(consideration, inputs, 0)) - { + function testBasicEthTo721( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { addErc721OfferItem(inputs.tokenId); addEthConsiderationItem(alice, inputs.paymentAmount); _configureBasicOrderParametersEthTo721(inputs); @@ -70,10 +90,77 @@ contract FulfillBasicOrderTest is BaseOrderTest { test(this.basicEthTo721, Context(referenceConsideration, inputs, 0)); } - function testBasicErc20To721(FuzzInputsCommon memory inputs) - public - validateInputs(Context(consideration, inputs, 0)) - { + function testBasicEthTo721Efficient( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + addErc721OfferItem(inputs.tokenId); + addEthConsiderationItem(alice, inputs.paymentAmount); + _configureBasicOrderParametersEthTo721(inputs); + + test(this.basicEthTo721Efficient, Context(consideration, inputs, 0)); + test( + this.basicEthTo721Efficient, + Context(referenceConsideration, inputs, 0) + ); + } + + function testBasicEthTo721WithAdditionalRecipients( + FuzzInputsCommon memory inputs, + uint256 numAdditionalRecipients + ) public validateInputs(Context(consideration, inputs, 0)) { + vm.assume(numAdditionalRecipients > 0); + vm.assume(inputs.paymentAmount < 10); + uint256 finalAdditionalRecipients = numAdditionalRecipients % 10; + + addErc721OfferItem(inputs.tokenId); + addEthConsiderationItem(alice, 1); + + AdditionalRecipient[] + storage _additionalRecipients = additionalRecipients; + for (uint256 i = 0; i < finalAdditionalRecipients; i++) { + _additionalRecipients.push( + AdditionalRecipient({ + recipient: alice, + amount: inputs.paymentAmount + }) + ); + addEthConsiderationItem(alice, inputs.paymentAmount); + } + _configureBasicOrderParametersEthTo721(inputs); + basicOrderParameters.additionalRecipients = _additionalRecipients; + basicOrderParameters.considerationAmount = 1; + basicOrderParameters + .totalOriginalAdditionalRecipients = finalAdditionalRecipients; + basicOrderParameters.fulfillerConduitKey = inputs.useConduit + ? conduitKeyOne + : bytes32(0); + + test( + this.basicEthTo721WithAdditionalRecipients, + Context(consideration, inputs, 0) + ); + test( + this.basicEthTo721WithAdditionalRecipients, + Context(referenceConsideration, inputs, 0) + ); + } + + function testBasicEthTo721WithZone( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + inputs.zone = address(0); + + addErc721OfferItem(inputs.tokenId); + addEthConsiderationItem(alice, inputs.paymentAmount); + _configureBasicOrderParametersEthTo721(inputs); + + test(this.basicEthTo721, Context(consideration, inputs, 0)); + test(this.basicEthTo721, Context(referenceConsideration, inputs, 0)); + } + + function testBasicErc20To721( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { addErc721OfferItem(inputs.tokenId); addErc20ConsiderationItem(alice, inputs.paymentAmount); _configureBasicOrderParametersErc20To721(inputs); @@ -82,6 +169,20 @@ contract FulfillBasicOrderTest is BaseOrderTest { test(this.basicErc20To721, Context(referenceConsideration, inputs, 0)); } + function testRevertCancelledOrder( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + addErc721OfferItem(inputs.tokenId); + addErc20ConsiderationItem(alice, inputs.paymentAmount); + _configureBasicOrderParametersErc20To721(inputs); + + test(this.revertCancelledOrder, Context(consideration, inputs, 0)); + test( + this.revertCancelledOrder, + Context(referenceConsideration, inputs, 0) + ); + } + function testBasicEthTo1155( FuzzInputsCommon memory inputs, uint128 tokenAmount @@ -173,6 +274,51 @@ contract FulfillBasicOrderTest is BaseOrderTest { ); } + function testRevertDirtyUpperBitsForAdditionalRecipients() public { + test( + this.revertDirtyUpperBitsForAdditionalRecipients, + Context(consideration, empty, 0) + ); + test( + this.revertDirtyUpperBitsForAdditionalRecipients, + Context(referenceConsideration, empty, 0) + ); + } + + function revertDirtyUpperBitsForAdditionalRecipients( + Context memory context + ) external stateless { + // Create basic order + ( + , + BasicOrderParameters memory _basicOrderParameters + ) = prepareBasicOrder(1); + + // Add additional recipients + _basicOrderParameters.additionalRecipients = new AdditionalRecipient[]( + 4 + ); + for (uint256 i = 0; i < 4; i++) { + _basicOrderParameters.additionalRecipients[ + i + ] = AdditionalRecipient({ recipient: alice, amount: 1 }); + } + + // Get the calldata that will be passed into fulfillBasicOrder. + bytes memory fulfillBasicOrderCalldata = abi.encodeWithSelector( + consideration.fulfillBasicOrder.selector, + _basicOrderParameters + ); + + _dirtyFirstAdditionalRecipient(fulfillBasicOrderCalldata); + + (bool success, ) = address(context.consideration).call( + fulfillBasicOrderCalldata + ); + + require(!success, "Expected revert"); + } + function testRevertUnusedItemParametersAddressSetOnNativeConsideration( FuzzInputsCommon memory inputs, uint128 tokenAmount, @@ -210,7 +356,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { globalSalt++, false ); - _configureOrderComponents(context.consideration.getCounter(alice)); + configureOrderComponents(context.consideration.getCounter(alice)); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents @@ -273,7 +419,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { globalSalt++, false ); - _configureOrderComponents(context.consideration.getCounter(alice)); + configureOrderComponents(context.consideration.getCounter(alice)); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents @@ -296,6 +442,125 @@ contract FulfillBasicOrderTest is BaseOrderTest { context.consideration.fulfillBasicOrder(_basicOrderParameters); } + function testRevertBasicEthTo721ZeroAmount( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + addErc721OfferItem(inputs.tokenId); + addErc20ConsiderationItem(alice, inputs.paymentAmount); + _configureBasicOrderParametersEthTo721(inputs); + + test( + this.revertBasicErc20To721ZeroAmount, + Context(consideration, inputs, 0) + ); + test( + this.revertBasicErc20To721ZeroAmount, + Context(referenceConsideration, inputs, 0) + ); + } + + function revertBasicErc20To721ZeroAmount( + Context memory context + ) external stateless { + test721_1.mint(alice, context.args.tokenId); + + considerationItems[0].startAmount = 0; + considerationItems[0].endAmount = 0; + + _configureOrderParameters( + alice, + address(0), + bytes32(0), + globalSalt++, + false + ); + configureOrderComponents(context.consideration.getCounter(alice)); + + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + BasicOrderParameters + memory _basicOrderParameters = toBasicOrderParameters( + baseOrderComponents, + BasicOrderType.ERC20_TO_ERC721_FULL_OPEN, + signature + ); + + vm.expectRevert(abi.encodeWithSignature("MissingItemAmount()")); + context.consideration.fulfillBasicOrder(_basicOrderParameters); + } + + function testRevertInvalidEthRecipient( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + InvalidEthRecipient invalidRecipient = new InvalidEthRecipient(); + invalidRecipientAddress = payable(address(invalidRecipient)); + vm.deal(invalidRecipientAddress, UINT256_MAX); + + addErc721OfferItem(inputs.tokenId); + addEthConsiderationItem(alice, inputs.paymentAmount); + + AdditionalRecipient[] + storage _additionalRecipients = additionalRecipients; + _additionalRecipients.push( + AdditionalRecipient({ + recipient: invalidRecipientAddress, + amount: 1 + }) + ); + addEthConsiderationItem(invalidRecipientAddress, 1); + + _configureBasicOrderParametersEthTo721(inputs); + basicOrderParameters.additionalRecipients = _additionalRecipients; + basicOrderParameters.considerationAmount = inputs.paymentAmount; + basicOrderParameters.totalOriginalAdditionalRecipients = 1; + + test(this.revertInvalidEthRecipient, Context(consideration, inputs, 0)); + } + + function revertInvalidEthRecipient( + Context memory context + ) external stateless { + test721_1.mint(alice, context.args.tokenId); + + configureOrderComponents( + context.args.zone, + context.args.zoneHash, + context.args.salt, + context.args.useConduit ? conduitKeyOne : bytes32(0) + ); + uint256 counter = context.consideration.getCounter(alice); + baseOrderComponents.counter = counter; + + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + basicOrderParameters.signature = signature; + + bytes + memory expectedRevert = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + vm.prank(invalidRecipientAddress); + vm.expectRevert(expectedRevert); + context.consideration.fulfillBasicOrder{ value: 10000000 }( + basicOrderParameters + ); + } + function testRevertUnusedItemParametersIdentifierSetOnNativeConsideration( FuzzInputsCommon memory inputs, uint128 tokenAmount, @@ -333,7 +598,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { globalSalt++, false ); - _configureOrderComponents(context.consideration.getCounter(alice)); + configureOrderComponents(context.consideration.getCounter(alice)); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents @@ -400,7 +665,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { globalSalt++, false ); - _configureOrderComponents(context.consideration.getCounter(alice)); + configureOrderComponents(context.consideration.getCounter(alice)); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents @@ -448,6 +713,44 @@ contract FulfillBasicOrderTest is BaseOrderTest { ); } + function revertCancelledOrder(Context memory context) external stateless { + test721_1.mint(alice, context.args.tokenId); + + configureOrderComponents( + context.args.zone, + context.args.zoneHash, + context.args.salt, + context.args.useConduit ? conduitKeyOne : bytes32(0) + ); + + uint256 counter = context.consideration.getCounter(alice); + baseOrderComponents.counter = counter; + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + basicOrderParameters.signature = signature; + OrderComponents[] memory myBaseOrderComponents = new OrderComponents[]( + 1 + ); + myBaseOrderComponents[0] = baseOrderComponents; + + vm.prank(alice); + + vm.expectEmit(true, true, true, false, address(context.consideration)); + emit OrderCancelled(orderHash, alice, context.args.zone); + context.consideration.cancel(myBaseOrderComponents); + + vm.expectRevert( + abi.encodeWithSignature("OrderIsCancelled(bytes32)", orderHash) + ); + context.consideration.fulfillBasicOrder(basicOrderParameters); + } + function revertUnusedItemParametersIdentifierSetOnErc20Consideration( Context memory context ) external stateless { @@ -462,7 +765,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { globalSalt++, false ); - _configureOrderComponents(context.consideration.getCounter(alice)); + configureOrderComponents(context.consideration.getCounter(alice)); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents @@ -485,7 +788,121 @@ contract FulfillBasicOrderTest is BaseOrderTest { context.consideration.fulfillBasicOrder(_basicOrderParameters); } - function prepareBasicOrder(uint256 tokenId) + function testRevertInvalidTimestamp( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + addErc721OfferItem(inputs.tokenId); + addEthConsiderationItem(alice, inputs.paymentAmount); + _configureBasicOrderParametersEthTo721(inputs); + + basicOrderParameters.startTime = block.timestamp + 1 days; + basicOrderParameters.endTime = block.timestamp + 2 days; + + test(this.revertInvalidTimestamp, Context(consideration, inputs, 0)); + test( + this.revertInvalidTimestamp, + Context(referenceConsideration, inputs, 0) + ); + } + + function revertInvalidTimestamp(Context memory context) external stateless { + test721_1.mint(alice, context.args.tokenId); + + _configureOrderParameters( + alice, + address(0), + bytes32(0), + globalSalt++, + false + ); + baseOrderComponents.startTime = block.timestamp + 1 days; + baseOrderComponents.endTime = block.timestamp + 2 days; + + uint256 counter = context.consideration.getCounter(alice); + baseOrderComponents.counter = counter; + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + basicOrderParameters.signature = signature; + + vm.expectRevert( + abi.encodeWithSelector( + ConsiderationEventsAndErrors.InvalidTime.selector, + basicOrderParameters.startTime, + basicOrderParameters.endTime + ) + ); + context.consideration.fulfillBasicOrder{ + value: context.args.paymentAmount + }(basicOrderParameters); + } + + function testRevertMissingOriginalConsiderationItems( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs, 0)) { + addErc721OfferItem(inputs.tokenId); + addEthConsiderationItem(alice, inputs.paymentAmount); + + _configureBasicOrderParametersEthTo721(inputs); + basicOrderParameters.considerationAmount = inputs.paymentAmount; + basicOrderParameters.totalOriginalAdditionalRecipients = 5; + + test( + this.revertMissingOriginalConsiderationItems, + Context(consideration, inputs, 0) + ); + test( + this.revertMissingOriginalConsiderationItems, + Context(referenceConsideration, inputs, 0) + ); + } + + function revertMissingOriginalConsiderationItems( + Context memory context + ) external stateless { + test721_1.mint(alice, context.args.tokenId); + + configureOrderComponents( + context.args.zone, + context.args.zoneHash, + context.args.salt, + bytes32(0) + ); + uint256 counter = context.consideration.getCounter(alice); + baseOrderComponents.counter = counter; + + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + basicOrderParameters.signature = signature; + + vm.expectRevert( + abi.encodeWithSelector( + ConsiderationEventsAndErrors + .MissingOriginalConsiderationItems + .selector + ) + ); + context.consideration.fulfillBasicOrder{ + value: context.args.paymentAmount + }(basicOrderParameters); + } + + function prepareBasicOrder( + uint256 tokenId + ) internal returns ( Order memory order, @@ -503,11 +920,11 @@ contract FulfillBasicOrderTest is BaseOrderTest { function basicErc20To1155(Context memory context) external stateless { test1155_1.mint(alice, context.args.tokenId, context.tokenAmount); - _configureOrderComponents( + configureOrderComponents( context.args.zone, context.args.zoneHash, context.args.salt, - bytes32(0) + context.args.useConduit ? conduitKeyOne : bytes32(0) ); uint256 counter = context.consideration.getCounter(alice); baseOrderComponents.counter = counter; @@ -531,11 +948,11 @@ contract FulfillBasicOrderTest is BaseOrderTest { function basicEthTo1155(Context memory context) external stateless { test1155_1.mint(alice, context.args.tokenId, context.tokenAmount); - _configureOrderComponents( + configureOrderComponents( context.args.zone, context.args.zoneHash, context.args.salt, - bytes32(0) + context.args.useConduit ? conduitKeyOne : bytes32(0) ); uint256 counter = context.consideration.getCounter(alice); baseOrderComponents.counter = counter; @@ -561,11 +978,11 @@ contract FulfillBasicOrderTest is BaseOrderTest { function basicEthTo721(Context memory context) external stateless { test721_1.mint(alice, context.args.tokenId); - _configureOrderComponents( + configureOrderComponents( context.args.zone, context.args.zoneHash, context.args.salt, - bytes32(0) + context.args.useConduit ? conduitKeyOne : bytes32(0) ); uint256 counter = context.consideration.getCounter(alice); baseOrderComponents.counter = counter; @@ -585,14 +1002,83 @@ contract FulfillBasicOrderTest is BaseOrderTest { assertEq(address(this), test721_1.ownerOf(context.args.tokenId)); } + function basicEthTo721Efficient(Context memory context) external stateless { + test721_1.mint(alice, context.args.tokenId); + + configureOrderComponents( + context.args.zone, + context.args.zoneHash, + context.args.salt, + context.args.useConduit ? conduitKeyOne : bytes32(0) + ); + uint256 counter = context.consideration.getCounter(alice); + baseOrderComponents.counter = counter; + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + basicOrderParameters.signature = signature; + context.consideration.fulfillBasicOrder_efficient_6GL6yc{ + value: context.args.paymentAmount + }(basicOrderParameters); + assertEq(address(this), test721_1.ownerOf(context.args.tokenId)); + } + + function basicEthTo721WithAdditionalRecipients( + Context memory context + ) external stateless { + test721_1.mint(alice, context.args.tokenId); + + configureOrderComponents( + context.args.zone, + context.args.zoneHash, + context.args.salt, + context.args.useConduit ? conduitKeyOne : bytes32(0) + ); + uint256 counter = context.consideration.getCounter(alice); + baseOrderComponents.counter = counter; + + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + basicOrderParameters.signature = signature; + + uint256 aliceBalanceBefore = alice.balance; + + context.consideration.fulfillBasicOrder{ + value: context.args.paymentAmount.mul(10000000) + }(basicOrderParameters); + + uint256 aliceBalanceAfter = alice.balance; + + assertEq(address(this), test721_1.ownerOf(context.args.tokenId)); + assertEq( + 1 + + (context.args.paymentAmount % 1000) * + basicOrderParameters.additionalRecipients.length, + aliceBalanceAfter - aliceBalanceBefore + ); + } + function basicErc20To721(Context memory context) external stateless { test721_1.mint(alice, context.args.tokenId); - _configureOrderComponents( + configureOrderComponents( context.args.zone, context.args.zoneHash, context.args.salt, - bytes32(0) + context.args.useConduit ? conduitKeyOne : bytes32(0) ); uint256 counter = context.consideration.getCounter(alice); baseOrderComponents.counter = counter; @@ -631,7 +1117,9 @@ contract FulfillBasicOrderTest is BaseOrderTest { basicOrderParameters.endTime = block.timestamp + 100; basicOrderParameters.zoneHash = args.zoneHash; basicOrderParameters.salt = args.salt; - basicOrderParameters.offererConduitKey = bytes32(0); + basicOrderParameters.offererConduitKey = args.useConduit + ? conduitKeyOne + : bytes32(0); basicOrderParameters.fulfillerConduitKey = bytes32(0); basicOrderParameters.totalOriginalAdditionalRecipients = 0; // additional recipients should always be empty @@ -656,7 +1144,9 @@ contract FulfillBasicOrderTest is BaseOrderTest { basicOrderParameters.endTime = block.timestamp + 100; basicOrderParameters.zoneHash = args.zoneHash; basicOrderParameters.salt = args.salt; - basicOrderParameters.offererConduitKey = bytes32(0); + basicOrderParameters.offererConduitKey = args.useConduit + ? conduitKeyOne + : bytes32(0); basicOrderParameters.fulfillerConduitKey = bytes32(0); basicOrderParameters.totalOriginalAdditionalRecipients = 0; // additional recipients should always be empty @@ -682,7 +1172,7 @@ contract FulfillBasicOrderTest is BaseOrderTest { .ERC20_TO_ERC721_FULL_OPEN; } - function _configureOrderComponents( + function configureOrderComponents( address zone, bytes32 zoneHash, uint256 salt, diff --git a/test/foundry/FulfillOrderTest.t.sol b/test/foundry/FulfillOrderTest.t.sol index 3d05a820a..57e4d3ffe 100644 --- a/test/foundry/FulfillOrderTest.t.sol +++ b/test/foundry/FulfillOrderTest.t.sol @@ -1,17 +1,26 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + Order, + OfferItem, + OrderParameters, + ConsiderationItem, + OrderComponents +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OrderType, BasicOrderType, ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -import { AdditionalRecipient } from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { Order, OfferItem, OrderParameters, ConsiderationItem, OrderComponents, BasicOrderParameters } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { TestERC721 } from "../../contracts/test/TestERC721.sol"; -import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; -import { TestERC20 } from "../../contracts/test/TestERC20.sol"; -import { ProxyRegistry } from "./interfaces/ProxyRegistry.sol"; -import { OwnableDelegateProxy } from "./interfaces/OwnableDelegateProxy.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; contract FulfillOrderTest is BaseOrderTest { @@ -45,9 +54,10 @@ contract FulfillOrderTest is BaseOrderTest { uint8 numTips; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } @@ -140,7 +150,7 @@ contract FulfillOrderTest is BaseOrderTest { function noNativeOfferItems(Context memory context) external stateless { configureOrderParameters(alice); uint256 counter = context.consideration.getCounter(alice); - _configureOrderComponents(counter); + configureOrderComponents(counter); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents ); @@ -152,7 +162,7 @@ contract FulfillOrderTest is BaseOrderTest { vm.expectRevert(abi.encodeWithSignature("InvalidNativeOfferItem()")); - context.consideration.fulfillOrder( + context.consideration.fulfillOrder{ value: 1 ether }( Order(baseOrderParameters, signature), bytes32(0) ); @@ -179,10 +189,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function nullAddressSpendReverts(Context memory context) - external - stateless - { + function nullAddressSpendReverts( + Context memory context + ) external stateless { // create a bad signature bytes memory signature = abi.encodePacked( bytes32(0), @@ -198,11 +207,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function testFulfillAscendingDescendingOffer(FuzzInputsCommon memory inputs) - public - validateInputs(inputs) - onlyPayable(inputs.zone) - { + function testFulfillAscendingDescendingOffer( + FuzzInputsCommon memory inputs + ) public validateInputs(inputs) onlyPayable(inputs.zone) { vm.assume(inputs.startAmount > 0 && inputs.endAmount > 0); inputs.warpAmount %= 1000; test( @@ -215,10 +222,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function fulfillAscendingDescendingOffer(Context memory context) - external - stateless - { + function fulfillAscendingDescendingOffer( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -292,10 +298,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function fulfillAscendingDescendingConsideration(Context memory context) - external - stateless - { + function fulfillAscendingDescendingConsideration( + Context memory context + ) external stateless { context.args.warpAmount %= 1000; bytes32 conduitKey = context.args.useConduit ? conduitKeyOne @@ -353,11 +358,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function testFulfillOrderEthToErc721(FuzzInputsCommon memory inputs) - public - validateInputs(inputs) - onlyPayable(inputs.zone) - { + function testFulfillOrderEthToErc721( + FuzzInputsCommon memory inputs + ) public validateInputs(inputs) onlyPayable(inputs.zone) { test( this.fulfillOrderEthToErc721, Context(referenceConsideration, inputs, 0, 0, 0) @@ -627,10 +630,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function fulfillOrder64And65Byte1271Signatures(Context memory context) - external - stateless - { + function fulfillOrder64And65Byte1271Signatures( + Context memory context + ) external stateless { test1155_1.mint(address(this), 1, 1); addErc1155OfferItem(1, 1); addEthConsiderationItem(payable(this), 1); @@ -668,7 +670,7 @@ contract FulfillOrderTest is BaseOrderTest { globalSalt++, false ); - _configureOrderComponents(context.consideration.getCounter(bob)); + configureOrderComponents(context.consideration.getCounter(bob)); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents ); @@ -725,10 +727,9 @@ contract FulfillOrderTest is BaseOrderTest { ); } - function fulfillOrderEthToErc721(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -814,10 +815,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc1155(Context memory context) - external - stateless - { + function fulfillOrderEthToErc1155( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -904,10 +904,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderSingleErc20ToSingleErc1155(Context memory context) - external - stateless - { + function fulfillOrderSingleErc20ToSingleErc1155( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -996,10 +995,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc721WithSingleEthTip(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721WithSingleEthTip( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -1100,10 +1098,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc1155WithSingleEthTip(Context memory context) - external - stateless - { + function fulfillOrderEthToErc1155WithSingleEthTip( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -1206,10 +1203,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc721WithMultipleEthTips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721WithMultipleEthTips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -1317,10 +1313,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc1155WithMultipleEthTips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc1155WithMultipleEthTips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -1431,10 +1426,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc721WithErc721Tips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721WithErc721Tips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -1541,10 +1535,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc1155WithErc721Tips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc1155WithErc721Tips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -1652,10 +1645,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc721WithErc1155Tips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721WithErc1155Tips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -1762,10 +1754,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc1155WithErc1155Tips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc1155WithErc1155Tips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -1871,10 +1862,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc721WithErc20Tips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721WithErc20Tips( + Context memory context + ) external stateless { bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -1978,10 +1968,9 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc1155WithErc20Tips(Context memory context) - external - stateless - { + function fulfillOrderEthToErc1155WithErc20Tips( + Context memory context + ) external stateless { context.numTips = (context.numTips % 64) + 1; bytes32 conduitKey = context.args.useConduit @@ -2087,10 +2076,13 @@ contract FulfillOrderTest is BaseOrderTest { }(Order(orderParameters, signature), conduitKey); } - function fulfillOrderEthToErc721FullRestricted(Context memory context) - external - stateless - { + function fulfillOrderEthToErc721FullRestricted( + Context memory context + ) external stateless { + context.args.zone = address( + uint160(bound(uint160(context.args.zone), 1, type(uint160).max)) + ); + bytes32 conduitKey = context.args.useConduit ? conduitKeyOne : bytes32(0); @@ -2168,14 +2160,17 @@ contract FulfillOrderTest is BaseOrderTest { conduitKey, considerationItems.length ); - vm.prank(alice); - context.consideration.fulfillOrder{ - value: context - .args - .paymentAmts[0] - .add(context.args.paymentAmts[1]) - .add(context.args.paymentAmts[2]) - }(Order(orderParameters, signature), conduitKey); + + uint256 value = context + .args + .paymentAmts[0] + .add(context.args.paymentAmts[1]) + .add(context.args.paymentAmts[2]); + hoax(context.args.zone, value); + context.consideration.fulfillOrder{ value: value }( + Order(orderParameters, signature), + conduitKey + ); } function testFulfillOrderRevertUnusedItemParametersAddressSetOnNativeConsideration( @@ -2541,4 +2536,70 @@ contract FulfillOrderTest is BaseOrderTest { bytes32(0) ); } + + function testFulfillOrderRevertCounterIncremented() public { + test( + this.fulfillOrderRevertCounterIncremented, + Context(referenceConsideration, empty, 0, 0, 0) + ); + test( + this.fulfillOrderRevertCounterIncremented, + Context(consideration, empty, 0, 0, 0) + ); + } + + function fulfillOrderRevertCounterIncremented( + Context memory context + ) external stateless { + test1155_1.mint(bob, 1, 1); + addErc1155OfferItem(1, 1); + addEthConsiderationItem(payable(bob), 1); + + _configureOrderParameters( + bob, + address(0), + bytes32(0), + globalSalt++, + false + ); + configureOrderComponents(context.consideration.getCounter(bob)); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder2098( + context.consideration, + bobPk, + orderHash + ); + + Order memory order = Order(baseOrderParameters, signature); + + _validateOrder(order, context.consideration); + + vm.prank(bob); + context.consideration.incrementCounter(); + + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + _validateOrder(order, context.consideration); + + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + context.consideration.fulfillOrder{ value: 1 }(order, bytes32(0)); + + configureOrderComponents(context.consideration.getCounter(bob)); + orderHash = context.consideration.getOrderHash(baseOrderComponents); + signature = signOrder(context.consideration, bobPk, orderHash); + + order = Order(baseOrderParameters, signature); + + _validateOrder(order, context.consideration); + + vm.prank(bob); + context.consideration.incrementCounter(); + + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + _validateOrder(order, context.consideration); + + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + context.consideration.fulfillOrder{ value: 1 }(order, bytes32(0)); + } } diff --git a/test/foundry/FullfillAvailableOrder.t.sol b/test/foundry/FullfillAvailableOrder.t.sol index 8b8f67127..729f97ca7 100644 --- a/test/foundry/FullfillAvailableOrder.t.sol +++ b/test/foundry/FullfillAvailableOrder.t.sol @@ -1,15 +1,29 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + Order, + OfferItem, + OrderParameters, + ConsiderationItem, + OrderComponents, + FulfillmentComponent +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OrderType, BasicOrderType, ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { Order, OfferItem, OrderParameters, ConsiderationItem, OrderComponents, BasicOrderParameters, FulfillmentComponent } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { TestERC721 } from "../../contracts/test/TestERC721.sol"; -import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; -import { TestERC20 } from "../../contracts/test/TestERC20.sol"; + import { stdError } from "forge-std/Test.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; contract FulfillAvailableOrder is BaseOrderTest { @@ -44,22 +58,23 @@ contract FulfillAvailableOrder is BaseOrderTest { vm.assume( inputs.paymentAmts[0].add(inputs.paymentAmts[1]).add( inputs.paymentAmts[2] - ) <= 2**128 - 1 + ) <= 2 ** 128 - 1 ); _; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } } - function testNoNativeOffersFulfillAvailable(uint8[8] memory itemTypes) - public - { + function testNoNativeOffersFulfillAvailable( + uint8[8] memory itemTypes + ) public { uint256 tokenId; for (uint256 i; i < 8; i++) { ItemType itemType = ItemType(itemTypes[i] % 4); @@ -92,13 +107,12 @@ contract FulfillAvailableOrder is BaseOrderTest { ); } - function noNativeOfferItemsFulfillAvailable(Context memory context) - external - stateless - { + function noNativeOfferItemsFulfillAvailable( + Context memory context + ) external stateless { configureOrderParameters(alice); uint256 counter = context.consideration.getCounter(alice); - _configureOrderComponents(counter); + configureOrderComponents(counter); bytes32 orderHash = context.consideration.getOrderHash( baseOrderComponents ); @@ -123,7 +137,7 @@ contract FulfillAvailableOrder is BaseOrderTest { addEthConsiderationItem(alice, 1); configureOrderParameters(alice); counter = context.consideration.getCounter(alice); - _configureOrderComponents(counter); + configureOrderComponents(counter); bytes32 orderHash2 = context.consideration.getOrderHash( baseOrderComponents ); @@ -222,10 +236,9 @@ contract FulfillAvailableOrder is BaseOrderTest { ); } - function fulfillAvailableOrdersOverflowOfferSide(Context memory context) - external - stateless - { + function fulfillAvailableOrdersOverflowOfferSide( + Context memory context + ) external stateless { // mint consideration nfts to the test contract test721_1.mint(address(this), 1); test721_1.mint(address(this), 2); diff --git a/test/foundry/GetterTests.t.sol b/test/foundry/GetterTests.t.sol index 079323047..7a1effc00 100644 --- a/test/foundry/GetterTests.t.sol +++ b/test/foundry/GetterTests.t.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; import { BaseConsiderationTest } from "./utils/BaseConsiderationTest.sol"; @@ -36,12 +36,12 @@ contract TestGetters is BaseConsiderationTest { // Length of "Consideration" assertEq(length, 13); // Check if there are dirty bits - assertEq(value, bytes32("Consideration")); + assertEq(value, bytes32(abi.encodePacked(name))); } function testGetsCorrectVersion() public { (string memory version, , ) = consideration.information(); - assertEq(version, "1.1"); + assertEq(version, "1.2"); } function testGetCorrectDomainSeparator() public { @@ -70,7 +70,7 @@ contract TestGetters is BaseConsiderationTest { ); } - function testGetCorrectDomainSeparator(uint256 _chainId) public { + function testGetCorrectDomainSeparator(uint64 _chainId) public { // ignore case where _chainId is the same as block.chainid vm.assume(_chainId != block.chainid); bytes memory typeName = abi.encodePacked( diff --git a/test/foundry/MatchAdvancedOrder.t.sol b/test/foundry/MatchAdvancedOrder.t.sol index 006f3dab3..b0576a585 100644 --- a/test/foundry/MatchAdvancedOrder.t.sol +++ b/test/foundry/MatchAdvancedOrder.t.sol @@ -1,13 +1,30 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + AdvancedOrder, + OfferItem, + OrderParameters, + ConsiderationItem, + OrderComponents, + CriteriaResolver, + FulfillmentComponent +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OrderType, ItemType } from "../../contracts/lib/ConsiderationEnums.sol"; -import { Order } from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { AdvancedOrder, OfferItem, OrderParameters, ConsiderationItem, OrderComponents, CriteriaResolver, FulfillmentComponent } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + import { stdError } from "forge-std/Test.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; contract MatchAdvancedOrder is BaseOrderTest { @@ -47,9 +64,10 @@ contract MatchAdvancedOrder is BaseOrderTest { FuzzInputsAscendingDescending args; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } @@ -144,10 +162,9 @@ contract MatchAdvancedOrder is BaseOrderTest { ); } - function matchAdvancedOrdersOverflowOrderSide(Context memory context) - external - stateless - { + function matchAdvancedOrdersOverflowOrderSide( + Context memory context + ) external stateless { addOfferItem(context.itemType, 1, 100); addErc721ConsiderationItem(alice, 1); @@ -178,7 +195,7 @@ contract MatchAdvancedOrder is BaseOrderTest { delete offerItems; delete considerationItems; - addOfferItem(context.itemType, 1, 2**256 - 1); + addOfferItem(context.itemType, 1, 2 ** 256 - 1); addErc721ConsiderationItem(alice, 2); OrderParameters memory secondOrderParameters = OrderParameters( @@ -304,7 +321,8 @@ contract MatchAdvancedOrder is BaseOrderTest { context.consideration.matchAdvancedOrders{ value: 99 }( advancedOrders, new CriteriaResolver[](0), - fulfillments + fulfillments, + address(0) ); } @@ -344,7 +362,7 @@ contract MatchAdvancedOrder is BaseOrderTest { test721_1.mint(bob, 2); addErc721OfferItem(2); - addConsiderationItem(alice, context.itemType, 1, 2**256 - 1); + addConsiderationItem(alice, context.itemType, 1, 2 ** 256 - 1); OrderParameters memory secondOrderParameters = OrderParameters( address(bob), @@ -467,7 +485,8 @@ contract MatchAdvancedOrder is BaseOrderTest { context.consideration.matchAdvancedOrders{ value: 99 }( advancedOrders, new CriteriaResolver[](0), - fulfillments + fulfillments, + address(0) ); } @@ -604,7 +623,8 @@ contract MatchAdvancedOrder is BaseOrderTest { context.consideration.matchAdvancedOrders{ value: context.args.amount }( advancedOrders, new CriteriaResolver[](0), // no criteria resolvers - fulfillments + fulfillments, + address(0) ); } @@ -752,7 +772,8 @@ contract MatchAdvancedOrder is BaseOrderTest { context.consideration.matchAdvancedOrders( orders, new CriteriaResolver[](0), - fulfillments + fulfillments, + address(0) ); uint256 balanceAfterOrder = token1.balanceOf(bob); // check the difference in alice's balance is equal to partial fill of current amount @@ -911,7 +932,8 @@ contract MatchAdvancedOrder is BaseOrderTest { context.consideration.matchAdvancedOrders( orders, new CriteriaResolver[](0), - fulfillments + fulfillments, + address(0) ); uint256 balanceAfterOrder = token1.balanceOf(alice); // check the difference in alice's balance is equal to partial fill of current amount diff --git a/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol b/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol new file mode 100644 index 000000000..1e4c56bd7 --- /dev/null +++ b/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.17; + +import { ItemType } from "../../contracts/lib/ConsiderationEnums.sol"; + +import { Order } from "../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + AdvancedOrder, + OfferItem, + ConsiderationItem, + CriteriaResolver, + Fulfillment +} from "../../contracts/lib/ConsiderationStructs.sol"; + +import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + +import { Vm } from "forge-std/Vm.sol"; + +contract MatchOrderUnspentOfferTest is BaseOrderTest { + struct Context { + ConsiderationInterface seaport; + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) { + fail( + "Stateless test function should have reverted with assertion failure status." + ); + } catch (bytes memory reason) { + assertPass(reason); + } + } + + /** + * @dev test that specifying the offerer as the recipient of the considerationItem results in + * execution filtering for items not specified in the matched order(s) + * ie: offer nft1, nft2 for erc20 + * fulfiller matches to erc20 offer, nft1 consideration + * specifies original offerer as recipient of unspent considerations + * fulfilling does not result in nft2 being transferred at all + */ + function testFilterOfferItemBySpecifyingOffererAsRecipient() public { + test( + this.execFilterOfferItemBySpecifyingOffererAsRecipient, + Context({ seaport: consideration }) + ); + test( + this.execFilterOfferItemBySpecifyingOffererAsRecipient, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpFilterOfferItemBySpecifyingOffererAsRecipient( + Context memory context + ) internal returns (AdvancedOrder[] memory, Fulfillment[] memory) { + string memory offererName = "offerer"; + address offerer = makeAddr(offererName); + string memory fulfillerName = "fulfiller"; + address fulfiller = makeAddr(fulfillerName); + + // allocate tokens and approvals + test721_1.mint(offerer, 1); + test721_2.mint(offerer, 1); + token1.mint(fulfiller, 10000); + vm.startPrank(offerer); + test721_1.setApprovalForAll(address(context.seaport), true); + test721_2.setApprovalForAll(address(context.seaport), true); + vm.stopPrank(); + vm.prank(fulfiller); + token1.approve(address(context.seaport), type(uint256).max); + + // configure orders + addOfferItem( + OfferItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifierOrCriteria: 1, + startAmount: 1, + endAmount: 1 + }) + ); + // add another offer item, this time using test721_2 + addOfferItem( + OfferItem({ + itemType: ItemType.ERC721, + token: address(test721_2), + identifierOrCriteria: 1, + startAmount: 1, + endAmount: 1 + }) + ); + + // add a considerationItem of 10k of token1 with offerer as the payable recipient + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 10000, + endAmount: 10000, + recipient: payable(offerer) + }) + ); + Order memory offererOrder = createSignedOrder( + context.seaport, + offererName + ); + + // offer 10k of token1 + + addOfferItem( + OfferItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 10000, + endAmount: 10000 + }) + ); + // add consideration item for test721_1 id 1 with fulfiller as recipient + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifierOrCriteria: 1, + startAmount: 1, + endAmount: 1, + recipient: payable(fulfiller) + }) + ); + // add consideration item for test721_2 id 1 with offerer as recipient, which we will try to filter out + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC721, + token: address(test721_2), + identifierOrCriteria: 1, + startAmount: 1, + endAmount: 1, + recipient: payable(offerer) + }) + ); + Order memory fulfillerOrder = createSignedOrder( + context.seaport, + fulfillerName + ); + + Fulfillment[] memory _fulfillments = createFulfillmentsFromMirrorOrders( + offererOrder.parameters, + fulfillerOrder.parameters + ); + + AdvancedOrder memory offererAdvanced = toAdvancedOrder(offererOrder); + AdvancedOrder memory fulfillerAdvanced = toAdvancedOrder( + fulfillerOrder + ); + + AdvancedOrder[] memory orders = new AdvancedOrder[](2); + orders[0] = offererAdvanced; + orders[1] = fulfillerAdvanced; + + return (orders, _fulfillments); + } + + function execFilterOfferItemBySpecifyingOffererAsRecipient( + Context memory context + ) external stateless { + ( + AdvancedOrder[] memory orders, + Fulfillment[] memory _fulfillments + ) = setUpFilterOfferItemBySpecifyingOffererAsRecipient(context); + vm.recordLogs(); + context.seaport.matchAdvancedOrders({ + orders: orders, + criteriaResolvers: new CriteriaResolver[](0), + fulfillments: _fulfillments, + recipient: makeAddr("offerer") + }); + Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); + // ensure that token2 was not transferred at any point + assertEq(recordedLogs.length, 5); + // first two are OrderFulfilled events + assertEq(recordedLogs[0].emitter, address(context.seaport)); + assertEq(recordedLogs[1].emitter, address(context.seaport)); + // next is OrdersMatched event + assertEq(recordedLogs[2].emitter, address(context.seaport)); + // next is 721_1 transfer + assertEq(recordedLogs[3].emitter, address(test721_1)); + // last is ERC20 transfer + assertEq(recordedLogs[4].emitter, address(token1)); + } + + function testSweepRemaining() public { + test(this.execSweepRemaining, Context({ seaport: consideration })); + test( + this.execSweepRemaining, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpSweepRemaining( + Context memory context + ) internal returns (Order[] memory, Fulfillment[] memory) { + string memory offererName = "offerer"; + address offerer = makeAddr(offererName); + string memory fulfillerName = "fulfiller"; + address fulfiller = makeAddr(fulfillerName); + + // allocate tokens and approvals + test721_1.mint(fulfiller, 1); + token1.mint(offerer, 10000); + vm.prank(fulfiller); + test721_1.setApprovalForAll(address(context.seaport), true); + vm.stopPrank(); + vm.prank(offerer); + token1.approve(address(context.seaport), type(uint256).max); + + // configure orders + addOfferItem( + OfferItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 1000, + endAmount: 1000 + }) + ); + // add another offer item, this time using test721_2 + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifierOrCriteria: 1, + startAmount: 1, + endAmount: 1, + recipient: payable(offerer) + }) + ); + + Order memory offererOrder = createSignedOrder( + context.seaport, + offererName + ); + + // offer 10k of token1 + + addOfferItem( + OfferItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifierOrCriteria: 1, + startAmount: 1, + endAmount: 1 + }) + ); + + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 800, + endAmount: 800, + recipient: payable(fulfiller) + }) + ); + + Order memory fulfillerOrder = createSignedOrder( + context.seaport, + fulfillerName + ); + + Fulfillment[] memory _fulfillments = createFulfillmentsFromMirrorOrders( + offererOrder.parameters, + fulfillerOrder.parameters + ); + + Order[] memory orders = new Order[](2); + orders[0] = offererOrder; + orders[1] = fulfillerOrder; + return (orders, _fulfillments); + } + + /** + * @dev test that unmatched item amounts are swept to the fulfiller when calling matchOrders + */ + function execSweepRemaining(Context memory context) external stateless { + ( + Order[] memory orders, + Fulfillment[] memory _fulfillments + ) = setUpSweepRemaining(context); + uint256 startingToken1Balance = token1.balanceOf(address(this)); + context.seaport.matchOrders(orders, _fulfillments); + uint256 endingToken1Balance = token1.balanceOf(address(this)); + assertEq(endingToken1Balance, startingToken1Balance + 200); + } + + function testSweepRemainingAdvanced() public { + test( + this.execSweepRemainingAdvanced, + Context({ seaport: consideration }) + ); + test( + this.execSweepRemainingAdvanced, + Context({ seaport: referenceConsideration }) + ); + } + + /** + * @dev test that unmatched item amounts are swept to the fulfiller when calling matchAdvancedOrders with a recipient of 0 + */ + function execSweepRemainingAdvanced( + Context memory context + ) external stateless { + ( + Order[] memory orders, + Fulfillment[] memory _fulfillments + ) = setUpSweepRemaining(context); + AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2); + advancedOrders[0] = toAdvancedOrder(orders[0]); + advancedOrders[1] = toAdvancedOrder(orders[1]); + + uint256 startingToken1Balance = token1.balanceOf(address(this)); + context.seaport.matchAdvancedOrders({ + orders: advancedOrders, + criteriaResolvers: new CriteriaResolver[](0), + fulfillments: _fulfillments, + recipient: address(0) + }); + uint256 endingToken1Balance = token1.balanceOf(address(this)); + assertEq(endingToken1Balance, startingToken1Balance + 200); + } +} diff --git a/test/foundry/MatchOrders.t.sol b/test/foundry/MatchOrders.t.sol index 39dec3dfc..dd46422a2 100644 --- a/test/foundry/MatchOrders.t.sol +++ b/test/foundry/MatchOrders.t.sol @@ -1,16 +1,27 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + ItemType +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + Order, + OrderParameters, + OrderComponents, + FulfillmentComponent +} from "../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { OrderType, ItemType } from "../../contracts/lib/ConsiderationEnums.sol"; -import { Order, Fulfillment, OfferItem, OrderParameters, ConsiderationItem, OrderComponents, FulfillmentComponent } from "../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { ConsiderationEventsAndErrors } from "../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { TestERC721 } from "../../contracts/test/TestERC721.sol"; -import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; -import { TestERC20 } from "../../contracts/test/TestERC20.sol"; + import { ArithmeticUtil } from "./utils/ArithmeticUtil.sol"; + import { stdError } from "forge-std/Test.sol"; contract MatchOrders is BaseOrderTest { @@ -54,7 +65,7 @@ contract MatchOrders is BaseOrderTest { uint256(context.args.paymentAmts[0]) + uint256(context.args.paymentAmts[1]) + uint256(context.args.paymentAmts[2]) <= - 2**128 - 1 + 2 ** 128 - 1 ); _; } @@ -63,14 +74,15 @@ contract MatchOrders is BaseOrderTest { ContextAscendingDescending memory context ) { vm.assume(context.args.amount > 100); - vm.assume(uint256(context.args.amount) * 2 <= 2**128 - 1); + vm.assume(uint256(context.args.amount) * 2 <= 2 ** 128 - 1); vm.assume(context.args.warp > 10 && context.args.warp < 1000); _; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } @@ -107,7 +119,7 @@ contract MatchOrders is BaseOrderTest { inputs.salt, inputs.useConduit ); - _configureOrderComponents(consideration.getCounter(alice)); + configureOrderComponents(consideration.getCounter(alice)); test( this.matchOrdersSingleErc721OfferSingleEthConsideration, Context(consideration, inputs) @@ -118,10 +130,9 @@ contract MatchOrders is BaseOrderTest { ); } - function testMatchOrdersOverflowOfferSide(FuzzInputsCommon memory inputs) - public - validateInputs(Context(consideration, inputs)) - { + function testMatchOrdersOverflowOfferSide( + FuzzInputsCommon memory inputs + ) public validateInputs(Context(consideration, inputs)) { for (uint256 i = 1; i < 4; ++i) { if (i == 2) { continue; @@ -182,7 +193,7 @@ contract MatchOrders is BaseOrderTest { inputs.salt, inputs.useConduit ); - _configureOrderComponents(consideration.getCounter(alice)); + configureOrderComponents(consideration.getCounter(alice)); testAscendingDescending( this.matchOrdersAscendingOfferAmount, ContextAscendingDescending(referenceConsideration, inputs) @@ -211,7 +222,7 @@ contract MatchOrders is BaseOrderTest { inputs.salt, inputs.useConduit ); - _configureOrderComponents(consideration.getCounter(alice)); + configureOrderComponents(consideration.getCounter(alice)); testAscendingDescending( this.matchOrdersAscendingConsiderationAmount, ContextAscendingDescending(referenceConsideration, inputs) @@ -240,7 +251,7 @@ contract MatchOrders is BaseOrderTest { inputs.salt, inputs.useConduit ); - _configureOrderComponents(consideration.getCounter(alice)); + configureOrderComponents(consideration.getCounter(alice)); testAscendingDescending( this.matchOrdersDescendingOfferAmount, ContextAscendingDescending(referenceConsideration, inputs) @@ -269,7 +280,7 @@ contract MatchOrders is BaseOrderTest { inputs.salt, inputs.useConduit ); - _configureOrderComponents(consideration.getCounter(alice)); + configureOrderComponents(consideration.getCounter(alice)); testAscendingDescending( this.matchOrdersDescendingConsiderationAmount, ContextAscendingDescending(referenceConsideration, inputs) @@ -297,7 +308,7 @@ contract MatchOrders is BaseOrderTest { context.args.salt, context.args.useConduit ); - _configureOrderComponents(consideration.getCounter(bob)); + configureOrderComponents(consideration.getCounter(bob)); bytes memory baseSignature = signOrder( context.consideration, bobPk, @@ -307,7 +318,7 @@ contract MatchOrders is BaseOrderTest { delete offerItems; delete considerationItems; - addOfferItem(itemType, 1, 2**256 - 1); + addOfferItem(itemType, 1, 2 ** 256 - 1); addErc721ConsiderationItem(alice, 2); OrderParameters memory secondOrderParameters = OrderParameters( @@ -456,7 +467,7 @@ contract MatchOrders is BaseOrderTest { test721_1.mint(bob, 2); addErc721OfferItem(2); - addConsiderationItem(alice, itemType, 1, 2**256 - 1); + addConsiderationItem(alice, itemType, 1, 2 ** 256 - 1); OrderParameters memory secondOrderParameters = OrderParameters( address(bob), diff --git a/test/foundry/NonMatchSelectorTest.t.sol b/test/foundry/NonMatchSelectorTest.t.sol new file mode 100644 index 000000000..95d90982f --- /dev/null +++ b/test/foundry/NonMatchSelectorTest.t.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +//Author: Saw-mon and Natalie + +pragma solidity ^0.8.17; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + NonMatchSelector_MagicMask, + NonMatchSelector_InvalidErrorValue +} from "../../contracts/lib/ConsiderationConstants.sol"; + +import { Test } from "forge-std/Test.sol"; + +contract NonMatchSelectorTest is Test { + function testNonMatchSelectorMagicMaskAndInvalidErrorValue() public { + assertEq( + NonMatchSelector_MagicMask + 1, + NonMatchSelector_InvalidErrorValue + ); + } + + function testSelectorMatchOrders() public { + _testSelector(ConsiderationInterface.matchOrders.selector, false); + } + + function testSelectorMatchAdvancedOrders() public { + _testSelector( + ConsiderationInterface.matchAdvancedOrders.selector, + false + ); + } + + function testSelectorFulfillAvailableOrders() public { + _testSelector( + ConsiderationInterface.fulfillAvailableOrders.selector, + true + ); + } + + function testSelectorFulfillAvailableAdvancedOrders() public { + _testSelector( + ConsiderationInterface.fulfillAvailableAdvancedOrders.selector, + true + ); + } + + function _testSelector(bytes4 selector, bool shouldBeSelected) internal { + bool isSelected; + + assembly { + isSelected := eq( + NonMatchSelector_MagicMask, + and(NonMatchSelector_MagicMask, selector) + ) + } + + assertEq(isSelected, shouldBeSelected); + } +} diff --git a/test/foundry/NonReentrant.t.sol b/test/foundry/NonReentrant.t.sol index f23f60da3..67013aff7 100644 --- a/test/foundry/NonReentrant.t.sol +++ b/test/foundry/NonReentrant.t.sol @@ -1,12 +1,38 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + OrderType, + BasicOrderType, + ItemType, + Side +} from "../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + AdditionalRecipient, + Fulfillment, + OfferItem, + ConsiderationItem, + FulfillmentComponent, + OrderComponents, + OrderParameters, + AdvancedOrder, + BasicOrderParameters, + Order +} from "../../contracts/lib/ConsiderationStructs.sol"; -import { OrderType, BasicOrderType, ItemType, Side } from "../../contracts/lib/ConsiderationEnums.sol"; -import { ConsiderationInterface } from "../../contracts/interfaces/ConsiderationInterface.sol"; -import { AdditionalRecipient, Fulfillment, OfferItem, ConsiderationItem, FulfillmentComponent, OrderComponents, AdvancedOrder, BasicOrderParameters, Order } from "../../contracts/lib/ConsiderationStructs.sol"; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { EntryPoint, ReentryPoint } from "./utils/reentrancy/ReentrantEnums.sol"; -import { OrderParameters, CriteriaResolver } from "./utils/reentrancy/ReentrantStructs.sol"; + +import { + EntryPoint, + ReentryPoint +} from "./utils/reentrancy/ReentrantEnums.sol"; + +import { CriteriaResolver } from "./utils/reentrancy/ReentrantStructs.sol"; contract NonReentrantTest is BaseOrderTest { BasicOrderParameters basicOrderParameters; @@ -44,26 +70,27 @@ contract NonReentrantTest is BaseOrderTest { event BytesReason(bytes data); - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } } function testNonReentrant() public { - for (uint256 i; i < 7; ++i) { - for (uint256 j; j < 10; ++j) { + for (uint256 i; i < 8; ++i) { + for (uint256 j; j < 11; ++j) { NonReentrantInputs memory inputs = NonReentrantInputs( EntryPoint(i), ReentryPoint(j) ); + test(this.nonReentrant, Context(consideration, inputs)); test( this.nonReentrant, Context(referenceConsideration, inputs) ); - test(this.nonReentrant, Context(consideration, inputs)); } } } @@ -99,6 +126,24 @@ contract NonReentrantTest is BaseOrderTest { } currentConsideration.fulfillBasicOrder(_basicOrderParameters); shouldReenter = false; + } else if (entryPoint == EntryPoint.FulfillBasicOrderEfficient) { + BasicOrderParameters + memory _basicOrderParameters = prepareBasicOrder(tokenId); + if (!reentering) { + shouldReenter = true; + vm.expectEmit( + true, + false, + false, + false, + address(address(this)) + ); + emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()")); + } + currentConsideration.fulfillBasicOrder_efficient_6GL6yc( + _basicOrderParameters + ); + shouldReenter = false; } else if (entryPoint == EntryPoint.FulfillOrder) { ( Order memory params, @@ -199,7 +244,8 @@ contract NonReentrantTest is BaseOrderTest { currentConsideration.matchAdvancedOrders{ value: 1 }( _orders, criteriaResolvers, - _fulfillments + _fulfillments, + address(0) ); } } @@ -220,10 +266,9 @@ contract NonReentrantTest is BaseOrderTest { } } - function prepareBasicOrder(uint256 tokenId) - internal - returns (BasicOrderParameters memory _basicOrderParameters) - { + function prepareBasicOrder( + uint256 tokenId + ) internal returns (BasicOrderParameters memory _basicOrderParameters) { test1155_1.mint(address(this), tokenId, 2); offerItems.push( @@ -275,7 +320,9 @@ contract NonReentrantTest is BaseOrderTest { ); } - function prepareOrder(uint256 tokenId) + function prepareOrder( + uint256 tokenId + ) internal returns ( Order memory _order, @@ -312,7 +359,9 @@ contract NonReentrantTest is BaseOrderTest { fulfillerConduitKey = bytes32(0); } - function prepareAdvancedOrder(uint256 tokenId) + function prepareAdvancedOrder( + uint256 tokenId + ) internal returns ( AdvancedOrder memory _order, @@ -351,7 +400,9 @@ contract NonReentrantTest is BaseOrderTest { fulfillerConduitKey = bytes32(0); } - function prepareAvailableOrders(uint256 tokenId) + function prepareAvailableOrders( + uint256 tokenId + ) internal returns ( Order[] memory _orders, @@ -393,7 +444,9 @@ contract NonReentrantTest is BaseOrderTest { _orders[0] = Order(_orderParameters, signature); } - function prepareFulfillAvailableAdvancedOrders(uint256 tokenId) + function prepareFulfillAvailableAdvancedOrders( + uint256 tokenId + ) internal returns ( AdvancedOrder[] memory advancedOrders, @@ -423,10 +476,9 @@ contract NonReentrantTest is BaseOrderTest { ); } - function prepareMatchOrders(uint256 tokenId) - internal - returns (Order[] memory, Fulfillment[] memory) - { + function prepareMatchOrders( + uint256 tokenId + ) internal returns (Order[] memory, Fulfillment[] memory) { test721_1.mint(address(this), tokenId); addErc721OfferItem(tokenId); addEthConsiderationItem(payable(address(this)), 1); @@ -527,15 +579,15 @@ contract NonReentrantTest is BaseOrderTest { return (orders, fulfillments); } - function _convertOrderToAdvanced(Order memory _order) - internal - pure - returns (AdvancedOrder memory) - { + function _convertOrderToAdvanced( + Order memory _order + ) internal pure returns (AdvancedOrder memory) { return AdvancedOrder(_order.parameters, 1, 1, _order.signature, ""); } - function prepareMatchAdvancedOrders(uint256 tokenId) + function prepareMatchAdvancedOrders( + uint256 tokenId + ) internal returns ( AdvancedOrder[] memory _orders, @@ -553,7 +605,7 @@ contract NonReentrantTest is BaseOrderTest { } function _doReenter() internal { - if (uint256(reentryPoint) < 7) { + if (uint256(reentryPoint) < uint256(ReentryPoint.Cancel)) { try this._entryPoint(EntryPoint(uint256(reentryPoint)), 10, true) {} catch (bytes memory reason) { diff --git a/test/foundry/SignatureVerification.t.sol b/test/foundry/SignatureVerification.t.sol new file mode 100644 index 000000000..46f39af6f --- /dev/null +++ b/test/foundry/SignatureVerification.t.sol @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { + SignatureVerification +} from "../../contracts/lib/SignatureVerification.sol"; + +import { + ReferenceSignatureVerification +} from "../../reference/lib/ReferenceSignatureVerification.sol"; + +import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + +contract SignatureVerifierLogic is BaseOrderTest, SignatureVerification { + bytes signature; + bytes signature1271; + bytes32 digest; + + function signatureVerificationDirtyScratchSpace() external { + digest = bytes32(uint256(69420)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); + signature = abi.encodePacked(r, s, v); + + // store bob's address in scratch space + assembly { + mstore(0x0, sload(bob.slot)) + } + + _assertValidSignature( + alice, + digest, + digest, + signature.length, + signature + ); + } + + function signatureVerification65ByteWithBadSignatureV() external { + digest = bytes32(uint256(69420)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); + v = 0; + signature = abi.encodePacked(r, s, v); + assertEq(signature.length, 65); + + _assertValidSignature( + alice, + digest, + digest, + signature.length, + signature + ); + } + + function signatureVerification65ByteJunkWithAcceptableSignatureV() + external + { + digest = bytes32(uint256(69420)); + // Note that Bob is signing but we're passing in Alice's address below. + (uint8 v, bytes32 r, bytes32 s) = vm.sign(bobPk, digest); + signature = abi.encodePacked(r, s, v); + assertEq(signature.length, 65); + + _assertValidSignature( + alice, + digest, + digest, + signature.length, + signature + ); + } + + function signatureVerification64ByteJunk() external { + digest = bytes32(uint256(69420)); + // Note that Bob is signing but we're passing in Alice's address below. + (, bytes32 r, bytes32 s) = vm.sign(bobPk, digest); + signature = abi.encodePacked(r, s); + assertEq(signature.length, 64); + + _assertValidSignature( + alice, + digest, + digest, + signature.length, + signature + ); + } + + function signatureVerificationTooLong() external { + digest = bytes32(uint256(69420)); + signature = new bytes(69); + + _assertValidSignature( + alice, + digest, + digest, + signature.length, + signature + ); + } + + function signatureVerificationValid() external { + digest = bytes32(uint256(69420)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); + signature = abi.encodePacked(r, s, v); + + _assertValidSignature( + alice, + digest, + digest, + signature.length, + signature + ); + } + + function signatureVerification1271Valid() external { + digest = bytes32(uint256(69420)); + // This is valid because we hardcoded the `isValidSignature` magic value + // response in the BaseOrderTest. + signature1271 = abi.encodePacked(bytes32(0), bytes32(0)); + + _assertValidSignature( + // A contract address is the signer. + address(this), + digest, + digest, + signature1271.length, + signature1271 + ); + } +} + +contract SignatureVerifierLogicWith1271Override is + BaseOrderTest, + SignatureVerification +{ + bytes signature1271; + bytes32 digest; + + ///@dev This overrides the hardcoded `isValidSignature` magic value response + /// in the BaseOrderTest. + function isValidSignature( + bytes32, + bytes memory + ) external pure override returns (bytes4) { + return 0xDEAFBEEF; + } + + function signatureVerification1271Invalid() external { + digest = bytes32(uint256(69420)); + signature1271 = abi.encodePacked(bytes32(0), bytes32(0)); + + _assertValidSignature( + // A contract address is the signer. + address(this), + digest, + digest, + signature1271.length, + signature1271 + ); + } +} + +contract SignatureVerifierLogicWith1271Fail is + BaseOrderTest, + SignatureVerification +{ + bytes signature1271; + bytes32 digest; + + ///@dev This overrides the hardcoded `isValidSignature` magic value response + /// in the BaseOrderTest. + function isValidSignature( + bytes32, + bytes memory + ) external pure override returns (bytes4) { + revert(); + } + + function signatureVerification1271Fail() external { + digest = bytes32(uint256(69420)); + signature1271 = abi.encodePacked(bytes32(0), bytes32(0)); + + _assertValidSignature( + // A contract address is the signer. + address(this), + digest, + digest, + signature1271.length, + signature1271 + ); + } +} + +contract ReferenceSignatureVerifierLogic is + BaseOrderTest, + ReferenceSignatureVerification +{ + bytes signature; + bytes signature1271; + bytes32 digest; + + function referenceSignatureVerificationDirtyScratchSpace() external { + digest = bytes32(uint256(69420)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); + signature = abi.encodePacked(r, s, v); + + // store bob's address in scratch space + assembly { + mstore(0x0, sload(bob.slot)) + } + + _assertValidSignature(alice, digest, digest, signature, signature); + } + + function referenceSignatureVerification65ByteWithBadSignatureV() external { + digest = bytes32(uint256(69420)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); + v = 0; + signature = abi.encodePacked(r, s, v); + assertEq(signature.length, 65); + + _assertValidSignature(alice, digest, digest, signature, signature); + } + + function referenceSignatureVerification65ByteJunkWithAcceptableSignatureV() + external + { + digest = bytes32(uint256(69420)); + // Note that Bob is signing but we're passing in Alice's address below. + (uint8 v, bytes32 r, bytes32 s) = vm.sign(bobPk, digest); + signature = abi.encodePacked(r, s, v); + assertEq(signature.length, 65); + + _assertValidSignature(alice, digest, digest, signature, signature); + } + + function referenceSignatureVerification64ByteJunk() external { + digest = bytes32(uint256(69420)); + // Note that Bob is signing but we're passing in Alice's address below. + (, bytes32 r, bytes32 s) = vm.sign(bobPk, digest); + signature = abi.encodePacked(r, s); + assertEq(signature.length, 64); + + _assertValidSignature(alice, digest, digest, signature, signature); + } + + function referenceSignatureVerificationTooLong() external { + digest = bytes32(uint256(69420)); + signature = new bytes(69); + + _assertValidSignature(alice, digest, digest, signature, signature); + } + + function referenceSignatureVerificationValid() external { + digest = bytes32(uint256(69420)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); + signature = abi.encodePacked(r, s, v); + + _assertValidSignature(alice, digest, digest, signature, signature); + } + + function referenceSignatureVerification1271Valid() external { + digest = bytes32(uint256(69420)); + signature1271 = abi.encodePacked(bytes32(0), bytes32(0)); + + _assertValidSignature( + // A contract address is the signer. + address(this), + digest, + digest, + signature1271, + signature1271 + ); + } +} + +contract ReferenceSignatureVerifierLogicWith1271Override is + BaseOrderTest, + ReferenceSignatureVerification +{ + bytes signature1271; + bytes32 digest; + + ///@dev This overrides the hardcoded `isValidSignature` magic value response + /// in the BaseOrderTest. + function isValidSignature( + bytes32, + bytes memory + ) external pure override returns (bytes4) { + return 0xDEAFBEEF; + } + + function referenceSignatureVerification1271Invalid() external { + digest = bytes32(uint256(69420)); + signature1271 = abi.encodePacked(bytes32(0), bytes32(0)); + + _assertValidSignature( + // A contract address is the signer. + address(this), + digest, + digest, + signature1271, + signature1271 + ); + } +} + +contract SignatureVerificationTest is BaseOrderTest { + function test(function() external fn) internal { + try fn() {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testSignatureVerification() public { + SignatureVerifierLogic logic = new SignatureVerifierLogic(); + logic.signatureVerificationDirtyScratchSpace(); + vm.expectRevert(abi.encodeWithSignature("BadSignatureV(uint8)", 0)); + logic.signatureVerification65ByteWithBadSignatureV(); + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + logic.signatureVerification65ByteJunkWithAcceptableSignatureV(); + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + logic.signatureVerification64ByteJunk(); + vm.expectRevert(abi.encodeWithSignature("InvalidSignature()")); + logic.signatureVerificationTooLong(); + logic.signatureVerificationValid(); + logic.signatureVerification1271Valid(); + + SignatureVerifierLogicWith1271Override logicWith1271Override = new SignatureVerifierLogicWith1271Override(); + vm.expectRevert(abi.encodeWithSignature("BadContractSignature()")); + logicWith1271Override.signatureVerification1271Invalid(); + + SignatureVerifierLogicWith1271Fail logicWith1271Fail = new SignatureVerifierLogicWith1271Fail(); + vm.expectRevert(abi.encodeWithSignature("BadContractSignature()")); + logicWith1271Fail.signatureVerification1271Fail(); + + ReferenceSignatureVerifierLogic referenceLogic = new ReferenceSignatureVerifierLogic(); + referenceLogic.referenceSignatureVerificationDirtyScratchSpace(); + vm.expectRevert(abi.encodeWithSignature("BadSignatureV(uint8)", 0)); + referenceLogic.referenceSignatureVerification65ByteWithBadSignatureV(); + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + referenceLogic + .referenceSignatureVerification65ByteJunkWithAcceptableSignatureV(); + vm.expectRevert(abi.encodeWithSignature("InvalidSigner()")); + referenceLogic.referenceSignatureVerification64ByteJunk(); + vm.expectRevert(abi.encodeWithSignature("InvalidSignature()")); + referenceLogic.referenceSignatureVerificationTooLong(); + referenceLogic.referenceSignatureVerificationValid(); + referenceLogic.referenceSignatureVerification1271Valid(); + + ReferenceSignatureVerifierLogicWith1271Override referenceLogicWith1271Override = new ReferenceSignatureVerifierLogicWith1271Override(); + vm.expectRevert(abi.encodeWithSignature("BadContractSignature()")); + referenceLogicWith1271Override + .referenceSignatureVerification1271Invalid(); + } +} diff --git a/test/foundry/TestNewHelpers.t.sol b/test/foundry/TestNewHelpers.t.sol new file mode 100644 index 000000000..6a1efb60a --- /dev/null +++ b/test/foundry/TestNewHelpers.t.sol @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + +import { + ConsiderationInterface +} from "../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + BasicOrderParameters, + Order, + CriteriaResolver, + Fulfillment, + OrderParameters, + FulfillmentComponent +} from "../../contracts/lib/ConsiderationStructs.sol"; + +import { BasicOrderType } from "../../contracts/lib/ConsiderationEnums.sol"; + +contract TestNewHelpersTest is BaseOrderTest { + struct Context { + ConsiderationInterface seaport; + } + + /** + * @dev to run tests against both the optimized and reference + * implementations with the exact same params, and to ensure setup works + * the same way, we use a stateless function that takes a context and then + * reverts after logic has been performed with the status of the HEVM + * failure slot. Each test file should have a test function that performs + * this call with its relevant Context struct, and then asserts that the + * revert bytes indicate no assertions failed. + */ + function test( + function(Context memory) external f, + Context memory context + ) internal { + try f(context) { + fail("Test logic should have reverted with assertion status"); + } catch (bytes memory reason) { + assertPass(reason); + } + } + + /** + * @dev A test should invoke the `test` function for both optimized and + * reference implementations by passing the relevant execution method and a + * Context struct. + */ + function testBasicOrder() public { + test(this.execBasicOrder, Context({ seaport: consideration })); + test(this.execBasicOrder, Context({ seaport: referenceConsideration })); + } + + /** + * @dev actual test logic should live in an external function marked with + * the "stateless" modifier, which reverts after execution with the value + * in the HEVM failure slot. Reverting with this value allows us to + * revert state changes (which *includes* HEVM assertion failure status, + * which will otherwise get reverted) and still assert that the test logic + * passed. + */ + function execBasicOrder(Context memory context) external stateless { + string memory label = "offerer"; + _setUpSingleOrderOfferConsiderationItems(label); + // create a signed order - this will configure baseOrderParameters and baseOrderComponents + // we will use BaseOrderComponents to configure the BaseOrderParameters and re-use the signature + Order memory order = createSignedOrder(context.seaport, label); + BasicOrderParameters + memory basicOrderParameters = toBasicOrderParameters( + order, + BasicOrderType.ERC721_TO_ERC20_FULL_OPEN + ); + + context.seaport.fulfillBasicOrder({ parameters: basicOrderParameters }); + } + + function testFulfillOrder() public { + test(this.execFulfillOrder, Context({ seaport: consideration })); + test( + this.execFulfillOrder, + Context({ seaport: referenceConsideration }) + ); + } + + function execFulfillOrder(Context memory context) external stateless { + string memory label = "offerer"; + _setUpSingleOrderOfferConsiderationItems(label); + // create a signed order - this will configure baseOrderParameters and baseOrderComponents + // we will use BaseOrderComponents to configure the BaseOrderParameters and re-use the signature + Order memory order = createSignedOrder(context.seaport, label); + + context.seaport.fulfillOrder({ + order: order, + fulfillerConduitKey: bytes32(0) + }); + } + + function testFulfillAdvancedOrder() public { + test( + this.execFulfillAdvancedOrder, + Context({ seaport: consideration }) + ); + test( + this.execFulfillAdvancedOrder, + Context({ seaport: referenceConsideration }) + ); + } + + function execFulfillAdvancedOrder( + Context memory context + ) external stateless { + string memory label = "offerer"; + _setUpSingleOrderOfferConsiderationItems(label); + // create a signed order - this will configure baseOrderParameters and baseOrderComponents + // we will use BaseOrderComponents to configure the BaseOrderParameters and re-use the signature + Order memory order = createSignedOrder(context.seaport, label); + + context.seaport.fulfillAdvancedOrder({ + advancedOrder: toAdvancedOrder(order), + criteriaResolvers: new CriteriaResolver[](0), + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + } + + function testMatchOrders() public { + test(this.execMatchOrders, Context({ seaport: consideration })); + test( + this.execMatchOrders, + Context({ seaport: referenceConsideration }) + ); + } + + function execMatchOrders(Context memory context) external stateless { + string memory label = "offerer"; + _setUpMatchOrderOfferConsiderationItems(label); + // create a signed order - this will configure baseOrderParameters and baseOrderComponents + // we will use BaseOrderComponents to configure the BaseOrderParameters and re-use the signature + Order memory order = createSignedOrder(context.seaport, label); + ( + Order memory mirror, + Fulfillment[] memory matchFulfillments + ) = createMirrorOrderAndFulfillments(context.seaport, order.parameters); + + Order[] memory orders = new Order[](2); + + orders[0] = order; + orders[1] = mirror; + + context.seaport.matchOrders({ + orders: orders, + fulfillments: matchFulfillments + }); + } + + function testFulfillAvailableOrders() public { + test( + this.execFulfillAvailableOrders, + Context({ seaport: consideration }) + ); + test( + this.execFulfillAvailableOrders, + Context({ seaport: referenceConsideration }) + ); + } + + function execFulfillAvailableOrders( + Context memory context + ) external stateless { + string memory label1 = "offerer"; + string memory label2 = "offerer 2"; + _setUpSingleOrderOfferConsiderationItems(label1, 1); + // caveat: need to create order explicitly after configuring since it relies on storage that may not be cleared + Order memory order = createSignedOrder(context.seaport, label1); + + _setUpSingleOrderOfferConsiderationItems(label2, 2); + Order memory order2 = createSignedOrder(context.seaport, label2); + + Order[] memory orders = new Order[](2); + orders[0] = order; + orders[1] = order2; + OrderParameters[] memory parameters = new OrderParameters[](2); + parameters[0] = order.parameters; + parameters[1] = order2.parameters; + ( + FulfillmentComponent[][] memory offerFulfillments, + FulfillmentComponent[][] memory considerationFulfillments + ) = createFulfillments(parameters); + + context.seaport.fulfillAvailableOrders({ + orders: orders, + offerFulfillments: offerFulfillments, + considerationFulfillments: considerationFulfillments, + fulfillerConduitKey: bytes32(0), + maximumFulfilled: 2 + }); + } + + function _setUpSingleOrderOfferConsiderationItems( + string memory label + ) internal { + _setUpSingleOrderOfferConsiderationItems(label, 1); + } + + function _setUpSingleOrderOfferConsiderationItems( + string memory label, + uint256 id + ) internal { + // make a labelled + reproducible address with ether, erc20s, and approvals for all erc20/erc721/erc1155 + address offerer = makeAddrWithAllocationsAndApprovals(label); + + // add a single erc20 offer item - start/end amounts the same, defaults to token1 + addErc20OfferItem({ amount: 100 }); + + // add a single considerationitem - defaults to test721_1 + addErc721ConsiderationItem({ + recipient: payable(offerer), + tokenId: id + }); + test721_1.mint(address(this), id); + } + + function _setUpMatchOrderOfferConsiderationItems( + string memory label + ) internal { + // make a labelled + reproducible address with ether, erc20s, and approvals for all erc20/erc721/erc1155 + address offerer = makeAddrWithAllocationsAndApprovals(label); + address mirror = makeAddrWithAllocationsAndApprovals("mirror offerer"); + + // add a single erc20 offer item - start/end amounts the same, defaults to token1 + addErc20OfferItem({ amount: 100 }); + + // add a single considerationitem - defaults to test721_1 + addErc721ConsiderationItem({ recipient: payable(offerer), tokenId: 1 }); + test721_1.mint(mirror, 1); + } + + // function _setUpFulfillAvailableOfferConsiderationItems(string) +} diff --git a/test/foundry/TokenTransferrer.t.sol b/test/foundry/TokenTransferrer.t.sol new file mode 100644 index 000000000..a989da498 --- /dev/null +++ b/test/foundry/TokenTransferrer.t.sol @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { + ConduitTransfer, + ConduitBatch1155Transfer, + ConduitItemType +} from "../../contracts/conduit/lib/ConduitStructs.sol"; + +import { TestERC20Revert } from "../../contracts/test/TestERC20Revert.sol"; + +import { TestERC20NotOk } from "../../contracts/test/TestERC20NotOk.sol"; + +import { TestERC721Revert } from "../../contracts/test/TestERC721Revert.sol"; + +import { TestERC1155Revert } from "../../contracts/test/TestERC1155Revert.sol"; + +import { BaseConduitTest } from "./conduit/BaseConduitTest.sol"; + +import { Conduit } from "../../contracts/conduit/Conduit.sol"; + +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; + +contract TokenTransferrerTest is BaseConduitTest, TokenTransferrerErrors { + bytes expectedRevert = + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + address alice = makeAddr("alice"); + address bob = makeAddr("bob"); + address noCodeTokenAddress = makeAddr("noCodeTokenAddress"); + + struct Context { + Conduit conduit; + bytes expectedRevert; + ConduitTransfer[] transfers; + ConduitBatch1155Transfer[] batchTransfers; + } + + function execute(Context memory context) external stateless { + vm.expectRevert(context.expectedRevert); + context.conduit.execute(context.transfers); + } + + function executeBatch(Context memory context) external stateless { + vm.expectRevert(context.expectedRevert); + context.conduit.executeBatch1155(context.batchTransfers); + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testRevertErc1155Transfer() public { + // Test the ERC1155 revert case. + TestERC1155Revert semifungibleTokenRevert; + semifungibleTokenRevert = new TestERC1155Revert(); + + ConduitTransfer[] memory revertTransfer; + revertTransfer = new ConduitTransfer[](1); + + ConduitTransfer[] memory noCodeTransfer; + noCodeTransfer = new ConduitTransfer[](1); + ConduitBatch1155Transfer[] memory noCodeBatchTransfer; + noCodeBatchTransfer = new ConduitBatch1155Transfer[](1); + + revertTransfer[0] = ConduitTransfer( + ConduitItemType.ERC1155, + address(semifungibleTokenRevert), + alice, + bob, + 0, + 1 + ); + + test( + this.execute, + Context( + conduit, + expectedRevert, + revertTransfer, + noCodeBatchTransfer + ) + ); + test( + this.execute, + Context( + referenceConduit, + expectedRevert, + revertTransfer, + noCodeBatchTransfer + ) + ); + } + + function testRevertErc721Transfer() public { + TestERC721Revert nonfungibleTokenRevert; + nonfungibleTokenRevert = new TestERC721Revert(); + vm.label(address(nonfungibleTokenRevert), "nonfungibleTokenRevert"); + + ConduitTransfer[] memory revertTransfer; + revertTransfer = new ConduitTransfer[](1); + + ConduitTransfer[] memory noCodeTransfer; + noCodeTransfer = new ConduitTransfer[](1); + ConduitBatch1155Transfer[] memory noCodeBatchTransfer; + noCodeBatchTransfer = new ConduitBatch1155Transfer[](1); + + revertTransfer[0] = ConduitTransfer( + ConduitItemType.ERC721, + address(nonfungibleTokenRevert), + alice, + bob, + 0, + 1 + ); + + test( + this.execute, + Context( + referenceConduit, + expectedRevert, + revertTransfer, + noCodeBatchTransfer + ) + ); + test( + this.execute, + Context( + conduit, + expectedRevert, + revertTransfer, + noCodeBatchTransfer + ) + ); + } + + function testRevertErc20Transfer() public { + // Test the generic failure case where the token contract reverts. + ConduitTransfer[] memory revertTransfer; + revertTransfer = new ConduitTransfer[](1); + + ConduitBatch1155Transfer[] memory noCodeBatchTransfer; + noCodeBatchTransfer = new ConduitBatch1155Transfer[](1); + + TestERC20Revert tokenRevert; + tokenRevert = new TestERC20Revert(); + vm.label(address(tokenRevert), "tokenRevert"); + + noCodeBatchTransfer[0] = ConduitBatch1155Transfer( + address(noCodeTokenAddress), + address(alice), + address(bob), + new uint256[](0), + new uint256[](0) + ); + + revertTransfer[0] = ConduitTransfer( + ConduitItemType.ERC20, + address(tokenRevert), + address(alice), + address(bob), + 0, + 1 + ); + + test( + this.execute, + Context( + conduit, + expectedRevert, + revertTransfer, + noCodeBatchTransfer + ) + ); + + expectedRevert = abi.encodeWithSelector( + TokenTransferGenericFailure.selector, + address(tokenRevert), + address(alice), + address(bob), + 0, + 1 + ); + + test( + this.execute, + Context( + referenceConduit, + expectedRevert, + revertTransfer, + noCodeBatchTransfer + ) + ); + } + + function testRevertNoCodeTransfer() public { + ConduitItemType[3] memory itemTypes; + itemTypes = [ + ConduitItemType.ERC20, + ConduitItemType.ERC721, + ConduitItemType.ERC1155 + ]; + ConduitItemType itemType; + + ConduitTransfer[] memory noCodeTransfer; + noCodeTransfer = new ConduitTransfer[](1); + ConduitBatch1155Transfer[] memory noCodeBatchTransfer; + noCodeBatchTransfer = new ConduitBatch1155Transfer[](1); + + // Iterate over each item type and test the revert case where there's no code. + for (uint256 i = 0; i < itemTypes.length; ++i) { + itemType = itemTypes[i]; + + noCodeTransfer[0] = ConduitTransfer( + itemType, + address(noCodeTokenAddress), + alice, + bob, + 0, + 1 + ); + + expectedRevert = abi.encodeWithSelector( + NoContract.selector, + noCodeTokenAddress + ); + + test( + this.execute, + Context( + referenceConduit, + expectedRevert, + noCodeTransfer, + noCodeBatchTransfer + ) + ); + test( + this.execute, + Context( + conduit, + expectedRevert, + noCodeTransfer, + noCodeBatchTransfer + ) + ); + } + + // Test the 1155 batch transfer no code revert. + noCodeBatchTransfer[0] = ConduitBatch1155Transfer( + noCodeTokenAddress, + alice, + bob, + new uint256[](0), + new uint256[](0) + ); + + test( + this.executeBatch, + Context( + referenceConduit, + expectedRevert, + noCodeTransfer, + noCodeBatchTransfer + ) + ); + test( + this.executeBatch, + Context( + conduit, + expectedRevert, + noCodeTransfer, + noCodeBatchTransfer + ) + ); + } + + function testRevertNotOk() public { + // Test the generic failure case where the token contract returns not OK but does not revert. + ConduitTransfer[] memory notOkTransfer; + notOkTransfer = new ConduitTransfer[](1); + + ConduitBatch1155Transfer[] memory noCodeBatchTransfer; + noCodeBatchTransfer = new ConduitBatch1155Transfer[](1); + + noCodeBatchTransfer[0] = ConduitBatch1155Transfer( + noCodeTokenAddress, + alice, + bob, + new uint256[](0), + new uint256[](0) + ); + + TestERC20NotOk tokenNotOk; + tokenNotOk = new TestERC20NotOk(); + vm.label(address(tokenNotOk), "tokenNotOk"); + + vm.startPrank(alice); + tokenNotOk.mint(alice, 100); + tokenNotOk.approve(address(consideration), uint256(100)); + tokenNotOk.approve(address(referenceConsideration), uint256(100)); + tokenNotOk.approve(address(conduit), uint256(100)); + tokenNotOk.approve(address(referenceConduit), uint256(100)); + vm.stopPrank(); + + notOkTransfer[0] = ConduitTransfer( + ConduitItemType.ERC20, + address(tokenNotOk), + address(alice), + address(bob), + 0, + 1 + ); + + expectedRevert = abi.encodeWithSelector( + BadReturnValueFromERC20OnTransfer.selector, + address(tokenNotOk), + address(alice), + address(bob), + 1 + ); + + test( + this.execute, + Context( + referenceConduit, + expectedRevert, + notOkTransfer, + noCodeBatchTransfer + ) + ); + test( + this.execute, + Context(conduit, expectedRevert, notOkTransfer, noCodeBatchTransfer) + ); + } +} diff --git a/test/foundry/TransferHelperMultipleRecipientsTest.sol b/test/foundry/TransferHelperMultipleRecipientsTest.sol new file mode 100644 index 000000000..c8f476cec --- /dev/null +++ b/test/foundry/TransferHelperMultipleRecipientsTest.sol @@ -0,0 +1,1467 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; + +import { + ConduitInterface +} from "../../contracts/interfaces/ConduitInterface.sol"; + +import { ConduitItemType } from "../../contracts/conduit/lib/ConduitEnums.sol"; + +import { TransferHelper } from "../../contracts/helpers/TransferHelper.sol"; + +import { + TransferHelperItem, + TransferHelperItemsWithRecipient +} from "../../contracts/helpers/TransferHelperStructs.sol"; + +import { TestERC20 } from "../../contracts/test/TestERC20.sol"; + +import { TestERC721 } from "../../contracts/test/TestERC721.sol"; + +import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; + +import { + ConduitMockInvalidMagic +} from "../../contracts/test/ConduitMockInvalidMagic.sol"; + +import { + ConduitMockRevertNoReason +} from "../../contracts/test/ConduitMockRevertNoReason.sol"; + +import { + ConduitControllerMock +} from "../../contracts/test/ConduitControllerMock.sol"; + +import { + InvalidERC721Recipient +} from "../../contracts/test/InvalidERC721Recipient.sol"; + +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; + +import { + TransferHelperErrors +} from "../../contracts/interfaces/TransferHelperErrors.sol"; + +import { + IERC721Receiver +} from "../../contracts/interfaces/IERC721Receiver.sol"; + +import { + ERC721ReceiverMock +} from "../../contracts/test/ERC721ReceiverMock.sol"; + +import { TestERC20Panic } from "../../contracts/test/TestERC20Panic.sol"; + +import { StubERC20 } from "./token/StubERC20.sol"; + +import { StubERC721 } from "./token/StubERC721.sol"; + +import { StubERC1155 } from "./token/StubERC1155.sol"; + +import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol"; + +contract TransferHelperMultipleRecipientsTest is BaseOrderTest { + using Strings for uint256; + TransferHelper transferHelper; + // Total supply of fungible tokens to be used in tests for all fungible tokens. + uint256 constant TOTAL_FUNGIBLE_TOKENS = 1e6; + // Total number of token identifiers to mint tokens for for ERC721s and ERC1155s. + uint256 constant TOTAL_TOKEN_IDENTIFIERS = 10; + // Constant bytes used for expecting revert with no message. + bytes constant REVERT_DATA_NO_MSG = "revert no message"; + ERC721ReceiverMock validERC721Receiver; + ERC721ReceiverMock invalidERC721Receiver; + InvalidERC721Recipient invalidRecipient; + + StubERC20[] stubERC20s; + StubERC721[] stubERC721s; + StubERC1155[] stubERC1155s; + + struct FromToBalance { + // Balance of from address. + uint256 from; + // Balance of to address. + uint256 to; + } + + struct FuzzInputsCommon { + // Amounts that can be used for the amount field on TransferHelperItem + uint256[10] amounts; + // Identifiers that can be used for the identifier field on TransferHelperItem + uint256[10] identifiers; + // Indexes that can be used to select tokens from the arrays erc20s/erc721s/erc1155s + uint256[10] tokenIndex; + // Recipients that can be used for the recipient field on TransferHelperItemsWithRecipients + address[10] recipients; + } + + function _deployStubTokens() internal { + for (uint256 i; i < 3; i++) { + StubERC20 stub20 = new StubERC20(); + StubERC721 stub721 = new StubERC721(); + StubERC1155 stub1155 = new StubERC1155(); + vm.label( + address(stub20), + string.concat("StubERC20 #", i.toString()) + ); + vm.label( + address(stub1155), + string.concat("StubERC1155 #", i.toString()) + ); + vm.label( + address(stub721), + string.concat("StubERC721 #", i.toString()) + ); + stubERC20s.push(stub20); + stubERC721s.push(stub721); + stubERC1155s.push(stub1155); + } + } + + function setUp() public override { + super.setUp(); + _deployAndConfigurePrecompiledTransferHelper(); + vm.label(address(transferHelper), "transferHelper"); + _deployStubTokens(); + + // Mint initial tokens to alice for tests. + for (uint256 tokenIdx = 0; tokenIdx < erc20s.length; tokenIdx++) { + erc20s[tokenIdx].mint(alice, TOTAL_FUNGIBLE_TOKENS); + } + + // Mint ERC721 and ERC1155 with token IDs 0 to TOTAL_TOKEN_IDENTIFIERS - 1 to alice + for ( + uint256 identifier = 0; + identifier < TOTAL_TOKEN_IDENTIFIERS; + identifier++ + ) { + for (uint256 tokenIdx = 0; tokenIdx < erc721s.length; tokenIdx++) { + erc721s[tokenIdx].mint(alice, identifier); + } + for (uint256 tokenIdx = 0; tokenIdx < erc1155s.length; tokenIdx++) { + erc1155s[tokenIdx].mint( + alice, + identifier, + TOTAL_FUNGIBLE_TOKENS + ); + } + } + + // Allow transfer helper to perform transfers for these addresses. + _setApprovals(alice); + _setApprovals(bob); + _setApprovals(cal); + + // Open a channel for transfer helper on the conduit + _updateConduitChannel(true); + + validERC721Receiver = new ERC721ReceiverMock( + IERC721Receiver.onERC721Received.selector, + ERC721ReceiverMock.Error.None + ); + vm.label(address(validERC721Receiver), "valid ERC721 receiver"); + invalidERC721Receiver = new ERC721ReceiverMock( + 0xabcd0000, + ERC721ReceiverMock.Error.RevertWithMessage + ); + vm.label( + address(invalidERC721Receiver), + "invalid (error) ERC721 receiver" + ); + + invalidRecipient = new InvalidERC721Recipient(); + vm.label( + address(invalidRecipient), + "invalid ERC721 receiver (bad selector)" + ); + } + + /** + * @dev TransferHelper depends on precomputed Conduit creation code hash, which will differ + * if tests are run with different compiler settings (which they are by default) + */ + function _deployAndConfigurePrecompiledTransferHelper() public { + if (!coverage_or_debug) { + transferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(conduitController)) + ) + ); + } else { + transferHelper = new TransferHelper(address(conduitController)); + } + } + + // Helper functions + + function _setApprovals(address _owner) internal override { + super._setApprovals(_owner); + vm.startPrank(_owner); + for (uint256 i = 0; i < erc20s.length; i++) { + erc20s[i].approve(address(transferHelper), MAX_INT); + } + for (uint256 i = 0; i < erc1155s.length; i++) { + erc1155s[i].setApprovalForAll(address(transferHelper), true); + } + for (uint256 i = 0; i < erc721s.length; i++) { + erc721s[i].setApprovalForAll(address(transferHelper), true); + } + vm.stopPrank(); + } + + function _updateConduitChannel(bool open) internal { + (address _conduit, ) = conduitController.getConduit(conduitKeyOne); + vm.prank(address(conduitController)); + ConduitInterface(_conduit).updateChannel(address(transferHelper), open); + } + + function _balanceOfTransferItemForAddress( + TransferHelperItem memory item, + address addr + ) internal view returns (uint256) { + if (item.itemType == ConduitItemType.ERC20) { + return TestERC20(item.token).balanceOf(addr); + } else if (item.itemType == ConduitItemType.ERC721) { + return + TestERC721(item.token).ownerOf(item.identifier) == addr ? 1 : 0; + } else if (item.itemType == ConduitItemType.ERC1155) { + return TestERC1155(item.token).balanceOf(addr, item.identifier); + } else if (item.itemType == ConduitItemType.NATIVE) { + // Balance for native does not matter as don't support native transfers so just return dummy value. + return 0; + } + // Revert on unsupported ConduitItemType. + revert(); + } + + function _balanceOfTransferItemForFromTo( + TransferHelperItem memory item, + address from, + address to + ) internal view returns (FromToBalance memory) { + return + FromToBalance( + _balanceOfTransferItemForAddress(item, from), + _balanceOfTransferItemForAddress(item, to) + ); + } + + modifier _ensureFuzzAssumptions(FuzzInputsCommon memory inputs) { + for (uint256 i = 0; i < inputs.amounts.length; i++) { + vm.assume(inputs.amounts[i] > 0); + vm.assume(inputs.recipients[i] != address(0)); + } + _; + } + + function _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + address from, + TransferHelperItem[] memory items, + address[10] memory recipients + ) internal view returns (TransferHelperItemsWithRecipient[] memory) { + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = new TransferHelperItemsWithRecipient[]( + recipients.length + ); + for (uint256 i = 0; i < recipients.length; i++) { + itemsWithRecipient[i] = TransferHelperItemsWithRecipient( + items, + _makeSafeRecipient(from, recipients[i]), + true + ); + } + + return itemsWithRecipient; + } + + function _unsafeGetTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + TransferHelperItem[] memory items, + address[10] memory recipients + ) internal pure returns (TransferHelperItemsWithRecipient[] memory) { + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = new TransferHelperItemsWithRecipient[]( + recipients.length + ); + for (uint256 i = 0; i < recipients.length; i++) { + itemsWithRecipient[i] = TransferHelperItemsWithRecipient( + items, + recipients[i], + true + ); + } + + return itemsWithRecipient; + } + + function _performSingleItemTransferAndCheckBalances( + TransferHelperItem memory item, + address from, + address[10] memory recipients, + bool makeSafeRecipient, + bytes memory expectRevertData + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = item; + + _performMultiItemTransferAndCheckBalances( + items, + from, + recipients, + makeSafeRecipient, + expectRevertData + ); + } + + function _performMultiItemTransferAndCheckBalances( + TransferHelperItem[] memory items, + address from, + address[10] memory recipients, + bool makeSafeRecipient, + bytes memory expectRevertData + ) public { + TransferHelperItemsWithRecipient[] memory itemsWithRecipient; + + if (makeSafeRecipient) { + itemsWithRecipient = _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + from, + items, + recipients + ); + } else { + itemsWithRecipient = _unsafeGetTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + items, + recipients + ); + } + + // Register expected revert if present. + if ( + // Compare hashes as we cannot directly compare bytes memory with bytes storage. + keccak256(expectRevertData) == keccak256(REVERT_DATA_NO_MSG) + ) { + vm.expectRevert(); + } else if (expectRevertData.length > 0) { + vm.expectRevert(expectRevertData); + } else { + // otherwise register expected emits + + address operator = address(conduit); + + for (uint256 i; i < itemsWithRecipient.length; i++) { + TransferHelperItemsWithRecipient + memory firstRecipientTransfer = itemsWithRecipient[i]; + for (uint256 j; j < firstRecipientTransfer.items.length; j++) { + TransferHelperItem memory item = firstRecipientTransfer + .items[j]; + // expect all 3 indexed topics plus data since Transfer event has same signature for ERC20/ERC721, + // but tokenId is indexed for 721 and not for ERC20 (so amount is data) + // ERC1155 has three indexed topics plus data. + + if (item.itemType == ConduitItemType.ERC20) { + vm.expectEmit(true, true, true, true, item.token); + + emit Transfer( + from, + firstRecipientTransfer.recipient, + item.amount + ); + } else if (item.itemType == ConduitItemType.ERC721) { + vm.expectEmit(true, true, true, true, item.token); + emit Transfer( + from, + firstRecipientTransfer.recipient, + item.identifier + ); + } else { + vm.expectEmit(true, true, true, true, item.token); + + emit TransferSingle( + operator, + from, + firstRecipientTransfer.recipient, + item.identifier, + item.amount + ); + } + } + } + } + + // Perform transfer. + vm.prank(from); + transferHelper.bulkTransfer(itemsWithRecipient, conduitKeyOne); + + // vm.stopPrank(); + } + + function _performMultiItemTransferAndCheckBalances( + TransferHelperItem[] memory items, + address from, + address[10] memory recipients, + bytes memory expectRevertDataWithConduit + ) public { + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + from, + items, + recipients + ); + // Register expected revert if present. + if ( + // Compare hashes as we cannot directly compare bytes memory with bytes storage. + keccak256(expectRevertDataWithConduit) == + keccak256(REVERT_DATA_NO_MSG) + ) { + vm.expectRevert(); + } else if (expectRevertDataWithConduit.length > 0) { + vm.expectRevert(expectRevertDataWithConduit); + } else { + // otherwise register expected emits + + address operator = address(conduit); + + for (uint256 i; i < itemsWithRecipient.length; i++) { + TransferHelperItemsWithRecipient + memory singleItemsWithRecipient = itemsWithRecipient[i]; + for ( + uint256 j; + j < singleItemsWithRecipient.items.length; + j++ + ) { + TransferHelperItem memory item = singleItemsWithRecipient + .items[j]; + // expect all 3 indexed topics plus data since Transfer event has same signature for ERC20/ERC721, + // but tokenId is indexed for 721 and not for ERC20 (so amount is data) + // ERC1155 has three indexed topics plus data. + if (item.itemType == ConduitItemType.ERC20) { + vm.expectEmit(true, true, true, true, item.token); + + emit Transfer( + from, + singleItemsWithRecipient.recipient, + item.amount + ); + } else if (item.itemType == ConduitItemType.ERC721) { + vm.expectEmit(true, true, true, true, item.token); + emit Transfer( + from, + singleItemsWithRecipient.recipient, + item.identifier + ); + } else { + vm.expectEmit(true, true, true, true, item.token); + emit TransferSingle( + operator, + from, + singleItemsWithRecipient.recipient, + item.identifier, + item.amount + ); + } + } + } + } + // Perform transfer. + vm.prank(from); + transferHelper.bulkTransfer(itemsWithRecipient, conduitKeyOne); + } + + function _makeSafeRecipient( + address from, + address fuzzRecipient + ) internal view returns (address) { + return _makeSafeRecipient(from, fuzzRecipient, false); + } + + function _makeSafeRecipient( + address from, + address fuzzRecipient, + bool reverting + ) internal view returns (address) { + if ( + fuzzRecipient == address(validERC721Receiver) || + (reverting && + (fuzzRecipient == address(invalidERC721Receiver) || + fuzzRecipient == address(invalidRecipient))) + ) { + return fuzzRecipient; + } else if ( + fuzzRecipient == address(0) || + fuzzRecipient.code.length > 0 || + from == fuzzRecipient + ) { + return address(uint160(fuzzRecipient) + 1); + } + return fuzzRecipient; + } + + function _getFuzzedTransferItem( + ConduitItemType itemType, + uint256 fuzzAmount, + uint256 fuzzIndex, + uint256 fuzzIdentifier + ) internal view returns (TransferHelperItem memory) { + return + _getFuzzedTransferItem( + itemType, + fuzzAmount, + fuzzIndex, + fuzzIdentifier, + true + ); + } + + function _getFuzzedTransferItem( + ConduitItemType itemType, + uint256 fuzzAmount, + uint256 fuzzIndex, + uint256 fuzzIdentifier, + bool useStub + ) internal view returns (TransferHelperItem memory) { + uint256 amount = fuzzAmount % (TOTAL_FUNGIBLE_TOKENS / 10); + uint256 identifier = fuzzIdentifier % TOTAL_TOKEN_IDENTIFIERS; + if (itemType == ConduitItemType.ERC20) { + address erc20; + if (useStub) { + uint256 index = fuzzIndex % stubERC20s.length; + erc20 = address(stubERC20s[index]); + } else { + uint256 index = fuzzIndex % erc20s.length; + erc20 = address(erc20s[index]); + } + return TransferHelperItem(itemType, erc20, 0, amount); + } else if (itemType == ConduitItemType.ERC1155) { + address erc1155; + if (useStub) { + uint256 index = fuzzIndex % stubERC1155s.length; + erc1155 = address(stubERC1155s[index]); + } else { + uint256 index = fuzzIndex % erc1155s.length; + erc1155 = address(erc1155s[index]); + } + return TransferHelperItem(itemType, erc1155, identifier, amount); + } else if (itemType == ConduitItemType.NATIVE) { + return TransferHelperItem(itemType, address(0), 0, amount); + } else if (itemType == ConduitItemType.ERC721) { + address erc721; + if (useStub) { + uint256 index = fuzzIndex % stubERC721s.length; + erc721 = address(stubERC721s[index]); + } else { + uint256 index = fuzzIndex % erc721s.length; + erc721 = address(erc721s[index]); + } + return TransferHelperItem(itemType, erc721, identifier, 1); + } + revert(); + } + + function _getFuzzedERC721TransferItemWithAmountGreaterThan1( + address, + uint256 fuzzAmount, + uint256 fuzzIndex, + uint256 fuzzIdentifier, + address + ) internal view returns (TransferHelperItem memory) { + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC721, + fuzzAmount, + fuzzIndex, + fuzzIdentifier + ); + item.amount = 2 + (fuzzAmount % TOTAL_FUNGIBLE_TOKENS); + return item; + } + + function getSelector( + bytes calldata returnData + ) public pure returns (bytes memory) { + return returnData[0x84:0x88]; + } + + // Test successful transfers + + function testBulkTransferERC20(FuzzInputsCommon memory inputs) public { + uint256 numItems = inputs.amounts.length; + + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[i], + inputs.tokenIndex[i], + 0 + ); + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC721( + FuzzInputsCommon memory inputs + ) public _ensureFuzzAssumptions(inputs) { + uint256 numItems = inputs.amounts.length; + + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem(ConduitItemType.ERC721, 1, i, i); + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC1155( + FuzzInputsCommon memory inputs + ) public _ensureFuzzAssumptions(inputs) { + uint256 numItems = inputs.amounts.length; + + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC1155, + inputs.amounts[i], + inputs.tokenIndex[i], + inputs.identifiers[i] + ); + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC1155andERC721( + FuzzInputsCommon memory inputs + ) public { + uint256 numItems = inputs.amounts.length; + + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + if (i % 2 == 0) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC1155, + inputs.amounts[i], + inputs.tokenIndex[i], + inputs.identifiers[i] + ); + } else { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + inputs.amounts[i], + inputs.tokenIndex[i], + inputs.identifiers[i] + ); + } + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC1155andERC721andERC20( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](3); + items[0] = _getFuzzedTransferItem( + ConduitItemType.ERC1155, + inputs.amounts[0], + inputs.tokenIndex[0], + inputs.identifiers[0] + ); + items[1] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[1], + inputs.identifiers[1] + ); + items[2] = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[2], + inputs.tokenIndex[2], + 0 + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferMultipleERC721SameContract( + FuzzInputsCommon memory inputs + ) public { + uint256 numItems = 3; + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + inputs.amounts[i], + // Same token index for all items since this is testing from same contract + inputs.tokenIndex[0], + // Each item has a different token identifier as alice only owns one ERC721 token + // for each identifier for this particular contract + i + ); + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferMultipleERC721DifferentContracts( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](3); + items[0] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + inputs.amounts[0], + // Different token index for all items since this is testing from different contracts + 0, + inputs.identifiers[0] + ); + items[1] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + inputs.amounts[1], + 1, + inputs.identifiers[1] + ); + items[2] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + inputs.amounts[2], + 2, + inputs.identifiers[2] + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferMultipleERC721andMultipleERC1155( + FuzzInputsCommon memory inputs + ) public { + uint256 numItems = 6; + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + // Fill items such that the first floor(numItems / 2) items are ERC1155 and the remaining + // items are ERC721 + for (uint256 i = 0; i < numItems; i++) { + if (i < numItems / 2) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC1155, + inputs.amounts[i], + // Ensure each item is from a different contract + i, + inputs.identifiers[i] + ); + } else { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + inputs.amounts[i], + i, + inputs.identifiers[i] + ); + } + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC7211NotUsingConduit( + FuzzInputsCommon memory inputs + ) public { + uint256 numItems = inputs.amounts.length; + + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[i], + inputs.identifiers[i] + ); + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC721ToContractRecipientNotUsingConduit( + FuzzInputsCommon memory inputs + ) public { + // ERC721ReceiverMock erc721Receiver = new ERC721ReceiverMock( + // IERC721Receiver.onERC721Received.selector, + // ERC721ReceiverMock.Error.None + // ); + + uint256 numItems = 6; + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[i], + i + ); + } + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + function testBulkTransferERC721AndERC20NotUsingConduit( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](2); + items[0] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[0], + inputs.identifiers[0] + ); + + items[1] = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[1], + inputs.tokenIndex[1], + 0 + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + "" + ); + } + + // Test reverts + + function testRevertBulkTransferERC20InvalidIdentifier( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[0], + inputs.tokenIndex[0], + 5 + ); + // Ensure ERC20 identifier is at least 1 + item.identifier += 1; + + _performSingleItemTransferAndCheckBalances( + item, + alice, + inputs.recipients, + true, + abi.encodePacked( + TransferHelperErrors.InvalidERC20Identifier.selector + ) + ); + } + + function testRevertBulkTransferERC721InvalidRecipient( + FuzzInputsCommon memory inputs + ) public { + address[10] memory invalidRecipients; + + for (uint256 i = 0; i < invalidRecipients.length; i++) { + InvalidERC721Recipient invalid = new InvalidERC721Recipient(); + invalidRecipients[i] = address(invalid); + emit log_named_address("recipient: ", invalidRecipients[i]); + } + + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[0], + inputs.identifiers[0], + false + ); + + _performSingleItemTransferAndCheckBalances( + item, + alice, + invalidRecipients, + false, + abi.encodeWithSignature( + "InvalidERC721Recipient(address)", + invalidRecipients[0] + ) + ); + } + + function testRevertBulkTransferETHonly( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = _getFuzzedTransferItem( + ConduitItemType.NATIVE, + inputs.amounts[0], + inputs.tokenIndex[0], + inputs.identifiers[0] + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + abi.encodePacked(TransferHelperErrors.InvalidItemType.selector) + ); + } + + function testRevertBulkTransferETHandERC721( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](2); + items[0] = _getFuzzedTransferItem( + ConduitItemType.NATIVE, + inputs.amounts[0], + inputs.tokenIndex[0], + inputs.identifiers[0] + ); + items[1] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[1], + inputs.identifiers[1] + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + abi.encodePacked(TransferHelperErrors.InvalidItemType.selector) + ); + } + + function testRevertBulkTransferERC721AmountMoreThan1UsingConduit( + FuzzInputsCommon memory inputs, + uint256 invalidAmount + ) public { + vm.assume(invalidAmount > 1); + + TransferHelperItem[] memory items = new TransferHelperItem[](1); + TransferHelperItem + memory item = _getFuzzedERC721TransferItemWithAmountGreaterThan1( + alice, + invalidAmount, + inputs.tokenIndex[0], + inputs.identifiers[0], + bob + ); + + items[0] = item; + + _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + alice, + items, + inputs.recipients + ); + + _performSingleItemTransferAndCheckBalances( + item, + alice, + inputs.recipients, + true, + abi.encodePacked( + TokenTransferrerErrors.InvalidERC721TransferAmount.selector, + items[0].amount + ) + ); + } + + function testRevertBulkTransferERC721AmountMoreThan1AndERC20UsingConduit( + FuzzInputsCommon memory inputs + ) public { + vm.assume(inputs.amounts[0] > 0); + + TransferHelperItem[] memory items = new TransferHelperItem[](2); + items[0] = _getFuzzedERC721TransferItemWithAmountGreaterThan1( + alice, + inputs.amounts[0], + inputs.tokenIndex[0], + inputs.identifiers[0], + bob + ); + + items[1] = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[1], + inputs.tokenIndex[1], + inputs.identifiers[1], + false + ); + + _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + alice, + items, + inputs.recipients + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + abi.encodePacked( + TokenTransferrerErrors.InvalidERC721TransferAmount.selector, + items[0].amount + ) + ); + } + + function testRevertBulkTransferNotOpenConduitChannel( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[0], + inputs.tokenIndex[0], + 0, + false + ); + + _updateConduitChannel(false); + + bytes memory returnedData = abi.encodeWithSelector( + 0x93daadf2, + address(transferHelper) + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + returnedData, + conduitKeyOne, + conduit + ) + ); + } + + function testRevertBulkTransferUnknownConduit( + FuzzInputsCommon memory inputs, + bytes32 fuzzConduitKey + ) public { + // Assume fuzzConduitKey is not equal to TransferHelper's value for "no conduit". + vm.assume( + fuzzConduitKey != bytes32(0) && fuzzConduitKey != conduitKeyOne + ); + + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = _getFuzzedTransferItem( + ConduitItemType.ERC20, + inputs.amounts[0], + inputs.tokenIndex[0], + 0, + false + ); + + // Reassign the conduit key that gets passed into TransferHelper to fuzzConduitKey. + conduitKeyOne = fuzzConduitKey; + + (address unknownConduitAddress, ) = conduitController.getConduit( + conduitKeyOne + ); + vm.label(unknownConduitAddress, "unknown conduit"); + + vm.expectRevert(); + vm.prank(alice); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + alice, + items, + inputs.recipients + ); + transferHelper.bulkTransfer(itemsWithRecipient, conduitKeyOne); + } + + function testRevertInvalidERC721Receiver( + FuzzInputsCommon memory inputs + ) public { + address[10] memory invalidReceivers; + + for (uint256 i = 0; i < 10; i++) { + invalidReceivers[i] = address( + new ERC721ReceiverMock( + 0xabcd0000, + ERC721ReceiverMock.Error.RevertWithMessage + ) + ); + } + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[0], + inputs.identifiers[0], + false + ); + _performSingleItemTransferAndCheckBalances( + item, + alice, + invalidReceivers, + false, + abi.encodeWithSignature( + "ERC721ReceiverErrorRevertString(string,address,address,uint256)", + "ERC721ReceiverMock: reverting", + invalidReceivers[0], + alice, + item.identifier + ) + ); + } + + function testRevertStringErrorWithConduit( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem memory item = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address _conduit, ) = conduitController.getConduit(conduitKeyOne); + // Attempt to transfer ERC721 tokens from bob to alice + // Expect revert since alice owns the tokens + _performSingleItemTransferAndCheckBalances( + item, + bob, + inputs.recipients, + true, + abi.encodeWithSignature( + "ConduitErrorRevertString(string,bytes32,address)", + "WRONG_FROM", + conduitKeyOne, + _conduit + ) + ); + } + + function testRevertPanicErrorWithConduit( + FuzzInputsCommon memory inputs + ) public { + // Create ERC20 token that reverts with a panic when calling transferFrom. + TestERC20Panic panicERC20 = new TestERC20Panic(); + + // Mint ERC20 tokens to alice. + panicERC20.mint(alice, 10); + + // Approve the ERC20 tokens + panicERC20.approve(alice, 10); + + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC20, + address(panicERC20), + 0, + 10 + ); + + (address _conduit, ) = conduitController.getConduit(conduitKeyOne); + bytes memory panicError = abi.encodeWithSelector(0x4e487b71, 18); + + // Revert with panic error when calling execute via conduit + _performMultiItemTransferAndCheckBalances( + items, + alice, + inputs.recipients, + true, + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + panicError, + conduitKeyOne, + _conduit + ) + ); + } + + function testRevertInvalidConduitMagicValue( + FuzzInputsCommon memory inputs + ) public { + // Deploy mock conduit controller + ConduitControllerMock mockConduitController = new ConduitControllerMock( + 2 // ConduitMockInvalidMagic + ); + + // Create conduit key using alice's address + bytes32 conduitKeyAlice = bytes32( + uint256(uint160(address(alice))) << 96 + ); + + // Deploy mock transfer helper that takes in the mock conduit controller + TransferHelper mockTransferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(mockConduitController)) + ) + ); + vm.label(address(mockTransferHelper), "mock transfer helper"); + + vm.startPrank(alice); + + // Create the mock conduit by calling the mock conduit controller + ConduitMockInvalidMagic mockConduit = ConduitMockInvalidMagic( + mockConduitController.createConduit(conduitKeyAlice, address(alice)) + ); + vm.label(address(mockConduit), "mock conduit"); + + address(mockConduit).codehash; + + // Assert the conduit key derived from the conduit address + // matches alice's conduit key + bytes32 mockConduitKey = mockConduitController.getKey( + address(mockConduit) + ); + + assertEq(mockConduitKey, conduitKeyAlice); + + // Create item to transfer + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address _conduit, bool exists) = mockConduitController.getConduit( + conduitKeyAlice + ); + + assertEq(address(mockConduit), _conduit); + assertEq(exists, true); + + vm.expectRevert( + abi.encodeWithSignature( + "InvalidConduit(bytes32,address)", + conduitKeyAlice, + mockConduit + ) + ); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + alice, + items, + inputs.recipients + ); + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice); + vm.stopPrank(); + } + + function testRevertNoErrorString(FuzzInputsCommon memory inputs) public { + // Deploy mock conduit controller + ConduitControllerMock mockConduitController = new ConduitControllerMock( + 1 // ConduitMockRevertNoReason + ); + + // Create conduit key using alice's address + bytes32 conduitKeyAlice = bytes32( + uint256(uint160(address(alice))) << 96 + ); + + // Deploy mock transfer helper that takes in the mock conduit controller + TransferHelper mockTransferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(mockConduitController)) + ) + ); + vm.label(address(mockTransferHelper), "mock transfer helper"); + + vm.startPrank(alice); + + // Create the mock conduit by calling the mock conduit controller + ConduitMockRevertNoReason mockConduit = ConduitMockRevertNoReason( + mockConduitController.createConduit(conduitKeyAlice, address(alice)) + ); + vm.label(address(mockConduit), "mock conduit"); + + address(mockConduit).codehash; + + // Assert the conduit key derived from the conduit address + // matches alice's conduit key + bytes32 mockConduitKey = mockConduitController.getKey( + address(mockConduit) + ); + + assertEq(mockConduitKey, conduitKeyAlice); + + // Create item to transfer + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address _conduit, bool exists) = mockConduitController.getConduit( + conduitKeyAlice + ); + + assertEq(address(mockConduit), _conduit); + assertEq(exists, true); + + vm.expectRevert( + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + "", + conduitKeyAlice, + mockConduit + ) + ); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + alice, + items, + inputs.recipients + ); + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice); + vm.stopPrank(); + } + + function testRevertWithData(FuzzInputsCommon memory inputs) public { + // Deploy mock conduit controller + ConduitControllerMock mockConduitController = new ConduitControllerMock( + 3 // ConduitMockRevertBytes + ); + + // Create conduit key using alice's address + bytes32 conduitKeyAlice = bytes32( + uint256(uint160(address(alice))) << 96 + ); + + // Deploy mock transfer helper that takes in the mock conduit controller + TransferHelper mockTransferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(mockConduitController)) + ) + ); + vm.label(address(mockTransferHelper), "mock transfer helper"); + + vm.startPrank(alice); + + // Create the mock conduit by calling the mock conduit controller + ConduitMockInvalidMagic mockConduit = ConduitMockInvalidMagic( + mockConduitController.createConduit(conduitKeyAlice, address(alice)) + ); + vm.label(address(mockConduit), "mock conduit"); + + address(mockConduit).codehash; + + // Assert the conduit key derived from the conduit address + // matches alice's conduit key + bytes32 mockConduitKey = mockConduitController.getKey( + address(mockConduit) + ); + + assertEq(mockConduitKey, conduitKeyAlice); + + // Create item to transfer + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address _conduit, bool exists) = mockConduitController.getConduit( + conduitKeyAlice + ); + + assertEq(address(mockConduit), _conduit); + assertEq(exists, true); + + bytes memory returnedData; + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithMultipleRecipientsFromTransferHelperItems( + alice, + items, + inputs.recipients + ); + try + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice) + returns (bytes4 /* magicValue */) {} catch (bytes memory reason) { + returnedData = this.getSelector(reason); + } + vm.expectRevert( + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + returnedData, + conduitKeyAlice, + mockConduit + ) + ); + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice); + vm.stopPrank(); + } +} diff --git a/test/foundry/TransferHelperTest.sol b/test/foundry/TransferHelperSingleRecipientTest.sol similarity index 52% rename from test/foundry/TransferHelperTest.sol rename to test/foundry/TransferHelperSingleRecipientTest.sol index 051934490..1fd8556f1 100644 --- a/test/foundry/TransferHelperTest.sol +++ b/test/foundry/TransferHelperSingleRecipientTest.sol @@ -1,32 +1,67 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; -// prettier-ignore -import { BaseConsiderationTest } from "./utils/BaseConsiderationTest.sol"; +pragma solidity ^0.8.17; import { BaseOrderTest } from "./utils/BaseOrderTest.sol"; -import { ConduitInterface } from "../../contracts/interfaces/ConduitInterface.sol"; +import { + ConduitInterface +} from "../../contracts/interfaces/ConduitInterface.sol"; import { ConduitItemType } from "../../contracts/conduit/lib/ConduitEnums.sol"; import { TransferHelper } from "../../contracts/helpers/TransferHelper.sol"; -import { TransferHelperItem } from "../../contracts/helpers/TransferHelperStructs.sol"; +import { + TransferHelperItem, + TransferHelperItemsWithRecipient +} from "../../contracts/helpers/TransferHelperStructs.sol"; import { TestERC20 } from "../../contracts/test/TestERC20.sol"; + import { TestERC721 } from "../../contracts/test/TestERC721.sol"; + import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; -import { TokenTransferrerErrors } from "../../contracts/interfaces/TokenTransferrerErrors.sol"; +import { + ConduitMockInvalidMagic +} from "../../contracts/test/ConduitMockInvalidMagic.sol"; + +import { + ConduitMockRevertNoReason +} from "../../contracts/test/ConduitMockRevertNoReason.sol"; + +import { + ConduitControllerMock +} from "../../contracts/test/ConduitControllerMock.sol"; + +import { + InvalidERC721Recipient +} from "../../contracts/test/InvalidERC721Recipient.sol"; + +import { + TokenTransferrerErrors +} from "../../contracts/interfaces/TokenTransferrerErrors.sol"; -import { TransferHelperInterface } from "../../contracts/interfaces/TransferHelperInterface.sol"; +import { + TransferHelperErrors +} from "../../contracts/interfaces/TransferHelperErrors.sol"; -contract TransferHelperTest is BaseOrderTest { +import { + IERC721Receiver +} from "../../contracts/interfaces/IERC721Receiver.sol"; + +import { + ERC721ReceiverMock +} from "../../contracts/test/ERC721ReceiverMock.sol"; + +import { TestERC20Panic } from "../../contracts/test/TestERC20Panic.sol"; + +contract TransferHelperSingleRecipientTest is BaseOrderTest { TransferHelper transferHelper; // Total supply of fungible tokens to be used in tests for all fungible tokens. uint256 constant TOTAL_FUNGIBLE_TOKENS = 1e6; // Total number of token identifiers to mint tokens for for ERC721s and ERC1155s. - uint256 constant TOTAL_TOKEN_IDENTIFERS = 10; + uint256 constant TOTAL_TOKEN_IDENTIFIERS = 10; // Constant bytes used for expecting revert with no message. bytes constant REVERT_DATA_NO_MSG = "revert no message"; @@ -38,8 +73,6 @@ contract TransferHelperTest is BaseOrderTest { } struct FuzzInputsCommon { - // Indicates if a conduit should be used for the transfer - bool useConduit; // Amounts that can be used for the amount field on TransferHelperItem uint256[10] amounts; // Identifiers that can be used for the identifier field on TransferHelperItem @@ -58,10 +91,10 @@ contract TransferHelperTest is BaseOrderTest { erc20s[tokenIdx].mint(alice, TOTAL_FUNGIBLE_TOKENS); } - // Mint ERC721 and ERC1155 with token IDs 0 to TOTAL_TOKEN_IDENTIFERS - 1 to alice + // Mint ERC721 and ERC1155 with token IDs 0 to TOTAL_TOKEN_IDENTIFIERS - 1 to alice for ( uint256 identifier = 0; - identifier < TOTAL_TOKEN_IDENTIFERS; + identifier < TOTAL_TOKEN_IDENTIFIERS; identifier++ ) { for (uint256 tokenIdx = 0; tokenIdx < erc721s.length; tokenIdx++) { @@ -90,12 +123,16 @@ contract TransferHelperTest is BaseOrderTest { * if tests are run with different compiler settings (which they are by default) */ function _deployAndConfigurePrecompiledTransferHelper() public { - transferHelper = TransferHelper( - deployCode( - "optimized-out/TransferHelper.sol/TransferHelper.json", - abi.encode(address(conduitController)) - ) - ); + if (!coverage_or_debug) { + transferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(conduitController)) + ) + ); + } else { + transferHelper = new TransferHelper(address(conduitController)); + } } // Helper functions @@ -113,14 +150,6 @@ contract TransferHelperTest is BaseOrderTest { erc721s[i].setApprovalForAll(address(transferHelper), true); } vm.stopPrank(); - emit log_named_address( - "Owner proxy approved for all tokens from", - _owner - ); - emit log_named_address( - "Consideration approved for all tokens from", - _owner - ); } function _updateConduitChannel(bool open) internal { @@ -160,11 +189,26 @@ contract TransferHelperTest is BaseOrderTest { ); } + function _getTransferHelperItemsWithRecipientFromTransferHelperItems( + TransferHelperItem[] memory items, + address to + ) internal pure returns (TransferHelperItemsWithRecipient[] memory) { + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = new TransferHelperItemsWithRecipient[]( + 1 + ); + itemsWithRecipient[0] = TransferHelperItemsWithRecipient( + items, + to, + true + ); + return itemsWithRecipient; + } + function _performSingleItemTransferAndCheckBalances( TransferHelperItem memory item, address from, address to, - bool useConduit, bytes memory expectRevertData ) public { TransferHelperItem[] memory items = new TransferHelperItem[](1); @@ -173,7 +217,6 @@ contract TransferHelperTest is BaseOrderTest { items, from, to, - useConduit, expectRevertData ); } @@ -182,7 +225,6 @@ contract TransferHelperTest is BaseOrderTest { TransferHelperItem[] memory items, address from, address to, - bool useConduit, bytes memory expectRevertData ) public { vm.startPrank(from); @@ -209,11 +251,12 @@ contract TransferHelperTest is BaseOrderTest { vm.expectRevert(expectRevertData); } // Perform transfer. - transferHelper.bulkTransfer( - items, - to, - useConduit ? conduitKeyOne : bytes32(0) - ); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithRecipientFromTransferHelperItems( + items, + to + ); + transferHelper.bulkTransfer(itemsWithRecipient, conduitKeyOne); // Get balances after transfer FromToBalance[] memory afterTransferBalances = new FromToBalance[]( @@ -267,17 +310,11 @@ contract TransferHelperTest is BaseOrderTest { uint256 fuzzIdentifier ) internal view returns (TransferHelperItem memory) { uint256 amount = fuzzAmount % TOTAL_FUNGIBLE_TOKENS; - uint256 identifier = fuzzIdentifier % TOTAL_TOKEN_IDENTIFERS; + uint256 identifier = fuzzIdentifier % TOTAL_TOKEN_IDENTIFIERS; if (itemType == ConduitItemType.ERC20) { uint256 index = fuzzIndex % erc20s.length; TestERC20 erc20 = erc20s[index]; - return - TransferHelperItem( - itemType, - address(erc20), - identifier, - amount - ); + return TransferHelperItem(itemType, address(erc20), 0, amount); } else if (itemType == ConduitItemType.ERC1155) { uint256 index = fuzzIndex % erc1155s.length; TestERC1155 erc1155 = erc1155s[index]; @@ -318,6 +355,12 @@ contract TransferHelperTest is BaseOrderTest { return item; } + function getSelector( + bytes calldata returnData + ) public pure returns (bytes memory) { + return returnData[0x84:0x88]; + } + // Test successful transfers function testBulkTransferERC20(FuzzInputsCommon memory inputs) public { @@ -325,16 +368,10 @@ contract TransferHelperTest is BaseOrderTest { ConduitItemType.ERC20, inputs.amounts[0], inputs.tokenIndex[0], - inputs.identifiers[0] + 0 ); - _performSingleItemTransferAndCheckBalances( - item, - alice, - bob, - inputs.useConduit, - "" - ); + _performSingleItemTransferAndCheckBalances(item, alice, bob, ""); } function testBulkTransferERC721(FuzzInputsCommon memory inputs) public { @@ -345,18 +382,12 @@ contract TransferHelperTest is BaseOrderTest { inputs.identifiers[0] ); - _performSingleItemTransferAndCheckBalances( - item, - alice, - bob, - inputs.useConduit, - "" - ); + _performSingleItemTransferAndCheckBalances(item, alice, bob, ""); } - function testBulkTransferERC721toBobThenCal(FuzzInputsCommon memory inputs) - public - { + function testBulkTransferERC721toBobThenCal( + FuzzInputsCommon memory inputs + ) public { TransferHelperItem memory item = _getFuzzedTransferItem( ConduitItemType.ERC721, inputs.amounts[0], @@ -364,20 +395,8 @@ contract TransferHelperTest is BaseOrderTest { inputs.identifiers[0] ); - _performSingleItemTransferAndCheckBalances( - item, - alice, - bob, - inputs.useConduit, - "" - ); - _performSingleItemTransferAndCheckBalances( - item, - bob, - cal, - inputs.useConduit, - "" - ); + _performSingleItemTransferAndCheckBalances(item, alice, bob, ""); + _performSingleItemTransferAndCheckBalances(item, bob, cal, ""); } function testBulkTransferERC1155(FuzzInputsCommon memory inputs) public { @@ -388,18 +407,12 @@ contract TransferHelperTest is BaseOrderTest { inputs.identifiers[0] ); - _performSingleItemTransferAndCheckBalances( - item, - alice, - bob, - inputs.useConduit, - "" - ); + _performSingleItemTransferAndCheckBalances(item, alice, bob, ""); } - function testBulkTransferERC1155andERC721(FuzzInputsCommon memory inputs) - public - { + function testBulkTransferERC1155andERC721( + FuzzInputsCommon memory inputs + ) public { TransferHelperItem[] memory items = new TransferHelperItem[](2); items[0] = _getFuzzedTransferItem( ConduitItemType.ERC1155, @@ -414,13 +427,7 @@ contract TransferHelperTest is BaseOrderTest { inputs.identifiers[1] ); - _performMultiItemTransferAndCheckBalances( - items, - alice, - bob, - inputs.useConduit, - "" - ); + _performMultiItemTransferAndCheckBalances(items, alice, bob, ""); } function testBulkTransferERC1155andERC721andERC20( @@ -435,7 +442,7 @@ contract TransferHelperTest is BaseOrderTest { ); items[1] = _getFuzzedTransferItem( ConduitItemType.ERC721, - inputs.amounts[1], + 1, inputs.tokenIndex[1], inputs.identifiers[1] ); @@ -443,16 +450,10 @@ contract TransferHelperTest is BaseOrderTest { ConduitItemType.ERC20, inputs.amounts[2], inputs.tokenIndex[2], - inputs.identifiers[2] + 0 ); - _performMultiItemTransferAndCheckBalances( - items, - alice, - bob, - inputs.useConduit, - "" - ); + _performMultiItemTransferAndCheckBalances(items, alice, bob, ""); } function testBulkTransferMultipleERC721SameContract( @@ -472,13 +473,7 @@ contract TransferHelperTest is BaseOrderTest { ); } - _performMultiItemTransferAndCheckBalances( - items, - alice, - bob, - inputs.useConduit, - "" - ); + _performMultiItemTransferAndCheckBalances(items, alice, bob, ""); } function testBulkTransferMultipleERC721DifferentContracts( @@ -505,13 +500,7 @@ contract TransferHelperTest is BaseOrderTest { inputs.identifiers[2] ); - _performMultiItemTransferAndCheckBalances( - items, - alice, - bob, - inputs.useConduit, - "" - ); + _performMultiItemTransferAndCheckBalances(items, alice, bob, ""); } function testBulkTransferMultipleERC721andMultipleERC1155( @@ -541,34 +530,57 @@ contract TransferHelperTest is BaseOrderTest { } } - _performMultiItemTransferAndCheckBalances( - items, - alice, - bob, - inputs.useConduit, - "" + _performMultiItemTransferAndCheckBalances(items, alice, bob, ""); + } + + function testBulkTransferERC7211NotUsingConduit( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[0], + inputs.identifiers[0] ); + + _performSingleItemTransferAndCheckBalances(item, alice, bob, ""); } - function testBulkTransferERC721AmountMoreThan1NotUsingConduit( + function testBulkTransferERC721ToContractRecipientNotUsingConduit( FuzzInputsCommon memory inputs ) public { - TransferHelperItem - memory item = _getFuzzedERC721TransferItemWithAmountGreaterThan1( - inputs.amounts[0], - inputs.tokenIndex[0], - inputs.identifiers[0] + ERC721ReceiverMock erc721Receiver = new ERC721ReceiverMock( + IERC721Receiver.onERC721Received.selector, + ERC721ReceiverMock.Error.None + ); + + uint256 numItems = 6; + TransferHelperItem[] memory items = new TransferHelperItem[](numItems); + + for (uint256 i = 0; i < numItems; i++) { + items[i] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[i], + i ); + } - _performSingleItemTransferAndCheckBalances(item, alice, bob, false, ""); + _performMultiItemTransferAndCheckBalances( + items, + alice, + address(erc721Receiver), + "" + ); } - function testBulkTransferERC721AmountMoreThan1AndERC20NotUsingConduit( + function testBulkTransferERC721AndERC20NotUsingConduit( FuzzInputsCommon memory inputs ) public { TransferHelperItem[] memory items = new TransferHelperItem[](2); - items[0] = _getFuzzedERC721TransferItemWithAmountGreaterThan1( - inputs.amounts[0], + items[0] = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, inputs.tokenIndex[0], inputs.identifiers[0] ); @@ -577,36 +589,81 @@ contract TransferHelperTest is BaseOrderTest { ConduitItemType.ERC20, inputs.amounts[1], inputs.tokenIndex[1], - inputs.identifiers[1] + 0 ); - _performMultiItemTransferAndCheckBalances(items, alice, bob, false, ""); + _performMultiItemTransferAndCheckBalances(items, alice, bob, ""); } // Test reverts - function testRevertBulkTransferETHonly(FuzzInputsCommon memory inputs) - public - { + function testRevertBulkTransferERC20InvalidIdentifier( + FuzzInputsCommon memory inputs + ) public { TransferHelperItem memory item = _getFuzzedTransferItem( - ConduitItemType.NATIVE, + ConduitItemType.ERC20, inputs.amounts[0], inputs.tokenIndex[0], + 5 + ); + // Ensure ERC20 identifier is at least 1 + item.identifier += 1; + + _performSingleItemTransferAndCheckBalances( + item, + alice, + bob, + abi.encodePacked( + TransferHelperErrors.InvalidERC20Identifier.selector + ) + ); + } + + function testRevertBulkTransferERC721InvalidRecipient( + FuzzInputsCommon memory inputs + ) public { + InvalidERC721Recipient invalidRecipient = new InvalidERC721Recipient(); + + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[0], inputs.identifiers[0] ); _performSingleItemTransferAndCheckBalances( item, alice, + address(invalidRecipient), + abi.encodeWithSignature( + "InvalidERC721Recipient(address)", + invalidRecipient + ) + ); + } + + function testRevertBulkTransferETHonly( + FuzzInputsCommon memory inputs + ) public { + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = _getFuzzedTransferItem( + ConduitItemType.NATIVE, + inputs.amounts[0], + inputs.tokenIndex[0], + inputs.identifiers[0] + ); + + _performMultiItemTransferAndCheckBalances( + items, + alice, bob, - inputs.useConduit, - abi.encodePacked(TransferHelperInterface.InvalidItemType.selector) + abi.encodePacked(TransferHelperErrors.InvalidItemType.selector) ); } - function testRevertBulkTransferETHandERC721(FuzzInputsCommon memory inputs) - public - { + function testRevertBulkTransferETHandERC721( + FuzzInputsCommon memory inputs + ) public { TransferHelperItem[] memory items = new TransferHelperItem[](2); items[0] = _getFuzzedTransferItem( ConduitItemType.NATIVE, @@ -616,7 +673,7 @@ contract TransferHelperTest is BaseOrderTest { ); items[1] = _getFuzzedTransferItem( ConduitItemType.ERC721, - inputs.amounts[1], + 1, inputs.tokenIndex[1], inputs.identifiers[1] ); @@ -625,28 +682,34 @@ contract TransferHelperTest is BaseOrderTest { items, alice, bob, - inputs.useConduit, - abi.encodePacked(TransferHelperInterface.InvalidItemType.selector) + abi.encodePacked(TransferHelperErrors.InvalidItemType.selector) ); } function testRevertBulkTransferERC721AmountMoreThan1UsingConduit( - FuzzInputsCommon memory inputs + FuzzInputsCommon memory inputs, + uint256 invalidAmount ) public { + vm.assume(invalidAmount > 1); + + TransferHelperItem[] memory items = new TransferHelperItem[](1); TransferHelperItem memory item = _getFuzzedERC721TransferItemWithAmountGreaterThan1( - inputs.amounts[0], + invalidAmount, inputs.tokenIndex[0], inputs.identifiers[0] ); + items[0] = item; + _getTransferHelperItemsWithRecipientFromTransferHelperItems(items, bob); + _performSingleItemTransferAndCheckBalances( item, alice, bob, - true, abi.encodePacked( - TokenTransferrerErrors.InvalidERC721TransferAmount.selector + TokenTransferrerErrors.InvalidERC721TransferAmount.selector, + items[0].amount ) ); } @@ -654,6 +717,8 @@ contract TransferHelperTest is BaseOrderTest { function testRevertBulkTransferERC721AmountMoreThan1AndERC20UsingConduit( FuzzInputsCommon memory inputs ) public { + vm.assume(inputs.amounts[0] > 0); + TransferHelperItem[] memory items = new TransferHelperItem[](2); items[0] = _getFuzzedERC721TransferItemWithAmountGreaterThan1( inputs.amounts[0], @@ -672,9 +737,9 @@ contract TransferHelperTest is BaseOrderTest { items, alice, bob, - true, abi.encodePacked( - TokenTransferrerErrors.InvalidERC721TransferAmount.selector + TokenTransferrerErrors.InvalidERC721TransferAmount.selector, + items[0].amount ) ); } @@ -682,21 +747,30 @@ contract TransferHelperTest is BaseOrderTest { function testRevertBulkTransferNotOpenConduitChannel( FuzzInputsCommon memory inputs ) public { - TransferHelperItem memory item = _getFuzzedTransferItem( + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = _getFuzzedTransferItem( ConduitItemType.ERC20, inputs.amounts[0], inputs.tokenIndex[0], - inputs.identifiers[0] + 0 ); + _updateConduitChannel(false); + + bytes memory returnedData = abi.encodeWithSelector( + 0x93daadf2, + address(transferHelper) + ); + _performSingleItemTransferAndCheckBalances( - item, + items[0], alice, bob, - true, - abi.encodeWithSelector( - ConduitInterface.ChannelClosed.selector, - address(transferHelper) + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + returnedData, + conduitKeyOne, + conduit ) ); } @@ -709,20 +783,344 @@ contract TransferHelperTest is BaseOrderTest { vm.assume( fuzzConduitKey != bytes32(0) && fuzzConduitKey != conduitKeyOne ); - TransferHelperItem memory item = _getFuzzedTransferItem( + + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = _getFuzzedTransferItem( ConduitItemType.ERC20, inputs.amounts[0], inputs.tokenIndex[0], - inputs.identifiers[0] + 0 ); + // Reassign the conduit key that gets passed into TransferHelper to fuzzConduitKey. conduitKeyOne = fuzzConduitKey; + + (address unknownConduitAddress, ) = conduitController.getConduit( + conduitKeyOne + ); + vm.label(unknownConduitAddress, "unknown conduit"); + + vm.expectRevert(); + vm.prank(alice); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithRecipientFromTransferHelperItems( + items, + bob + ); + transferHelper.bulkTransfer(itemsWithRecipient, conduitKeyOne); + } + + function testRevertInvalidERC721Receiver( + FuzzInputsCommon memory inputs + ) public { + // Deploy invalid mock ERC721 receiver + ERC721ReceiverMock mockReceiver = new ERC721ReceiverMock( + 0xabcd0000, + ERC721ReceiverMock.Error.RevertWithMessage + ); + + TransferHelperItem memory item = _getFuzzedTransferItem( + ConduitItemType.ERC721, + 1, + inputs.tokenIndex[0], + inputs.identifiers[0] + ); _performSingleItemTransferAndCheckBalances( item, alice, + address(mockReceiver), + abi.encodeWithSignature( + "ERC721ReceiverErrorRevertString(string,address,address,uint256)", + "ERC721ReceiverMock: reverting", + mockReceiver, + alice, + item.identifier + ) + ); + } + + function testRevertStringErrorWithConduit() public { + TransferHelperItem memory item = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address derivedConduit, ) = conduitController.getConduit( + conduitKeyOne + ); + // Attempt to transfer ERC721 tokens from bob to alice + // Expect revert since alice owns the tokens + _performSingleItemTransferAndCheckBalances( + item, bob, - true, - REVERT_DATA_NO_MSG + alice, + abi.encodeWithSignature( + "ConduitErrorRevertString(string,bytes32,address)", + "WRONG_FROM", + conduitKeyOne, + derivedConduit + ) + ); + } + + function testRevertPanicErrorWithConduit() public { + // Create ERC20 token that reverts with a panic when calling transferFrom. + TestERC20Panic panicERC20 = new TestERC20Panic(); + + // Mint ERC20 tokens to alice. + panicERC20.mint(alice, 10); + + // Approve the ERC20 tokens + panicERC20.approve(alice, 10); + + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC20, + address(panicERC20), + 0, + 10 + ); + + (address derivedConduit, ) = conduitController.getConduit( + conduitKeyOne + ); + bytes memory panicError = abi.encodeWithSelector(0x4e487b71, 18); + + // Revert with panic error when calling execute via conduit + _performMultiItemTransferAndCheckBalances( + items, + alice, + bob, + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + panicError, + conduitKeyOne, + derivedConduit + ) + ); + } + + function testRevertInvalidConduitMagicValue() public { + // Deploy mock conduit controller + ConduitControllerMock mockConduitController = new ConduitControllerMock( + 2 // ConduitMockInvalidMagic + ); + + // Create conduit key using alice's address + bytes32 conduitKeyAlice = bytes32( + uint256(uint160(address(alice))) << 96 + ); + + TransferHelper mockTransferHelper; + if (!coverage_or_debug) { + // Deploy mock transfer helper that takes in the mock conduit controller + mockTransferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(mockConduitController)) + ) + ); + } else { + mockTransferHelper = new TransferHelper( + address(mockConduitController) + ); + } + vm.label(address(mockTransferHelper), "mock transfer helper"); + + vm.startPrank(alice); + + // Create the mock conduit by calling the mock conduit controller + ConduitMockInvalidMagic mockConduit = ConduitMockInvalidMagic( + mockConduitController.createConduit(conduitKeyAlice, address(alice)) + ); + vm.label(address(mockConduit), "mock conduit"); + + address(mockConduit).codehash; + + // Assert the conduit key derived from the conduit address + // matches alice's conduit key + bytes32 mockConduitKey = mockConduitController.getKey( + address(mockConduit) + ); + + assertEq(mockConduitKey, conduitKeyAlice); + + // Create item to transfer + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address derivedConduit, bool exists) = mockConduitController + .getConduit(conduitKeyAlice); + + assertEq(address(mockConduit), derivedConduit); + assertEq(exists, true); + + vm.expectRevert( + abi.encodeWithSignature( + "InvalidConduit(bytes32,address)", + conduitKeyAlice, + mockConduit + ) + ); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithRecipientFromTransferHelperItems( + items, + bob + ); + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice); + vm.stopPrank(); + } + + function testRevertNoErrorString() public { + // Deploy mock conduit controller + ConduitControllerMock mockConduitController = new ConduitControllerMock( + 1 // ConduitMockRevertNoReason + ); + + // Create conduit key using alice's address + bytes32 conduitKeyAlice = bytes32( + uint256(uint160(address(alice))) << 96 + ); + + // Deploy mock transfer helper that takes in the mock conduit controller + TransferHelper mockTransferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(mockConduitController)) + ) + ); + vm.label(address(mockTransferHelper), "mock transfer helper"); + + vm.startPrank(alice); + + // Create the mock conduit by calling the mock conduit controller + ConduitMockRevertNoReason mockConduit = ConduitMockRevertNoReason( + mockConduitController.createConduit(conduitKeyAlice, address(alice)) + ); + vm.label(address(mockConduit), "mock conduit"); + + address(mockConduit).codehash; + + // Assert the conduit key derived from the conduit address + // matches alice's conduit key + bytes32 mockConduitKey = mockConduitController.getKey( + address(mockConduit) ); + + assertEq(mockConduitKey, conduitKeyAlice); + + // Create item to transfer + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address derivedConduit, bool exists) = mockConduitController + .getConduit(conduitKeyAlice); + + assertEq(address(mockConduit), derivedConduit); + assertEq(exists, true); + + vm.expectRevert( + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + "", + conduitKeyAlice, + mockConduit + ) + ); + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithRecipientFromTransferHelperItems( + items, + bob + ); + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice); + vm.stopPrank(); + } + + function testRevertWithData() public { + // Deploy mock conduit controller + ConduitControllerMock mockConduitController = new ConduitControllerMock( + 3 // ConduitMockRevertBytes + ); + + // Create conduit key using alice's address + bytes32 conduitKeyAlice = bytes32( + uint256(uint160(address(alice))) << 96 + ); + + // Deploy mock transfer helper that takes in the mock conduit controller + TransferHelper mockTransferHelper = TransferHelper( + deployCode( + "optimized-out/TransferHelper.sol/TransferHelper.json", + abi.encode(address(mockConduitController)) + ) + ); + vm.label(address(mockTransferHelper), "mock transfer helper"); + + vm.startPrank(alice); + + // Create the mock conduit by calling the mock conduit controller + ConduitMockInvalidMagic mockConduit = ConduitMockInvalidMagic( + mockConduitController.createConduit(conduitKeyAlice, address(alice)) + ); + vm.label(address(mockConduit), "mock conduit"); + + address(mockConduit).codehash; + + // Assert the conduit key derived from the conduit address + // matches alice's conduit key + bytes32 mockConduitKey = mockConduitController.getKey( + address(mockConduit) + ); + + assertEq(mockConduitKey, conduitKeyAlice); + + // Create item to transfer + TransferHelperItem[] memory items = new TransferHelperItem[](1); + items[0] = TransferHelperItem( + ConduitItemType.ERC721, + address(erc721s[0]), + 5, + 1 + ); + + (address derivedConduit, bool exists) = mockConduitController + .getConduit(conduitKeyAlice); + + assertEq(address(mockConduit), derivedConduit); + assertEq(exists, true); + + bytes memory returnedData; + TransferHelperItemsWithRecipient[] + memory itemsWithRecipient = _getTransferHelperItemsWithRecipientFromTransferHelperItems( + items, + bob + ); + try + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice) + returns (bytes4 /* magicValue */) {} catch (bytes memory reason) { + returnedData = this.getSelector(reason); + } + vm.expectRevert( + abi.encodeWithSignature( + "ConduitErrorRevertBytes(bytes,bytes32,address)", + returnedData, + conduitKeyAlice, + mockConduit + ) + ); + mockTransferHelper.bulkTransfer(itemsWithRecipient, conduitKeyAlice); + vm.stopPrank(); } } diff --git a/test/foundry/conduit/BaseConduitTest.sol b/test/foundry/conduit/BaseConduitTest.sol index da7d87236..8edb4e5ea 100644 --- a/test/foundry/conduit/BaseConduitTest.sol +++ b/test/foundry/conduit/BaseConduitTest.sol @@ -1,14 +1,20 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; import { BaseConsiderationTest } from "../utils/BaseConsiderationTest.sol"; -import { ConduitTransfer, ConduitItemType, ConduitBatch1155Transfer } from "../../../contracts/conduit/lib/ConduitStructs.sol"; +import { + ConduitTransfer, + ConduitItemType, + ConduitBatch1155Transfer +} from "../../../contracts/conduit/lib/ConduitStructs.sol"; import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; import { ERC721Recipient } from "../utils/ERC721Recipient.sol"; import { ERC1155Recipient } from "../utils/ERC1155Recipient.sol"; -import { ERC1155TokenReceiver } from "@rari-capital/solmate/src/tokens/ERC1155.sol"; +import { + ERC1155TokenReceiver +} from "@rari-capital/solmate/src/tokens/ERC1155.sol"; contract BaseConduitTest is BaseConsiderationTest, @@ -74,10 +80,10 @@ contract BaseConduitTest is } ///@dev helper to coerce a fuzzed address into one that can accept tokens if necessary - function receiver(address addr, ConduitItemType itemType) - internal - returns (address) - { + function receiver( + address addr, + ConduitItemType itemType + ) internal returns (address) { // 0 address is not valid mint or origin address if (addr == address(0)) { return address(1); @@ -275,8 +281,8 @@ contract BaseConduitTest is TestERC20 erc20 = TestERC20(token); erc20.mint(from, transfer.amount); vm.startPrank(from); - erc20.approve(address(conduit), 2**256 - 1); - erc20.approve(address(referenceConduit), 2**256 - 1); + erc20.approve(address(conduit), 2 ** 256 - 1); + erc20.approve(address(referenceConduit), 2 ** 256 - 1); vm.stopPrank(); } else if (itemType == ConduitItemType.ERC1155) { TestERC1155 erc1155 = TestERC1155(token); @@ -318,11 +324,9 @@ contract BaseConduitTest is } } - function getExpectedTokenBalance(ConduitTransfer memory transfer) - internal - view - returns (uint256) - { + function getExpectedTokenBalance( + ConduitTransfer memory transfer + ) internal view returns (uint256) { return userToExpectedTokenIdentifierBalance[transfer.to][transfer.token][ transfer.identifier @@ -343,9 +347,9 @@ contract BaseConduitTest is return batchTokenBalances; } - function updateExpectedTokenBalances(ConduitTransfer[] memory transfers) - internal - { + function updateExpectedTokenBalances( + ConduitTransfer[] memory transfers + ) internal { for (uint256 i = 0; i < transfers.length; ++i) { ConduitTransfer memory transfer = transfers[i]; ConduitItemType itemType = transfer.itemType; diff --git a/test/foundry/conduit/ConduitExecute.t.sol b/test/foundry/conduit/ConduitExecute.t.sol index 1cc43ab7c..aad0875a2 100644 --- a/test/foundry/conduit/ConduitExecute.t.sol +++ b/test/foundry/conduit/ConduitExecute.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; -import { BaseConsiderationTest } from "../utils/BaseConsiderationTest.sol"; -import { ConduitTransfer, ConduitItemType } from "../../../contracts/conduit/lib/ConduitStructs.sol"; +import { + ConduitTransfer, + ConduitItemType +} from "../../../contracts/conduit/lib/ConduitStructs.sol"; import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; -import { ERC721Recipient } from "../utils/ERC721Recipient.sol"; -import { ERC1155Recipient } from "../utils/ERC1155Recipient.sol"; import { BaseConduitTest } from "./BaseConduitTest.sol"; import { Conduit } from "../../../contracts/conduit/Conduit.sol"; @@ -21,9 +21,10 @@ contract ConduitExecuteTest is BaseConduitTest { ConduitTransfer[] transfers; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } diff --git a/test/foundry/conduit/ConduitExecuteBatch1155.t.sol b/test/foundry/conduit/ConduitExecuteBatch1155.t.sol index 8d41f7259..aadc3fcf4 100644 --- a/test/foundry/conduit/ConduitExecuteBatch1155.t.sol +++ b/test/foundry/conduit/ConduitExecuteBatch1155.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; -import { BaseConsiderationTest } from "../utils/BaseConsiderationTest.sol"; -import { ConduitTransfer, ConduitBatch1155Transfer, ConduitItemType } from "../../../contracts/conduit/lib/ConduitStructs.sol"; +import { + ConduitBatch1155Transfer +} from "../../../contracts/conduit/lib/ConduitStructs.sol"; import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; -import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; -import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; -import { ERC721Recipient } from "../utils/ERC721Recipient.sol"; -import { ERC1155Recipient } from "../utils/ERC1155Recipient.sol"; import { BaseConduitTest } from "./BaseConduitTest.sol"; import { Conduit } from "../../../contracts/conduit/Conduit.sol"; @@ -21,9 +18,10 @@ contract ConduitExecuteBatch1155Test is BaseConduitTest { ConduitBatch1155Transfer[] batchTransfers; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } diff --git a/test/foundry/conduit/ConduitExecuteWithBatch1155.t.sol b/test/foundry/conduit/ConduitExecuteWithBatch1155.t.sol index 3d2c9ed93..d65b431a0 100644 --- a/test/foundry/conduit/ConduitExecuteWithBatch1155.t.sol +++ b/test/foundry/conduit/ConduitExecuteWithBatch1155.t.sol @@ -1,16 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; import { Conduit } from "../../../contracts/conduit/Conduit.sol"; -import { ConduitController } from "../../../contracts/conduit/ConduitController.sol"; import { BaseConduitTest } from "./BaseConduitTest.sol"; -import { ConduitTransfer, ConduitBatch1155Transfer, ConduitItemType } from "../../../contracts/conduit/lib/ConduitStructs.sol"; +import { + ConduitTransfer, + ConduitBatch1155Transfer, + ConduitItemType +} from "../../../contracts/conduit/lib/ConduitStructs.sol"; import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; -import { ERC721Recipient } from "../utils/ERC721Recipient.sol"; -import { ERC1155Recipient } from "../utils/ERC1155Recipient.sol"; contract ConduitExecuteWithBatch1155Test is BaseConduitTest { struct FuzzInputs { @@ -24,9 +25,10 @@ contract ConduitExecuteWithBatch1155Test is BaseConduitTest { ConduitBatch1155Transfer[] batchTransfers; } - function test(function(Context memory) external fn, Context memory context) - internal - { + function test( + function(Context memory) external fn, + Context memory context + ) internal { try fn(context) {} catch (bytes memory reason) { assertPass(reason); } diff --git a/test/foundry/interfaces/OwnableDelegateProxy.sol b/test/foundry/interfaces/OwnableDelegateProxy.sol index c4cb97ecd..9f0101f1c 100644 --- a/test/foundry/interfaces/OwnableDelegateProxy.sol +++ b/test/foundry/interfaces/OwnableDelegateProxy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; interface OwnableDelegateProxy { function name() external returns (string memory); diff --git a/test/foundry/interfaces/ProxyRegistry.sol b/test/foundry/interfaces/ProxyRegistry.sol index 90fa938bc..c85ec1a11 100644 --- a/test/foundry/interfaces/ProxyRegistry.sol +++ b/test/foundry/interfaces/ProxyRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; import { OwnableDelegateProxy } from "./OwnableDelegateProxy.sol"; @@ -8,8 +8,7 @@ interface ProxyRegistry { function registerProxy() external returns (OwnableDelegateProxy); - function proxies(address _addr) - external - view - returns (OwnableDelegateProxy); + function proxies( + address _addr + ) external view returns (OwnableDelegateProxy); } diff --git a/test/foundry/offerers/AdjustedAmountOfferer.t.sol b/test/foundry/offerers/AdjustedAmountOfferer.t.sol new file mode 100644 index 000000000..9b3bb4e1c --- /dev/null +++ b/test/foundry/offerers/AdjustedAmountOfferer.t.sol @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { AdjustedAmountOfferer } from "./impl/AdjustedAmountOfferer.sol"; + +import { + ERC20Interface +} from "../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + OrderType, + ItemType +} from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationItem, + AdvancedOrder, + CriteriaResolver +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationEventsAndErrors +} from "../../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; + +import { + ZoneInteractionErrors +} from "../../../contracts/interfaces/ZoneInteractionErrors.sol"; + +contract AdjustedAmountOffererTest is + BaseOrderTest, + ConsiderationEventsAndErrors, + ZoneInteractionErrors +{ + AdjustedAmountOfferer offerer; + CriteriaResolver[] criteriaResolvers; + + struct Context { + ConsiderationInterface seaport; + } + + function setUp() public virtual override { + super.setUp(); + token1.mint(address(this), 100000); + token2.mint(address(this), 100000); + } + + function setUpOfferer( + int256 offerAdjust, + int256 considerationAdjust + ) internal { + address[] memory seaports = new address[](2); + seaports[0] = address(consideration); + seaports[1] = address(referenceConsideration); + offerer = new AdjustedAmountOfferer( + seaports, + ERC20Interface(address(token1)), + ERC20Interface(address(token2)), + offerAdjust, + considerationAdjust + ); + token1.mint(address(offerer), 100000); + token2.mint(address(offerer), 100000); + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function setUpNormalOrder(address recipient) public { + // add normal offer item identifier 2 + addOfferItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: 1000 + }); + // add consideration item to address(test) for 1000 of token1 + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(token2), + identifierOrCriteria: 0, + startAmount: 1000, + endAmount: 1000, + recipient: payable(recipient) + }) + ); + } + + function testLessMinimumReceived() public { + setUpLessMinimumReceived(); + + test(this.execLessMinimumReceived, Context({ seaport: consideration })); + test( + this.execLessMinimumReceived, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpLessMinimumReceived() internal { + setUpOfferer(-1, 0); + setUpNormalOrder(address(offerer)); + } + + function execLessMinimumReceived( + Context memory context + ) external stateless { + vm.expectRevert( + abi.encodeWithSelector( + InvalidContractOrder.selector, + uint256(uint160(address(offerer))) << 96 + ) + ); + fulfillAdvanced(context, configureAdvancedOrder()); + } + + // testMoreMinimumReceived: same as above but specify setUpOfferer(1, 0) + function testMoreMinimumReceived() public { + setUpMoreMinimumReceived(); + + test(this.execMoreMinimumReceived, Context({ seaport: consideration })); + test( + this.execMoreMinimumReceived, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpMoreMinimumReceived() internal { + setUpOfferer(1, 0); + setUpNormalOrder(address(offerer)); + } + + function execMoreMinimumReceived( + Context memory context + ) external stateless { + uint256 startBalance = token2.balanceOf(address(this)); + fulfillAdvanced(context, configureAdvancedOrder()); + assertEq(token1.balanceOf(address(this)), startBalance + 1001); + } + + // do the same as above but now for consideration items, specifying -1 and 1 as the second arguments to setUpOfferer + + function testLessMaximumSpent() public { + setUpLessMaximumSpent(); + + test(this.execLessMaximumSpent, Context({ seaport: consideration })); + test( + this.execLessMaximumSpent, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpLessMaximumSpent() internal { + setUpOfferer(0, -1); + setUpNormalOrder(address(offerer)); + } + + function execLessMaximumSpent(Context memory context) external stateless { + uint256 startBalance = token1.balanceOf(address(this)); + + fulfillAdvanced(context, configureAdvancedOrder()); + assertEq(token2.balanceOf(address(this)), startBalance - 999); + } + + function testMoreMaximumSpent() public { + setUpMoreMaximumSpent(); + + test(this.execMoreMaximumSpent, Context({ seaport: consideration })); + test( + this.execMoreMaximumSpent, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpMoreMaximumSpent() internal { + setUpOfferer(0, 1); + setUpNormalOrder(address(offerer)); + } + + function execMoreMaximumSpent(Context memory context) external stateless { + vm.expectRevert( + abi.encodeWithSelector( + InvalidContractOrder.selector, + uint256(uint160(address(offerer))) << 96 + ) + ); + fulfillAdvanced(context, configureAdvancedOrder()); + } + + // make sure altering offer item start/end amount results in falure + + function testAlterOfferItem() public { + setUpOfferer(0, 0); + setUpNormalOrder(address(offerer)); + offerItems[0].endAmount += 1; + + test(this.execAlterOfferItem, Context({ seaport: consideration })); + test( + this.execAlterOfferItem, + Context({ seaport: referenceConsideration }) + ); + } + + function execAlterOfferItem(Context memory context) external stateless { + vm.expectRevert( + abi.encodeWithSelector( + InvalidContractOrder.selector, + uint256(uint160(address(offerer))) << 96 + ) + ); + fulfillAdvanced(context, configureAdvancedOrder()); + } + + // make sure altering consideration item start/end amount results in falure + function testAlterConsiderationItem() public { + setUpOfferer(0, 0); + setUpNormalOrder(address(offerer)); + considerationItems[0].endAmount += 1; + + test( + this.execAlterConsiderationItem, + Context({ seaport: consideration }) + ); + test( + this.execAlterConsiderationItem, + Context({ seaport: referenceConsideration }) + ); + } + + function execAlterConsiderationItem( + Context memory context + ) external stateless { + vm.expectRevert( + abi.encodeWithSelector( + InvalidContractOrder.selector, + uint256(uint160(address(offerer))) << 96 + ) + ); + fulfillAdvanced(context, configureAdvancedOrder()); + } + + function configureAdvancedOrder() internal returns (AdvancedOrder memory) { + return configureAdvancedOrder(1, 1); + } + + function configureAdvancedOrder( + uint120 numer, + uint120 denom + ) internal returns (AdvancedOrder memory) { + return + AdvancedOrder({ + parameters: getOrderParameters( + address(offerer), + OrderType.CONTRACT + ), + numerator: numer, + denominator: denom, + signature: "", + extraData: "" + }); + } + + function fulfillAdvanced( + Context memory context, + AdvancedOrder memory advancedOrder + ) internal { + context.seaport.fulfillAdvancedOrder({ + advancedOrder: advancedOrder, + fulfillerConduitKey: bytes32(0), + criteriaResolvers: criteriaResolvers, + recipient: address(0) + }); + } +} diff --git a/test/foundry/offerers/BadOfferer.t.sol b/test/foundry/offerers/BadOfferer.t.sol new file mode 100644 index 000000000..bbdb35e26 --- /dev/null +++ b/test/foundry/offerers/BadOfferer.t.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { BadOfferer } from "./impl/BadOfferer.sol"; + +import { + ERC20Interface, + ERC721Interface +} from "../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + OfferItem, + ConsiderationItem, + AdvancedOrder, + CriteriaResolver, + OrderParameters, + FulfillmentComponent +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ItemType, + OrderType +} from "../../../contracts/lib/ConsiderationEnums.sol"; + +contract BadOffererTest is BaseOrderTest { + BadOfferer badOfferer; + + struct Context { + ConsiderationInterface seaport; + uint256 id; + bool eoa; + } + + function setUp() public override { + super.setUp(); + token1.mint(address(this), 100000); + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testNormalOrder() public { + uint256 id = 1; + test( + this.execOrderWithContext, + Context({ seaport: consideration, id: id, eoa: false }) + ); + test( + this.execOrderWithContext, + Context({ seaport: referenceConsideration, id: id, eoa: false }) + ); + } + + function testOrderNothing() public { + uint256 id = 2; + test( + this.execOrderWithContext, + Context({ seaport: consideration, id: id, eoa: false }) + ); + test( + this.execOrderWithContext, + Context({ seaport: referenceConsideration, id: id, eoa: false }) + ); + } + + function testOrderRevert() public { + uint256 id = 3; + test( + this.execOrderWithContext, + Context({ seaport: consideration, id: id, eoa: false }) + ); + test( + this.execOrderWithContext, + Context({ seaport: referenceConsideration, id: id, eoa: false }) + ); + } + + function testOrderGarbage() public { + uint256 id = 4; + test( + this.execOrderWithContext, + Context({ seaport: consideration, id: id, eoa: false }) + ); + test( + this.execOrderWithContext, + Context({ seaport: referenceConsideration, id: id, eoa: false }) + ); + } + + function testOrderEoa() public { + uint256 id = 1; + test( + this.execOrderWithContext, + Context({ seaport: consideration, id: id, eoa: true }) + ); + test( + this.execOrderWithContext, + Context({ seaport: referenceConsideration, id: id, eoa: true }) + ); + } + + function execOrderWithContext(Context memory context) external stateless { + if (!context.eoa) { + badOfferer = new BadOfferer( + address(context.seaport), + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)) + ); + } else { + badOfferer = BadOfferer(makeAddr("eoa")); + } + + AdvancedOrder memory badOrder = configureBadOffererOrder(context.id); + AdvancedOrder memory normalOrder = configureNormalOrder(context); + + configureFulfillmentComponents(); + + AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2); + advancedOrders[0] = badOrder; + advancedOrders[1] = normalOrder; + CriteriaResolver[] memory resolvers; + + context.seaport.fulfillAvailableAdvancedOrders({ + advancedOrders: advancedOrders, + criteriaResolvers: resolvers, + offerFulfillments: offerComponentsArray, + considerationFulfillments: considerationComponentsArray, + fulfillerConduitKey: bytes32(0), + recipient: address(0), + maximumFulfilled: 2 + }); + } + + function configureFulfillmentComponents() internal { + addSingleFulfillmentComponentsTo( + FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }), + offerComponentsArray + ); + addSingleFulfillmentComponentsTo( + FulfillmentComponent({ orderIndex: 1, itemIndex: 0 }), + offerComponentsArray + ); + addSingleFulfillmentComponentsTo( + FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }), + considerationComponentsArray + ); + addSingleFulfillmentComponentsTo( + FulfillmentComponent({ orderIndex: 1, itemIndex: 0 }), + considerationComponentsArray + ); + } + + function configureBadOffererOrder( + uint256 id + ) internal returns (AdvancedOrder memory advancedOrder) { + test721_1.mint(address(this), id); + + OfferItem[] memory offer = new OfferItem[](1); + offer[0] = OfferItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1 + }); + ConsiderationItem[] memory cons = new ConsiderationItem[](1); + cons[0] = ConsiderationItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifierOrCriteria: id, + startAmount: 1, + endAmount: 1, + recipient: payable(address(badOfferer)) + }); + OrderParameters memory orderParameters = OrderParameters({ + offerer: address(badOfferer), + zone: address(0), + offer: offer, + consideration: cons, + orderType: OrderType.CONTRACT, + startTime: 1, + endTime: 101, + zoneHash: bytes32(0), + salt: 0, + conduitKey: bytes32(0), + totalOriginalConsiderationItems: 1 + }); + advancedOrder = AdvancedOrder({ + parameters: orderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + } + + function configureNormalOrder( + Context memory context + ) internal returns (AdvancedOrder memory advancedOrder) { + (address offerer, uint256 pkey) = makeAddrAndKey("normal offerer"); + vm.prank(offerer); + test721_1.setApprovalForAll(address(context.seaport), true); + test721_1.mint(offerer, 201); + addErc20ConsiderationItem(payable(offerer), 100); + addErc721OfferItem(201); + configureOrderParameters(offerer); + configureOrderComponents(0); + bytes32 orderHash = context.seaport.getOrderHash(baseOrderComponents); + bytes memory signature = signOrder(context.seaport, pkey, orderHash); + + advancedOrder = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: signature, + extraData: "" + }); + } +} diff --git a/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol b/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol new file mode 100644 index 000000000..038d3829d --- /dev/null +++ b/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +import "forge-std/Test.sol"; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { DifferentialTest } from "../utils/DifferentialTest.sol"; + +import { + ERC20Interface, + ERC721Interface +} from "../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + ItemType, + OrderType +} from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { ItemType } from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + AdvancedOrder, + OrderParameters, + OrderComponents, + CriteriaResolver, + SpentItem, + ReceivedItem +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ContractOffererInterface +} from "../../../contracts/interfaces/ContractOffererInterface.sol"; + +import { + TestContractOffererNativeToken +} from "../../../contracts/test/TestContractOffererNativeToken.sol"; + +import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; + +import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; + +contract ContractOffersNativeTokenOfferItems is + DifferentialTest, + BaseOrderTest +{ + struct FuzzArgs { + uint256 ethAmount; + uint256 nftId; + } + + struct Context { + ConsiderationInterface seaport; + FuzzArgs args; + } + + modifier validateInputs(FuzzArgs memory args) { + vm.assume(args.ethAmount > 0 && args.ethAmount < 100000); + _; + } + + TestERC721 erc721; + + function setUp() public override { + super.setUp(); + erc721 = new TestERC721(); + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testEthForErc721( + FuzzArgs memory args + ) public validateInputs(args) { + test( + this.ethForErc721, + Context({ + seaport: consideration, + args: FuzzArgs({ ethAmount: 1, nftId: 1 }) + }) + ); + test( + this.ethForErc721, + Context({ seaport: referenceConsideration, args: args }) + ); + } + + function ethForErc721(Context memory context) public stateless { + TestContractOffererNativeToken contractOfferer = new TestContractOffererNativeToken( + address(context.seaport) + ); + vm.deal(address(contractOfferer), UINT256_MAX); + + test721_1.setApprovalForAll(address(contractOfferer), true); + test721_1.mint(address(this), context.args.nftId); + + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: context.args.nftId, + amount: 1 + }); + + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem({ + itemType: ItemType.NATIVE, + token: address(0), + identifier: 0, + amount: context.args.ethAmount + }); + + addEthOfferItem(context.args.ethAmount); + addErc721ConsiderationItem( + payable(address(contractOfferer)), + context.args.nftId + ); + + OrderParameters memory orderParameters = OrderParameters( + address(contractOfferer), + address(0), + offerItems, + considerationItems, + OrderType.CONTRACT, + block.timestamp, + block.timestamp + 1000, + bytes32(0), + 0, + bytes32(0), + considerationItems.length + ); + + AdvancedOrder memory advancedOrder = AdvancedOrder( + orderParameters, + 1, + 1, + "", + "" + ); + + uint256 originalBalance = address(this).balance; + + context.seaport.fulfillAdvancedOrder( + advancedOrder, + new CriteriaResolver[](0), + bytes32(0), + address(0) + ); + + assertEq( + context.args.ethAmount, + address(this).balance - originalBalance + ); + assertEq( + address(contractOfferer), + test721_1.ownerOf(context.args.nftId) + ); + } +} diff --git a/test/foundry/offerers/OffererCriteriaAdvanced.t.sol b/test/foundry/offerers/OffererCriteriaAdvanced.t.sol new file mode 100644 index 000000000..35dcd6d93 --- /dev/null +++ b/test/foundry/offerers/OffererCriteriaAdvanced.t.sol @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { PassthroughOfferer } from "./impl/PassthroughOfferer.sol"; + +import { Merkle } from "murky/Merkle.sol"; + +import { + ERC20Interface, + ERC721Interface +} from "../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + OrderType, + ItemType, + Side +} from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationItem, + OfferItem, + AdvancedOrder, + CriteriaResolver +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationEventsAndErrors +} from "../../../contracts/interfaces/ConsiderationEventsAndErrors.sol"; + +import { + ZoneInteractionErrors +} from "../../../contracts/interfaces/ZoneInteractionErrors.sol"; + +contract OffererCriteriaAdvancedTest is + BaseOrderTest, + ConsiderationEventsAndErrors, + ZoneInteractionErrors +{ + PassthroughOfferer offerer; + CriteriaResolver[] criteriaResolvers; + + struct Context { + ConsiderationInterface seaport; + } + + function setUp() public virtual override { + super.setUp(); + token1.mint(address(this), 100000); + test721_1.mint(address(this), 1); + address[] memory seaports = new address[](2); + seaports[0] = address(consideration); + seaports[1] = address(referenceConsideration); + offerer = new PassthroughOfferer( + seaports, + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)) + ); + token1.mint(address(offerer), 100000); + test721_1.mint(address(offerer), 2); + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testOnlyWholeFractional1() public { + setUpOnlyWholeFractional(); + + test( + this.execOnlyWholeFractional1, + Context({ seaport: consideration }) + ); + test( + this.execOnlyWholeFractional1, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpOnlyWholeFractional() public { + setUpNormalOrder(address(offerer)); + } + + function setUpNormalOrder(address recipient) public { + // add normal offer item identifier 2 + addOfferItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 2, + amount: 1 + }); + // add consideration item to address(test) for 1000 of token1 + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 1000, + endAmount: 1000, + recipient: payable(recipient) + }) + ); + } + + function execOnlyWholeFractional1( + Context memory context + ) external stateless { + fulfillAdvanced(context, configureAdvancedOrder()); + assertEq(test721_1.ownerOf(2), address(this)); + } + + // same as above but test a 0/n fraction + function testOnlyWholeFractional0() public { + setUpOnlyWholeFractional(); + + test( + this.execOnlyWholeFractional0, + Context({ seaport: consideration }) + ); + test( + this.execOnlyWholeFractional0, + Context({ seaport: referenceConsideration }) + ); + } + + function execOnlyWholeFractional0( + Context memory context + ) external stateless { + vm.expectRevert(BadFraction.selector); + fulfillAdvanced(context, configureAdvancedOrder(0, 1)); + } + + // same as above but test a n/n fraction (2/2) + function testOnlyWholeFractional2() public { + setUpOnlyWholeFractional(); + + test( + this.execOnlyWholeFractional2, + Context({ seaport: consideration }) + ); + test( + this.execOnlyWholeFractional2, + Context({ seaport: referenceConsideration }) + ); + } + + function execOnlyWholeFractional2( + Context memory context + ) external stateless { + vm.expectRevert(BadFraction.selector); + fulfillAdvanced(context, configureAdvancedOrder(2, 2)); + } + + function setUpOnlyWholeFractional0() public { + // add normal offer item identifier 2 + addOfferItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 2, + amount: 1 + }); + // add consideration item to address(test) for 1000 of token1 + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 0, + endAmount: 0, + recipient: payable(address(offerer)) + }) + ); + } + + function testZeroReceiverWildcard() public { + setUpNormalOrder(address(0)); + + test( + this.execZeroReceiverWildcard, + Context({ seaport: consideration }) + ); + test( + this.execZeroReceiverWildcard, + Context({ seaport: referenceConsideration }) + ); + } + + function execZeroReceiverWildcard( + Context memory context + ) external stateless { + fulfillAdvanced(context, configureAdvancedOrder(1, 1)); + assertEq(test721_1.ownerOf(2), address(this)); + } + + function testMismatchedReceiver() public { + setUpNormalOrder(makeAddr("not offerer")); + + test(this.execMismatchedReceiver, Context({ seaport: consideration })); + test( + this.execMismatchedReceiver, + Context({ seaport: referenceConsideration }) + ); + } + + function execMismatchedReceiver(Context memory context) external stateless { + vm.expectRevert( + abi.encodeWithSelector( + InvalidContractOrder.selector, + (uint256(uint160(address(offerer)))) << 96 + ) + ); + fulfillAdvanced(context, configureAdvancedOrder(1, 1)); + } + + function testCriteriaMinimumReceived() public { + setUpCriteriaMinimumReceived(); + + test( + this.execCriteriaMinimumReceived, + Context({ seaport: consideration }) + ); + test( + this.execCriteriaMinimumReceived, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpCriteriaMinimumReceived() internal { + (bytes32 root, bytes32[] memory proof) = getRootAndProof(2); + // addOfferItem type ERC721_WITH_CRITERIA, criteria equal to root of merkle tree + addOfferItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifier: uint256(root), + amount: 1 + }); + // add consideration item to address(test) for 1000 of token1 + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 1000, + endAmount: 1000, + recipient: payable(address(offerer)) + }) + ); + + criteriaResolvers.push( + CriteriaResolver({ + orderIndex: 0, + side: Side.OFFER, + index: 0, + identifier: 2, + criteriaProof: proof + }) + ); + } + + function execCriteriaMinimumReceived( + Context memory context + ) external stateless { + fulfillAdvanced(context, configureAdvancedOrder()); + assertEq(test721_1.ownerOf(2), address(this)); + } + + function testCriteriaMaximumSpent() public { + setUpCriteriaMaximumSpent(); + + test( + this.execCriteriaMaximumSpent, + Context({ seaport: consideration }) + ); + test( + this.execCriteriaMaximumSpent, + Context({ seaport: referenceConsideration }) + ); + } + + function setUpCriteriaMaximumSpent() internal { + (bytes32 root, bytes32[] memory proof) = getRootAndProof(1); + // addOfferItem type ERC20, amount 1000 + addOfferItem( + OfferItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifierOrCriteria: 0, + startAmount: 1000, + endAmount: 1000 + }) + ); + // addConsiderationItem type ERC721_WITH_CRITERIA, criteria equal to root of merkle tree, recipient offerer + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifierOrCriteria: uint256(root), + startAmount: 1, + endAmount: 1, + recipient: payable(address(offerer)) + }) + ); + + // add criteria resolver for side consideration, orderIndex 0, index 0, identifier 2, proof + criteriaResolvers.push( + CriteriaResolver({ + orderIndex: 0, + side: Side.CONSIDERATION, + index: 0, + identifier: 1, + criteriaProof: proof + }) + ); + } + + function execCriteriaMaximumSpent( + Context memory context + ) external stateless { + fulfillAdvanced(context, configureAdvancedOrder()); + assertEq(test721_1.ownerOf(1), address(offerer)); + } + + function configureAdvancedOrder() internal returns (AdvancedOrder memory) { + return configureAdvancedOrder(1, 1); + } + + function configureAdvancedOrder( + uint120 numer, + uint120 denom + ) internal returns (AdvancedOrder memory) { + return + AdvancedOrder({ + parameters: getOrderParameters( + address(offerer), + OrderType.CONTRACT + ), + numerator: numer, + denominator: denom, + signature: "", + extraData: "" + }); + } + + function fulfillAdvanced( + Context memory context, + AdvancedOrder memory advancedOrder + ) internal { + context.seaport.fulfillAdvancedOrder({ + advancedOrder: advancedOrder, + fulfillerConduitKey: bytes32(0), + criteriaResolvers: criteriaResolvers, + recipient: address(0) + }); + } + + function getRootAndProof( + uint256 identifier + ) internal returns (bytes32 root, bytes32[] memory proof) { + Merkle tree = new Merkle(); + bytes32[] memory leaves = generateLeaves(); + root = tree.getRoot(leaves); + proof = tree.getProof(leaves, identifier); + return (root, proof); + } + + /** + * @dev Generate hashed leaves for identifiers [0,50) for insertion into a Merkle tree. + */ + function generateLeaves() internal pure returns (bytes32[] memory) { + uint256[] memory leaves = new uint256[](50); + for (uint256 i = 0; i < leaves.length; ++i) { + leaves[i] = i; + } + return toHashedLeaves(leaves); + } +} diff --git a/test/foundry/offerers/StatefulOfferer.t.sol b/test/foundry/offerers/StatefulOfferer.t.sol new file mode 100644 index 000000000..d3b599914 --- /dev/null +++ b/test/foundry/offerers/StatefulOfferer.t.sol @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { StatefulRatifierOfferer } from "./impl/StatefulRatifierOfferer.sol"; + +import { + ERC20Interface, + ERC721Interface +} from "../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { + OfferItem, + ConsiderationItem, + AdvancedOrder, + CriteriaResolver, + OrderComponents, + FulfillmentComponent +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { OrderType } from "../../../contracts/lib/ConsiderationEnums.sol"; + +contract StatefulOffererTest is BaseOrderTest { + StatefulRatifierOfferer offerer; + + struct Context { + ConsiderationInterface consideration; + uint8 numToAdd; + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) {} catch (bytes memory reason) { + assertPass(reason); + } + } + + function testFulfillAdvanced() public { + test( + this.execFulfillAdvanced, + Context({ consideration: consideration, numToAdd: 0 }) + ); + test( + this.execFulfillAdvanced, + Context({ consideration: referenceConsideration, numToAdd: 0 }) + ); + } + + function execFulfillAdvanced(Context memory context) public stateless { + offerer = new StatefulRatifierOfferer( + address(context.consideration), + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)), + 1 + ); + addErc20OfferItem(1); + addErc721ConsiderationItem(payable(address(offerer)), 42); + addErc721ConsiderationItem(payable(address(offerer)), 43); + addErc721ConsiderationItem(payable(address(offerer)), 44); + + test721_1.mint(address(this), 42); + test721_1.mint(address(this), 43); + test721_1.mint(address(this), 44); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(0); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "context" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + context.consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertTrue(offerer.called()); + } + + function testCancelAdvancedRevert() public { + test( + this.execCancelAdvancedRevert, + Context({ consideration: consideration, numToAdd: 0 }) + ); + test( + this.execCancelAdvancedRevert, + Context({ consideration: referenceConsideration, numToAdd: 0 }) + ); + } + + function execCancelAdvancedRevert(Context memory context) public stateless { + offerer = new StatefulRatifierOfferer( + address(context.consideration), + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)), + 1 + ); + addErc20OfferItem(1); + addErc721ConsiderationItem(payable(address(offerer)), 42); + + test721_1.mint(address(this), 42); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(0); + + OrderComponents[] memory myBaseOrderComponents = new OrderComponents[]( + 1 + ); + myBaseOrderComponents[0] = baseOrderComponents; + + vm.prank(address(offerer)); + // Contract orders cannot be cancelled. + vm.expectRevert(abi.encodeWithSignature("CannotCancelOrder()")); + context.consideration.cancel(myBaseOrderComponents); + } + + function testFulfillAdvancedFuzz(uint8 numToAdd) public { + numToAdd = uint8(bound(numToAdd, 1, 255)); + test( + this.execFulfillAdvancedFuzz, + Context({ consideration: consideration, numToAdd: numToAdd }) + ); + test( + this.execFulfillAdvancedFuzz, + Context({ + consideration: referenceConsideration, + numToAdd: numToAdd + }) + ); + } + + function execFulfillAdvancedFuzz(Context memory context) public stateless { + offerer = new StatefulRatifierOfferer( + address(context.consideration), + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)), + context.numToAdd + ); + addErc20OfferItem(1); + addErc721ConsiderationItem(payable(address(offerer)), 42); + addErc721ConsiderationItem(payable(address(offerer)), 43); + addErc721ConsiderationItem(payable(address(offerer)), 44); + + test721_1.mint(address(this), 42); + test721_1.mint(address(this), 43); + test721_1.mint(address(this), 44); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(0); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "context" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + context.consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertTrue(offerer.called()); + } + + function testMatchAdvancedOrders() public { + test( + this.execMatchAdvancedOrders, + Context({ consideration: consideration, numToAdd: 0 }) + ); + test( + this.execMatchAdvancedOrders, + Context({ consideration: referenceConsideration, numToAdd: 0 }) + ); + } + + function execMatchAdvancedOrders( + Context memory context + ) external stateless { + offerer = new StatefulRatifierOfferer( + address(context.consideration), + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)), + 1 + ); + addErc20OfferItem(1); + addErc721ConsiderationItem(payable(address(offerer)), 42); + addErc721ConsiderationItem(payable(address(offerer)), 43); + addErc721ConsiderationItem(payable(address(offerer)), 44); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(0); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "context" + }); + + AdvancedOrder memory mirror = createMirrorContractOffererOrder( + context, + "mirroroooor", + order + ); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + AdvancedOrder[] memory orders = new AdvancedOrder[](2); + orders[0] = order; + orders[1] = mirror; + + //match first order offer to second order consideration + createFulfillmentFromComponentsAndAddToFulfillments({ + _offer: FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }), + _consideration: FulfillmentComponent({ + orderIndex: 1, + itemIndex: 0 + }) + }); + // match second order first offer to first order first consideration + createFulfillmentFromComponentsAndAddToFulfillments({ + _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: 0 }), + _consideration: FulfillmentComponent({ + orderIndex: 0, + itemIndex: 0 + }) + }); + // match second order second offer to first order second consideration + createFulfillmentFromComponentsAndAddToFulfillments({ + _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: 1 }), + _consideration: FulfillmentComponent({ + orderIndex: 0, + itemIndex: 1 + }) + }); + // match second order third offer to first order third consideration + createFulfillmentFromComponentsAndAddToFulfillments({ + _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: 2 }), + _consideration: FulfillmentComponent({ + orderIndex: 0, + itemIndex: 2 + }) + }); + + context.consideration.matchAdvancedOrders({ + orders: orders, + criteriaResolvers: criteriaResolvers, + fulfillments: fulfillments, + recipient: address(0) + }); + assertTrue(offerer.called()); + } + + function testFulfillAvailableAdvancedOrders() public { + test( + this.execFulfillAvailableAdvancedOrders, + Context({ consideration: consideration, numToAdd: 0 }) + ); + test( + this.execFulfillAvailableAdvancedOrders, + Context({ consideration: referenceConsideration, numToAdd: 0 }) + ); + } + + function execFulfillAvailableAdvancedOrders( + Context memory context + ) external stateless { + offerer = new StatefulRatifierOfferer( + address(context.consideration), + ERC20Interface(address(token1)), + ERC721Interface(address(test721_1)), + 1 + ); + addErc20OfferItem(1); + addErc721ConsiderationItem(payable(address(offerer)), 42); + addErc721ConsiderationItem(payable(address(offerer)), 43); + addErc721ConsiderationItem(payable(address(offerer)), 44); + + test721_1.mint(address(this), 42); + test721_1.mint(address(this), 43); + test721_1.mint(address(this), 44); + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(0); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "context" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + AdvancedOrder[] memory orders = new AdvancedOrder[](1); + orders[0] = order; + offerComponents.push( + FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }) + ); + offerComponentsArray.push(offerComponents); + + considerationComponents.push( + FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }) + ); + considerationComponentsArray.push(considerationComponents); + delete considerationComponents; + considerationComponents.push( + FulfillmentComponent({ orderIndex: 0, itemIndex: 1 }) + ); + considerationComponentsArray.push(considerationComponents); + delete considerationComponents; + considerationComponents.push( + FulfillmentComponent({ orderIndex: 0, itemIndex: 2 }) + ); + considerationComponentsArray.push(considerationComponents); + + context.consideration.fulfillAvailableAdvancedOrders({ + advancedOrders: orders, + criteriaResolvers: criteriaResolvers, + offerFulfillments: offerComponentsArray, + considerationFulfillments: considerationComponentsArray, + fulfillerConduitKey: bytes32(0), + recipient: address(0), + maximumFulfilled: 1 + }); + assertTrue(offerer.called()); + } + + function createMirrorContractOffererOrder( + Context memory context, + string memory _offerer, + AdvancedOrder memory advancedOrder + ) internal returns (AdvancedOrder memory) { + delete offerItems; + delete considerationItems; + + (address _offererAddr, uint256 pkey) = makeAddrAndKey(_offerer); + test721_1.mint(address(_offererAddr), 42); + test721_1.mint(address(_offererAddr), 43); + test721_1.mint(address(_offererAddr), 44); + + vm.prank(_offererAddr); + test721_1.setApprovalForAll(address(context.consideration), true); + + for (uint256 i; i < advancedOrder.parameters.offer.length; i++) { + OfferItem memory _offerItem = advancedOrder.parameters.offer[i]; + + addConsiderationItem({ + itemType: _offerItem.itemType, + token: _offerItem.token, + identifier: _offerItem.identifierOrCriteria, + startAmount: _offerItem.startAmount, + endAmount: _offerItem.endAmount, + recipient: payable(_offererAddr) + }); + } + // do the same for considerationItem -> offerItem + for ( + uint256 i; + i < advancedOrder.parameters.consideration.length; + i++ + ) { + ConsiderationItem memory _considerationItem = advancedOrder + .parameters + .consideration[i]; + + addOfferItem({ + itemType: _considerationItem.itemType, + token: _considerationItem.token, + identifier: _considerationItem.identifierOrCriteria, + startAmount: _considerationItem.startAmount, + endAmount: _considerationItem.endAmount + }); + } + + _configureOrderParameters({ + offerer: _offererAddr, + zone: advancedOrder.parameters.zone, + zoneHash: advancedOrder.parameters.zoneHash, + salt: advancedOrder.parameters.salt, + useConduit: false + }); + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + pkey, + orderHash + ); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: advancedOrder.denominator, + denominator: advancedOrder.numerator, + signature: signature, + extraData: "" + }); + + return order; + } +} diff --git a/test/foundry/offerers/TestPoolOffererImpl.t.sol b/test/foundry/offerers/TestPoolOffererImpl.t.sol new file mode 100644 index 000000000..f5ce478ae --- /dev/null +++ b/test/foundry/offerers/TestPoolOffererImpl.t.sol @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +import { Test } from "forge-std/Test.sol"; + +import { ItemType } from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + SpentItem, + ReceivedItem +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + EnumerableSet +} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol"; + +import { + IERC721 +} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; + +import { + IERC20 +} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; + +import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; + +import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; + +import { TestPoolOfferer } from "./impl/TestPoolOfferer.sol"; + +contract TestPoolFactoryImpl { + address immutable seaport; + + constructor(address _seaport) { + seaport = _seaport; + } + + function createPoolOfferer( + address erc721, + uint256[] calldata tokenIds, + address erc20, + uint256 amount + ) external returns (address newPool) { + newPool = address( + new TestPoolImpl( + seaport, + erc721, + tokenIds, + erc20, + amount, + msg.sender + ) + ); + IERC20(erc20).transferFrom(msg.sender, newPool, amount); + for (uint256 i; i < tokenIds.length; i++) { + IERC721(erc721).transferFrom(msg.sender, newPool, tokenIds[i]); + } + } +} + +contract TestPoolImpl is TestPoolOfferer { + using EnumerableSet for EnumerableSet.UintSet; + + constructor( + address seaport, + address _token, + uint256[] memory _tokenIds, + address _payment, + uint256 amount, + address owner + ) TestPoolOfferer(seaport, _token, _tokenIds, _payment, amount, owner) {} + + function getInternalBalance() external view returns (uint256) { + return balance; + } + + function getInternalTokenBalance() external view returns (uint256) { + return tokenIds.length(); + } + + function inTokenIds(uint256 id) external view returns (bool) { + return tokenIds.contains(id); + } +} + +contract TestPoolOffererImpl is Test { + TestPoolFactoryImpl factory; + TestPoolImpl test; + TestERC20 erc20; + TestERC721 erc721; + address seaport; + + function setUp() public { + seaport = makeAddr("seaport"); + erc20 = new TestERC20(); + erc721 = new TestERC721(); + + factory = new TestPoolFactoryImpl(seaport); + + erc20.mint(address(this), 2e18); + erc721.mint(address(this), 0); + erc721.mint(address(this), 1); + erc721.mint(address(this), 2); + erc721.mint(address(this), 3); + erc721.mint(address(this), 4); + + erc20.approve(seaport, type(uint256).max); + erc20.approve(address(factory), type(uint256).max); + erc721.setApprovalForAll(seaport, true); + erc721.setApprovalForAll(address(factory), true); + + uint256[] memory tokenIds = new uint256[](3); + tokenIds[0] = 0; + tokenIds[1] = 1; + tokenIds[2] = 2; + + test = TestPoolImpl( + factory.createPoolOfferer( + address(erc721), + tokenIds, + address(erc20), + 1e18 + ) + ); + } + + function testInitialized() public { + assertEq(erc20.balanceOf(address(test)), 1e18); + assertEq(erc721.balanceOf(address(test)), 3); + assertEq(erc721.ownerOf(0), address(test)); + assertEq(erc721.ownerOf(1), address(test)); + assertEq(erc721.ownerOf(2), address(test)); + } + + function testPreviewOrder() public { + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 0, + amount: 1 + }); + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.previewOrder( + address(0), + address(this), + minimumReceived, + maximumSpent, + "" + ); + + assertEq(spentItems.length, 1, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[0].amount, 1, "wrong spentitem amount"); + assertEq(spentItems[0].identifier, 0, "wrong spentitem identifier"); + assertEq(spentItems[0].token, address(erc721), "wrong spentitem token"); + assertEq(receivedItems.length, 1, "wrong receivedItems length"); + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(receivedItems[0].identifier, 0, "wrong identifier"); + assertEq(receivedItems[0].amount, 5e17, "wrong amount"); + assertEq(receivedItems[0].token, address(erc20), "wrong token"); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + } + + function testPreviewOrder_wildcard() public { + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(erc721), + identifier: 0, + amount: 1 + }); + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.previewOrder( + address(0), + address(this), + minimumReceived, + maximumSpent, + "" + ); + + assertEq(spentItems.length, 1, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[0].amount, 1, "wrong spentitem amount"); + assertEq(spentItems[0].identifier, 0, "wrong spentitem identifier"); + assertEq(spentItems[0].token, address(erc721), "wrong spentitem token"); + assertEq(receivedItems.length, 1, "wrong receivedItems length"); + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(receivedItems[0].identifier, 0, "wrong identifier"); + assertEq(receivedItems[0].amount, 5e17, "wrong amount"); + assertEq(receivedItems[0].token, address(erc20), "wrong token"); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + } + + function testPreviewOrder2() public { + SpentItem[] memory minimumReceived = new SpentItem[](2); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 0, + amount: 1 + }); + minimumReceived[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 1, + amount: 1 + }); + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.previewOrder( + address(0), + address(this), + minimumReceived, + maximumSpent, + "" + ); + assertEq(spentItems.length, 2, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[0].amount, 1, "wrong spentitem amount"); + assertEq(spentItems[0].identifier, 0, "wrong spentitem identifier"); + assertEq(spentItems[0].token, address(erc721), "wrong spentitem token"); + assertEq( + uint8(spentItems[1].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[1].amount, 1, "wrong spentitem amount"); + assertEq(spentItems[1].identifier, 1, "wrong spentitem identifier"); + assertEq(spentItems[1].token, address(erc721), "wrong spentitem token"); + assertEq(receivedItems.length, 1, "wrong receivedItems length"); + + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(receivedItems[0].identifier, 0, "wrong identifier"); + assertEq(receivedItems[0].amount, 2e18, "wrong amount"); + assertEq(receivedItems[0].token, address(erc20), "wrong token"); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + } + + function testPreviewOrder3() public { + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + + SpentItem[] memory maximumSpent = new SpentItem[](2); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 3, + amount: 1 + }); + maximumSpent[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 4, + amount: 1 + }); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.previewOrder( + address(0), + address(this), + minimumReceived, + maximumSpent, + "" + ); + + assertEq(spentItems.length, 1, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(spentItems[0].identifier, 0, "wrong identifier"); + assertEq(spentItems[0].amount, 4e17, "wrong amount"); + assertEq(spentItems[0].token, address(erc20), "wrong token"); + + assertEq(receivedItems.length, 2, "wrong receivedItems length"); + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(receivedItems[0].amount, 1, "wrong spentitem amount"); + assertEq(receivedItems[0].identifier, 3, "wrong spentitem identifier"); + assertEq( + receivedItems[0].token, + address(erc721), + "wrong spentitem token" + ); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + + assertEq( + uint8(receivedItems[1].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(receivedItems[1].amount, 1, "wrong spentitem amount"); + assertEq(receivedItems[1].identifier, 4, "wrong spentitem identifier"); + assertEq( + receivedItems[1].token, + address(erc721), + "wrong spentitem token" + ); + assertEq(receivedItems[1].recipient, address(test), "wrong receiver"); + } + + function testGenerateOrder() public { + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + + SpentItem[] memory maximumSpent = new SpentItem[](2); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 3, + amount: 1 + }); + maximumSpent[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 4, + amount: 1 + }); + vm.prank(seaport); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.generateOrder( + address(this), + minimumReceived, + maximumSpent, + "" + ); + + assertEq(spentItems.length, 1, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(spentItems[0].identifier, 0, "wrong identifier"); + assertEq(spentItems[0].amount, 4e17, "wrong amount"); + assertEq(spentItems[0].token, address(erc20), "wrong token"); + + assertEq(receivedItems.length, 2, "wrong receivedItems length"); + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(receivedItems[0].amount, 1, "wrong spentitem amount"); + assertEq(receivedItems[0].identifier, 3, "wrong spentitem identifier"); + assertEq( + receivedItems[0].token, + address(erc721), + "wrong spentitem token" + ); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + + assertEq( + uint8(receivedItems[1].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(receivedItems[1].amount, 1, "wrong spentitem amount"); + assertEq(receivedItems[1].identifier, 4, "wrong spentitem identifier"); + assertEq( + receivedItems[1].token, + address(erc721), + "wrong spentitem token" + ); + assertEq(receivedItems[1].recipient, address(test), "wrong receiver"); + + assertEq( + test.getInternalBalance(), + 1e18 - 4e17, + "wrong internal balance" + ); + assertEq( + test.getInternalTokenBalance(), + 5, + "wrong internal token balance" + ); + assertTrue(test.inTokenIds(3), "id not in tokenIds"); + assertTrue(test.inTokenIds(4), "id not in tokenIds"); + } + + function testGenerateOrder2() public { + SpentItem[] memory minimumReceived = new SpentItem[](2); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 0, + amount: 1 + }); + minimumReceived[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(erc721), + identifier: 1, + amount: 1 + }); + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + vm.prank(seaport); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.generateOrder( + address(this), + minimumReceived, + maximumSpent, + "" + ); + + assertEq(spentItems.length, 2, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[0].amount, 1, "wrong spentitem amount"); + assertEq(spentItems[0].identifier, 0, "wrong spentitem identifier"); + assertEq(spentItems[0].token, address(erc721), "wrong spentitem token"); + assertEq( + uint8(spentItems[1].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[1].amount, 1, "wrong spentitem amount"); + assertEq(spentItems[1].identifier, 1, "wrong spentitem identifier"); + assertEq(spentItems[1].token, address(erc721), "wrong spentitem token"); + assertEq(receivedItems.length, 1, "wrong receivedItems length"); + + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(receivedItems[0].identifier, 0, "wrong identifier"); + assertEq(receivedItems[0].amount, 2e18, "wrong amount"); + assertEq(receivedItems[0].token, address(erc20), "wrong token"); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + + assertEq( + test.getInternalBalance(), + 1e18 + 2e18, + "wrong internal balance" + ); + assertEq( + test.getInternalTokenBalance(), + 1, + "wrong internal token balance" + ); + assertFalse(test.inTokenIds(0), "id in tokenIds"); + assertFalse(test.inTokenIds(1), "id in tokenIds"); + } + + function testGenerateOrder_wildcard1() public { + SpentItem[] memory minimumReceived = new SpentItem[](2); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(erc721), + identifier: 101, + amount: 1 + }); + minimumReceived[1] = SpentItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(erc721), + identifier: 10, + amount: 1 + }); + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + vm.prank(seaport); + ( + SpentItem[] memory spentItems, + ReceivedItem[] memory receivedItems + ) = test.generateOrder( + address(this), + minimumReceived, + maximumSpent, + "" + ); + + assertEq(spentItems.length, 2, "wrong spentItems length"); + assertEq( + uint8(spentItems[0].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[0].amount, 1, "wrong spentitem amount"); + assertLt(spentItems[0].identifier, 3, "wrong spentitem identifier"); + assertEq(spentItems[0].token, address(erc721), "wrong spentitem token"); + assertEq( + uint8(spentItems[1].itemType), + uint8(ItemType.ERC721), + "wrong spentitem type" + ); + assertEq(spentItems[1].amount, 1, "wrong spentitem amount"); + assertLt(spentItems[1].identifier, 3, "wrong spentitem identifier"); + assertEq(spentItems[1].token, address(erc721), "wrong spentitem token"); + assertEq(receivedItems.length, 1, "wrong receivedItems length"); + + assertEq( + uint8(receivedItems[0].itemType), + uint8(ItemType.ERC20), + "wrong receiveditem type" + ); + assertEq(receivedItems[0].identifier, 0, "wrong identifier"); + assertEq(receivedItems[0].amount, 2e18, "wrong amount"); + assertEq(receivedItems[0].token, address(erc20), "wrong token"); + assertEq(receivedItems[0].recipient, address(test), "wrong receiver"); + + assertEq( + test.getInternalBalance(), + 1e18 + 2e18, + "wrong internal balance" + ); + assertEq( + test.getInternalTokenBalance(), + 1, + "wrong internal token balance" + ); + assertFalse( + test.inTokenIds(spentItems[0].identifier), + "id not in tokenIds" + ); + assertFalse( + test.inTokenIds(spentItems[1].identifier), + "id not in tokenIds" + ); + + assertTrue( + spentItems[0].identifier != spentItems[1].identifier, + "same id" + ); + } + + function testGenerateOrder_wildcard_rejectWildcardSpent() public { + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem(ItemType.ERC20, address(erc20), 6e17, 1); + + SpentItem[] memory maximumSpent = new SpentItem[](2); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(erc721), + identifier: 3, + amount: 1 + }); + maximumSpent[1] = SpentItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(erc721), + identifier: 4, + amount: 1 + }); + vm.startPrank(seaport); + vm.expectRevert(TestPoolOfferer.InvalidItemType.selector); + test.generateOrder(address(this), minimumReceived, maximumSpent, ""); + } +} diff --git a/test/foundry/offerers/TestPoolOffererTest.t.sol b/test/foundry/offerers/TestPoolOffererTest.t.sol new file mode 100644 index 000000000..1378e48eb --- /dev/null +++ b/test/foundry/offerers/TestPoolOffererTest.t.sol @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { Test } from "forge-std/Test.sol"; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { TestPoolFactory, TestPoolOfferer } from "./impl/TestPoolFactory.sol"; + +import { + SpentItem, + AdvancedOrder, + ConsiderationItem, + CriteriaResolver, + OfferItem, + OrderType +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { ItemType } from "../../../contracts/lib/ConsiderationEnums.sol"; + +contract TestPoolOffererTest is BaseOrderTest { + TestPoolFactory factory; + TestPoolOfferer offerer; + + function setUp() public override { + super.setUp(); + factory = new TestPoolFactory(address(consideration)); + uint256[] memory tokenIds = new uint256[](5); + tokenIds[0] = 101; + tokenIds[1] = 102; + tokenIds[2] = 103; + tokenIds[3] = 104; + tokenIds[4] = 105; + for (uint256 i; i < tokenIds.length; i++) { + test721_1.mint(address(this), tokenIds[i]); + } + + token1.approve(address(factory), 1000); + test721_1.setApprovalForAll(address(factory), true); + offerer = factory.createPoolOfferer( + address(test721_1), + tokenIds, + address(token1), + 1000 + ); + + vm.label(address(factory), "factory"); + vm.label(address(offerer), "offerer"); + } + + function testBuyOne() public { + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 101, + amount: 1 + }); + + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: 300 // will not spend entire amount + }); + + addOfferItem(ItemType.ERC721, 101, 1); + addConsiderationItem(payable(address(offerer)), ItemType.ERC20, 0, 250); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(this)), 1); + assertEq(test721_1.ownerOf(101), address(this)); + assertEq(token1.balanceOf(address(offerer)), 1250); + } + + function testBuyTwo() public { + SpentItem[] memory minimumReceived = new SpentItem[](2); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 101, + amount: 1 + }); + minimumReceived[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 102, + amount: 1 + }); + + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: 1000000 + }); + + addOfferItem(ItemType.ERC721, 101, 1); + addOfferItem(ItemType.ERC721, 102, 1); + addConsiderationItem(payable(address(offerer)), ItemType.ERC20, 0, 666); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(this)), 2); + assertEq(test721_1.ownerOf(101), address(this)); + assertEq(token1.balanceOf(address(offerer)), 1666); + } + + function testSellOne() public { + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 101, + amount: 1 + }); + + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: 300 // will not spend entire amount + }); + + test721_1.mint(address(this), 106); + + addConsiderationItem( + payable(address(offerer)), + ItemType.ERC721, + 106, + 1 + ); + addOfferItem(ItemType.ERC20, 0, 166); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(offerer)), 6); + assertEq(test721_1.ownerOf(106), address(offerer)); + assertEq(token1.balanceOf(address(offerer)), 833); + } + + function testSellTwo() public { + test721_1.mint(address(this), 106); + test721_1.mint(address(this), 107); + SpentItem[] memory maximumSpent = new SpentItem[](2); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 106, + amount: 1 + }); + maximumSpent[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 107, + amount: 1 + }); + + SpentItem[] memory minimumReceived = new SpentItem[](1); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: 1000000 + }); + + addConsiderationItem( + payable(address(offerer)), + ItemType.ERC721, + 106, + 1 + ); + addConsiderationItem( + payable(address(offerer)), + ItemType.ERC721, + 107, + 1 + ); + addOfferItem(ItemType.ERC20, 0, 286); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(offerer)), 7); + assertEq(test721_1.ownerOf(106), address(offerer)); + assertEq(test721_1.ownerOf(107), address(offerer)); + assertEq(token1.balanceOf(address(offerer)), 714); + } + + function testBuyOneWildCard() public { + addOfferItem( + OfferItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1 + }) + ); + addConsiderationItem(payable(address(offerer)), ItemType.ERC20, 0, 250); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(this)), 1); + assertEq(test721_1.ownerOf(101), address(this)); + assertEq(token1.balanceOf(address(offerer)), 1250); + } + + function testBuyTwoWildCard() public { + addOfferItem( + OfferItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1 + }) + ); + addOfferItem( + OfferItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1 + }) + ); + addConsiderationItem(payable(address(offerer)), ItemType.ERC20, 0, 666); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(this)), 2); + assertEq(test721_1.ownerOf(101), address(this)); + assertEq(token1.balanceOf(address(offerer)), 1666); + } + + function testSellOneWildCard() public { + test721_1.mint(address(this), 106); + + addOfferItem(ItemType.ERC20, 0, 166); + addConsiderationItem( + ConsiderationItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifierOrCriteria: 0, + startAmount: 1, + endAmount: 1, + recipient: payable(address(offerer)) + }) + ); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(offerer)), 6); + assertEq(test721_1.ownerOf(106), address(offerer)); + assertEq(token1.balanceOf(address(offerer)), 833); + } + + function testBuyTwoHeterogenous() public { + SpentItem[] memory minimumReceived = new SpentItem[](2); + minimumReceived[0] = SpentItem({ + itemType: ItemType.ERC721_WITH_CRITERIA, + token: address(test721_1), + identifier: 0, + amount: 1 + }); + minimumReceived[1] = SpentItem({ + itemType: ItemType.ERC721, + token: address(test721_1), + identifier: 101, + amount: 1 + }); + + SpentItem[] memory maximumSpent = new SpentItem[](1); + maximumSpent[0] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: 1000000 + }); + + addOfferItem(ItemType.ERC721, 101, 1); + addOfferItem(ItemType.ERC721, 102, 1); + addConsiderationItem(payable(address(offerer)), ItemType.ERC20, 0, 666); + + _configureOrderParameters({ + offerer: address(offerer), + zone: address(0), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.orderType = OrderType.CONTRACT; + + configureOrderComponents(consideration.getCounter(address(offerer))); + + consideration.getOrderHash(baseOrderComponents); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: "", + extraData: "" + }); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + + consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertEq(test721_1.balanceOf(address(this)), 2); + assertEq(test721_1.ownerOf(101), address(this)); + assertEq(test721_1.ownerOf(102), address(this)); + assertEq(token1.balanceOf(address(offerer)), 1666); + } +} diff --git a/test/foundry/offerers/impl/AdjustedAmountOfferer.sol b/test/foundry/offerers/impl/AdjustedAmountOfferer.sol new file mode 100644 index 000000000..ff739a9f8 --- /dev/null +++ b/test/foundry/offerers/impl/AdjustedAmountOfferer.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ERC20Interface +} from "../../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../../../../contracts/interfaces/ContractOffererInterface.sol"; + +import { + SpentItem, + ReceivedItem, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +contract AdjustedAmountOfferer is ContractOffererInterface { + int256 immutable offerAmountAdjust; + int256 immutable considerationAmountAdjust; + + constructor( + address[] memory seaports, + ERC20Interface _token1, + ERC20Interface _token2, + int256 _offerAmountAdjust, + int256 _considerationAmountAdjust + ) { + for (uint256 i = 0; i < seaports.length; ++i) { + address seaport = seaports[i]; + _token1.approve(seaport, type(uint256).max); + _token2.approve(seaport, type(uint256).max); + } + offerAmountAdjust = _offerAmountAdjust; + considerationAmountAdjust = _considerationAmountAdjust; + } + + /** + * @dev Generates an order with the specified minimum and maximum spent items, + */ + function generateOrder( + address, + SpentItem[] calldata a, + SpentItem[] calldata b, + bytes calldata c + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + return previewOrder(address(this), address(this), a, b, c); + } + + /** + * @dev View function to preview an order generated in response to a minimum + * set of received items, maximum set of spent items, and context + * (supplied as extraData). + */ + function previewOrder( + address, + address, + SpentItem[] calldata a, + SpentItem[] calldata b, + bytes calldata + ) + public + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + return ( + adjustAmounts(a, offerAmountAdjust), + _convertSpentToReceived(adjustAmounts(b, considerationAmountAdjust)) + ); + } + + function adjustAmounts( + SpentItem[] memory items, + int256 amount + ) internal pure returns (SpentItem[] memory) { + SpentItem[] memory adjustedItems = new SpentItem[](items.length); + for (uint256 i = 0; i < items.length; ++i) { + adjustedItems[i] = items[i]; + adjustedItems[i].amount = uint256(int256(items[i].amount) + amount); + } + return adjustedItems; + } + + function _convertSpentToReceived( + SpentItem[] memory spentItems + ) internal view returns (ReceivedItem[] memory) { + ReceivedItem[] memory receivedItems = new ReceivedItem[]( + spentItems.length + ); + for (uint256 i = 0; i < spentItems.length; ++i) { + receivedItems[i] = _convertSpentToReceived(spentItems[i]); + } + return receivedItems; + } + + function _convertSpentToReceived( + SpentItem memory spentItem + ) internal view returns (ReceivedItem memory) { + return + ReceivedItem({ + itemType: spentItem.itemType, + token: spentItem.token, + identifier: spentItem.identifier, + amount: spentItem.amount, + recipient: payable(address(this)) + }); + } + + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata /* context */, + bytes32[] calldata /* orderHashes */, + uint256 /* contractNonce */ + ) external pure override returns (bytes4 /* ratifyOrderMagicValue */) { + return AdjustedAmountOfferer.ratifyOrder.selector; + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("AdjustedAmountOfferer", schemas); + } +} diff --git a/test/foundry/offerers/impl/BadOfferer.sol b/test/foundry/offerers/impl/BadOfferer.sol new file mode 100644 index 000000000..150bdf49a --- /dev/null +++ b/test/foundry/offerers/impl/BadOfferer.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ERC20Interface, + ERC721Interface +} from "../../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../../../../contracts/interfaces/ContractOffererInterface.sol"; + +import { ItemType } from "../../../../contracts/lib/ConsiderationEnums.sol"; + +import { + SpentItem, + ReceivedItem, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +interface ERC20Mintable { + function mint(address to, uint256 amount) external; +} + +contract BadOfferer is ContractOffererInterface { + error IntentionalRevert(); + + ERC20Interface token1; + ERC721Interface token2; + + constructor( + address seaport, + ERC20Interface _token1, + ERC721Interface _token2 + ) { + _token1.approve(seaport, type(uint256).max); + token1 = _token1; + token2 = _token2; + ERC20Mintable(address(_token1)).mint(address(this), 100000); + } + + /** + * @dev Generates an order with the specified minimum and maximum spent items, + */ + function generateOrder( + address a, + SpentItem[] calldata b, + SpentItem[] calldata c, + bytes calldata d + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + return previewOrder(a, a, b, c, d); + } + + /** + * @dev View function to preview an order generated in response to a minimum + * set of received items, maximum set of spent items, and context + * (supplied as extraData). + */ + function previewOrder( + address, + address, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata + ) + public + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + if (maximumSpent[0].identifier == 1) { + offer = minimumReceived; + consideration = new ReceivedItem[](1); + consideration[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 1, + amount: 1, + recipient: payable(address(this)) + }); + return (offer, consideration); + } else if (maximumSpent[0].identifier == 2) { + // return nothing + assembly { + return(0, 0) + } + } else if (maximumSpent[0].identifier == 3) { + revert IntentionalRevert(); + } else { + // return garbage + bytes32 h1 = keccak256(abi.encode(minimumReceived)); + bytes32 h2 = keccak256(abi.encode(maximumSpent)); + assembly { + mstore(0x00, h1) + mstore(0x20, h2) + return(0, 0x100) + } + } + } + + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata /* context */, + bytes32[] calldata /* orderHashes */, + uint256 /* contractNonce */ + ) external pure override returns (bytes4 /* ratifyOrderMagicValue */) { + return BadOfferer.ratifyOrder.selector; + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("BadOfferer", schemas); + } +} diff --git a/test/foundry/offerers/impl/PassthroughOfferer.sol b/test/foundry/offerers/impl/PassthroughOfferer.sol new file mode 100644 index 000000000..40264a5cf --- /dev/null +++ b/test/foundry/offerers/impl/PassthroughOfferer.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ERC20Interface, + ERC721Interface +} from "../../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../../../../contracts/interfaces/ContractOffererInterface.sol"; + +import { + SpentItem, + ReceivedItem, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +contract PassthroughOfferer is ContractOffererInterface { + constructor( + address[] memory seaports, + ERC20Interface _token1, + ERC721Interface _token2 + ) { + for (uint256 i = 0; i < seaports.length; ++i) { + address seaport = seaports[i]; + _token1.approve(seaport, type(uint256).max); + _token2.setApprovalForAll(seaport, true); + } + } + + /** + * @dev Generates an order with the specified minimum and maximum spent items, + */ + function generateOrder( + address, + SpentItem[] calldata a, + SpentItem[] calldata b, + bytes calldata c + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + return previewOrder(address(this), address(this), a, b, c); + } + + /** + * @dev View function to preview an order generated in response to a minimum + * set of received items, maximum set of spent items, and context + * (supplied as extraData). + */ + function previewOrder( + address, + address, + SpentItem[] calldata a, + SpentItem[] calldata b, + bytes calldata + ) + public + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + return (a, _convertSpentToReceived(b)); + } + + function _convertSpentToReceived( + SpentItem[] calldata spentItems + ) internal view returns (ReceivedItem[] memory) { + ReceivedItem[] memory receivedItems = new ReceivedItem[]( + spentItems.length + ); + for (uint256 i = 0; i < spentItems.length; ++i) { + receivedItems[i] = _convertSpentToReceived(spentItems[i]); + } + return receivedItems; + } + + function _convertSpentToReceived( + SpentItem calldata spentItem + ) internal view returns (ReceivedItem memory) { + return + ReceivedItem({ + itemType: spentItem.itemType, + token: spentItem.token, + identifier: spentItem.identifier, + amount: spentItem.amount, + recipient: payable(address(this)) + }); + } + + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata /* context */, + bytes32[] calldata /* orderHashes */, + uint256 /* contractNonce */ + ) external pure override returns (bytes4 /* ratifyOrderMagicValue */) { + return PassthroughOfferer.ratifyOrder.selector; + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("PassthroughOfferer", schemas); + } +} diff --git a/test/foundry/offerers/impl/StatefulRatifierOfferer.sol b/test/foundry/offerers/impl/StatefulRatifierOfferer.sol new file mode 100644 index 000000000..c106afd39 --- /dev/null +++ b/test/foundry/offerers/impl/StatefulRatifierOfferer.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ERC20Interface, + ERC721Interface +} from "../../../../contracts/interfaces/AbridgedTokenInterfaces.sol"; + +import { + ContractOffererInterface +} from "../../../../contracts/interfaces/ContractOffererInterface.sol"; + +import { + ItemType, + Side +} from "../../../../contracts/lib/ConsiderationEnums.sol"; + +import { + SpentItem, + ReceivedItem, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +interface ERC20Mintable { + function mint(address to, uint256 amount) external; +} + +contract StatefulRatifierOfferer is ContractOffererInterface { + error IncorrectValue(uint256 actual, uint256 expected); + error IncorrectToken(address actual, address expected); + error IncorrectItemType(ItemType actual, ItemType expected); + error IncorrectContext(bytes context); + error IncorrectOrderHashesLength(uint256 actual, uint256 expected); + error IncorrectLength(Side side, uint256 actual, uint256 expected); + + ERC20Interface token1; + ERC721Interface token2; + uint256 value; + bool public called; + uint256 numOffersToReturn; + + constructor( + address seaport, + ERC20Interface _token1, + ERC721Interface _token2, + uint256 _numOffersToReturn + ) { + numOffersToReturn = _numOffersToReturn; + _token1.approve(seaport, type(uint256).max); + token1 = _token1; + token2 = _token2; + ERC20Mintable(address(_token1)).mint(address(this), 100000); + } + + /** + * @dev Generates an order with the specified minimum and maximum spent items, + * and the optional extra data. + * + * @param - Fulfiller, unused here. + * @param minimumReceived The minimum items that the caller is willing to + * receive. + * @param - maximumSent, unused here. + * @param - context, unused here. + * + * @return offer A tuple containing the offer items. + * @return consideration A tuple containing the consideration items. + */ + function generateOrder( + address, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata, + bytes calldata + ) + external + virtual + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Generate an offer of ERC20 items. + value = minimumReceived[0].amount; + offer = new SpentItem[](numOffersToReturn); + for (uint256 i; i < numOffersToReturn; i++) { + // Create a new ERC20 item with a unique value. + offer[i] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: value + i + }); + } + + // Generate a consideration of a three ERC721 items. + consideration = new ReceivedItem[](3); + consideration[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 42, + amount: 1, + recipient: payable(address(this)) + }); + + // Return the offer and consideration. + consideration[1] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 43, + amount: 1, + recipient: payable(address(this)) + }); + consideration[2] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 44, + amount: 1, + recipient: payable(address(this)) + }); + return (offer, consideration); + } + + /** + * @dev View function to preview an order generated in response to a minimum + * set of received items, maximum set of spent items, and context + * (supplied as extraData). + * + * @param - caller, unused here. + * @param - fulfiller, unused here. + * @param minimumReceived The minimum received set. + * @param - maximumSpent, unused here. + * @param - context, unused here. + * + * @return offer The offer for the order. + * @return consideration The consideration for the order. + */ + function previewOrder( + address, + address, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata, + bytes calldata + ) + external + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Set the value of the items to be spent. + uint256 _value = minimumReceived[0].amount; + + // Create an offer array and populate it with ERC20 items. + offer = new SpentItem[](numOffersToReturn); + for (uint256 i; i < numOffersToReturn; i++) { + offer[i] = SpentItem({ + itemType: ItemType.ERC20, + token: address(token1), + identifier: 0, + amount: _value + i + }); + } + + // Create a consideration array with three ERC721 items. + consideration = new ReceivedItem[](3); + consideration[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 42, + amount: 1, + recipient: payable(address(this)) + }); + + // Return the offer and consideration. + consideration[1] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 43, + amount: 1, + recipient: payable(address(this)) + }); + consideration[2] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(token2), + identifier: 44, + amount: 1, + recipient: payable(address(this)) + }); + return (offer, consideration); + } + + function ratifyOrder( + SpentItem[] calldata minimumReceived /* offer */, + ReceivedItem[] calldata maximumSpent /* consideration */, + bytes calldata context /* context */, + bytes32[] calldata orderHashes /* orderHashes */, + uint256 /* contractNonce */ + ) external override returns (bytes4 /* ratifyOrderMagicValue */) { + // check that the length matches what is expected + if (minimumReceived.length != numOffersToReturn) { + revert IncorrectLength( + Side.OFFER, + minimumReceived.length, + numOffersToReturn + ); + } + // Check that all minimumReceived items are of type ERC20. + + for (uint256 i = 0; i < minimumReceived.length; i++) { + if (minimumReceived[i].itemType != ItemType.ERC20) { + revert IncorrectItemType( + minimumReceived[i].itemType, + ItemType.ERC20 + ); + } + + // Check that the token address for each minimumReceived item is + // correct. + if (minimumReceived[i].token != address(token1)) { + revert IncorrectToken( + minimumReceived[i].token, + address(token1) + ); + } + + // Check that the value of each minimumReceived item is correct. + if (minimumReceived[i].amount != value + i) { + revert IncorrectValue(minimumReceived[i].amount, value + i); + } + } + + if (maximumSpent.length != 3) { + revert IncorrectLength(Side.CONSIDERATION, maximumSpent.length, 3); + } + // Check that all maximumSpent items are of type ERC721, that the + // address is correct, and that the token ID is correct. + for (uint256 i; i < maximumSpent.length; i++) { + if (maximumSpent[i].itemType != ItemType.ERC721) { + revert IncorrectItemType( + maximumSpent[i].itemType, + ItemType.ERC721 + ); + } + if (maximumSpent[i].token != address(token2)) { + revert IncorrectToken(maximumSpent[i].token, address(token2)); + } + if (maximumSpent[i].identifier != 42 + i) { + revert IncorrectValue(maximumSpent[i].identifier, 42 + i); + } + } + + // Check that the context is correct. + if (keccak256(context) != keccak256("context")) { + revert IncorrectContext(context); + } + + // Check that the orderHashes length is correct. + if (orderHashes.length < 1) { + revert IncorrectOrderHashesLength(orderHashes.length, 1); + } + + // Set the public called bool to true. + called = true; + + // Return the ratifyOrderMagicValue. + return ContractOffererInterface.ratifyOrder.selector; + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("StatefulRatifierOfferer", schemas); + } +} diff --git a/test/foundry/offerers/impl/TestPoolFactory.sol b/test/foundry/offerers/impl/TestPoolFactory.sol new file mode 100644 index 000000000..ad3086d70 --- /dev/null +++ b/test/foundry/offerers/impl/TestPoolFactory.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +import { + IERC721 +} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; + +import { + IERC20 +} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; + +import { TestPoolOfferer } from "./TestPoolOfferer.sol"; + +contract TestPoolFactory { + // The address of the Seaport contract. + address immutable seaport; + + // Constructor that takes the Seaport contract address as an argument. + constructor(address _seaport) { + seaport = _seaport; + } + + // Function to create a new TestPoolOfferer contract. + function createPoolOfferer( + address erc721, + uint256[] calldata tokenIds, + address erc20, + uint256 amount + ) external returns (TestPoolOfferer newPool) { + // Create a new TestPoolOfferer contract + newPool = new TestPoolOfferer( + seaport, + erc721, + tokenIds, + erc20, + amount, + msg.sender + ); + + // Transfer the specified amount of ERC20 tokens from the caller to the + // new contract. + IERC20(erc20).transferFrom(msg.sender, address(newPool), amount); + + // Transfer the specified ERC721 tokens from the caller to the new + // contract. + for (uint256 i; i < tokenIds.length; i++) { + IERC721(erc721).transferFrom( + msg.sender, + address(newPool), + tokenIds[i] + ); + } + } +} diff --git a/test/foundry/offerers/impl/TestPoolOfferer.sol b/test/foundry/offerers/impl/TestPoolOfferer.sol new file mode 100644 index 000000000..40ca802fb --- /dev/null +++ b/test/foundry/offerers/impl/TestPoolOfferer.sol @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +import { + ContractOffererInterface +} from "../../../../contracts/interfaces/ContractOffererInterface.sol"; + +import { ItemType } from "../../../../contracts/lib/ConsiderationEnums.sol"; + +import { + SpentItem, + ReceivedItem, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +import { + EnumerableSet +} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol"; + +import { + IERC721 +} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; + +import { + IERC20 +} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; + +import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol"; + +contract TestPoolOfferer is ContractOffererInterface, Ownable { + using EnumerableSet for EnumerableSet.UintSet; + + error OnlySeaport(); + error NotImplemented(); + error InvalidItemType(); + error InvalidToken(); + error InvalidTokenId(uint256 id); + + address immutable _SEAPORT; + address immutable erc721; + EnumerableSet.UintSet tokenIds; + address immutable erc20; + uint256 balance; + uint256 immutable k; + uint256 constant scale = 10_000; + + constructor( + address seaport, + address _erc721, + uint256[] memory _tokenIds, + address _erc20, + uint256 amount, + address + ) { + // Set immutable values and storage variables. + _SEAPORT = seaport; + erc721 = _erc721; + for (uint256 i; i < _tokenIds.length; ++i) { + tokenIds.add(_tokenIds[i]); + } + // tokenIds = _tokenIds; + erc20 = _erc20; + balance = amount; + k = amount * scale * _tokenIds.length; + + // Make the necessary approvals. + IERC20(erc20).approve(seaport, type(uint256).max); + IERC721(erc721).setApprovalForAll(seaport, true); + } + + /** + * @dev Generate an order based on the minimumReceived and maximumSpent + * arrays. This function can only be called by Seaport. + * + * @param - The address of the fulfiller. + * @param minimumReceived An array of SpentItem structs representing the + * minimum amount that the offerer is willing to + * receive. + * @param maximumSpent An array of SpentItem structs representing the + * maximum amount that the offerer is willing to + * spend. + * + * @return offer An array of SpentItem structs representing the + * offer. + * @return consideration An array of ReceivedItem structs representing the + * consideration. + */ + function generateOrder( + address /* fulfiller */, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata + ) + external + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Only Seaport may call this function. + if (msg.sender != _SEAPORT) { + revert OnlySeaport(); + } + // If true, this offerer is spending NFTs and receiving ERC20. + bool nftOffer; + uint256 newBalance; + ( + offer, + consideration, + newBalance, + nftOffer + ) = _generateOfferAndConsideration(minimumReceived, maximumSpent); + + // Update token ids and balances. + // Note that no tokens will actually be exchanged until Seaport executes + // fulfillments. + if (nftOffer) { + // Remove outgoing tokenIDs from pool and assign concrete IDs to any + // criteria-based "wildcard" erc721 items. + _processNftOffer(offer); + } else { + // Add incoming NFT ids to pool. + _processNftConsideration(consideration); + } + // Update internal erc20 balance. + balance = newBalance; + } + + /** @dev Generate an offer and consideration based on the minimumReceived + * and maximumSpent arrays. + * + * @param minimumReceived An array of SpentItem structs representing the + * minimum amount that the offerer is willing to + * receive. + * @param maximumSpent An array of SpentItem structs representing the + * maximum amount that the offerer is willing to + * spend. + * + * @return offer An array of SpentItem structs representing the + * offer. + * @return consideration An array of ReceivedItem structs representing the + * consideration. + */ + function previewOrder( + address, + address, + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent, + bytes calldata + ) + external + view + override + returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) + { + // Declare a local variable to track whether the offer is an NFT offer. + bool nftOffer; + + // Generate the offer and consideration, and store the result in the + // offer and consideration variables. + // The _generateOfferAndConsideration function also returns a bool + // indicating whether the offer is an NFT offer. + (offer, consideration, , nftOffer) = _generateOfferAndConsideration( + minimumReceived, + maximumSpent + ); + + // If it's an NFT offer, call the _previewNftOffer function with the + // offer as an argument. + if (nftOffer) _previewNftOffer(offer); + } + + /** + * @dev Ratify an order. + * + * @param - An array of SpentItem structs representing the + * offer. + * @param - An array of ReceivedItem structs representing the + * consideration. + * @param - The context of the order. + * @param - An array of order hashes. + * @param - The contract nonce. + * + * @return - The magic value of the ratifyOrder + * function. + */ + function ratifyOrder( + SpentItem[] calldata /* offer */, + ReceivedItem[] calldata /* consideration */, + bytes calldata /* context */, + bytes32[] calldata /* orderHashes */, + uint256 /* contractNonce */ + ) external pure override returns (bytes4 /* ratifyOrderMagicValue */) { + return ContractOffererInterface.ratifyOrder.selector; + } + + /** + * @dev Returns the metadata for this contract offerer. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 1337; + schemas[0].metadata = new bytes(0); + + return ("TestPoolOfferer", schemas); + } + + /** + * @dev Add incoming tokens to the set of IDs in the pool. + * + * @param maximumSpent An array of ReceivedItem structs representing the + * maximum amount that the offerer is willing to spend. + */ + function _processNftConsideration( + ReceivedItem[] memory maximumSpent + ) internal { + // Iterate over each item in the maximumSpent array. + for (uint256 i = 0; i < maximumSpent.length; ++i) { + // Retrieve the maximum spent item. + ReceivedItem memory maximumSpentItem = maximumSpent[i]; + + // Attempt to add the item's identifier to the `tokenIds` set. + bool added = tokenIds.add(maximumSpentItem.identifier); + + // If the item's identifier was not added to the set, it means that + // it was already in the pool. In that case, revert the transaction. + if (!added) { + revert InvalidTokenId(maximumSpentItem.identifier); + } + } + } + + /** + * @dev Remove outgoing tokens from the set of IDs in the pool. + * + * @param minimumReceived An array of SpentItem structs representing the + * minimum amount that the offerer is willing to + * receive. + */ + function _processNftOffer(SpentItem[] memory minimumReceived) internal { + // Declare a local variable to track the index of the criteria-based + // "wildcard" items. + // uint256 criteriaIndex; + + // Iterate over each item in the minimumReceived array. + for (uint256 i = 0; i < minimumReceived.length; ++i) { + // Retrieve the minimum received item. + SpentItem memory minimumReceivedItem = minimumReceived[i]; + + // Declare a local variable to hold the identifier of the item. + uint256 identifier; + + // If the item type is ERC721, set the identifier to the item's + // identifier. Otherwise, the item is a wildcard token. + if (minimumReceivedItem.itemType == ItemType.ERC721) { + identifier = minimumReceivedItem.identifier; + } else { + // For wildcard tokens, always pick the token ID in the first + // position of the set. + identifier = tokenIds.at(0); + + // Set the item type to ERC721 and the identifier to the token + // ID. + minimumReceivedItem.itemType = ItemType.ERC721; + minimumReceivedItem.identifier = identifier; + + // Increment the criteria index. + // ++criteriaIndex; + } + + // Attempt to remove the item's identifier from the `tokenIds` set. + bool removed = tokenIds.remove(identifier); + + // If a token was not removed it means that the token is not or is + // no longer in the pool. Note that criteria-based "wildcard" items + // should follow concrete items in the offer array to avoid + // circumstances where the offerer tries to spend the same ID twice. + if (!removed) { + revert InvalidTokenId(identifier); + } + } + } + + /** + * @dev Preview an NFT offer by assigning concrete token IDs to any + * criteria-based "wildcard" erc721 items. + * + * @param minimumReceived An array of SpentItem structs representing the + * minimum amount that the offerer is willing to + * receive. + */ + function _previewNftOffer( + SpentItem[] memory minimumReceived + ) internal view { + // Declare a local variable to track the index of the criteria-based + // "wildcard" items. + uint256 criteriaIndex; + + // Iterate over each item in the minimumReceived array. + for (uint256 i = 0; i < minimumReceived.length; ++i) { + // Retrieve the minimum received item. + SpentItem memory minimumReceivedItem = minimumReceived[i]; + + // If the item type is ERC721_WITH_CRITERIA, it means that the item + // is a wildcard token. In that case, assign a concrete token ID + // to the item. + if (minimumReceivedItem.itemType == ItemType.ERC721_WITH_CRITERIA) { + // Pick the next token ID in the set, starting at index 0. + minimumReceivedItem.itemType = ItemType.ERC721; + minimumReceivedItem.identifier = tokenIds.at(criteriaIndex); + + // Increment the criteria index. + ++criteriaIndex; + } + } + } + + /** @dev Generate offer and consideration items based on the number of + * ERC721 tokens offered or requested. + * + * @param minimumReceived An array of SpentItem structs representing the + * minimum amount that the offerer is willing to + * receive. + * @param maximumSpent An array of SpentItem structs representing the + * maximum amount that the offerer is willing to + * spend. + * + * @return offer An array of SpentItem structs representing the + * offer. + * @return consideration An array of ReceivedItem structs representing the + * consideration. + * @return newBalance The new balance of the contract. + * @return nftOffer Whether the offer is an NFT offer. + */ + function _generateOfferAndConsideration( + SpentItem[] calldata minimumReceived, + SpentItem[] calldata maximumSpent + ) + internal + view + returns ( + SpentItem[] memory offer, + ReceivedItem[] memory consideration, + uint256 newBalance, + bool nftOffer + ) + { + // Validate that all tokens in each set are "homogenous" + // (ERC20 or ERC721/_WITH_CRITERIA). + _validateSpentItems(minimumReceived, true); + _validateSpentItems(maximumSpent, false); + + // If the fulfiller is spending ERC20 tokens, calculate how much is + // needed for the number of tokens specified in minimumReceived. + if (maximumSpent[0].itemType == ItemType.ERC20) { + // Calculate the number of new tokens. + uint256 newNumTokens = tokenIds.length() - minimumReceived.length; + + // Calculate the new balance. k is set above, it's: amount * scale * + // _tokenIds.length. + newBalance = k / (scale * newNumTokens); + + // Calculate the amount of ERC20 tokens to pay for N items. + uint256 considerationAmount = newBalance - balance; + + // Generate the offer and consideration. + consideration = new ReceivedItem[](1); + consideration[0] = ReceivedItem({ + itemType: ItemType.ERC20, + token: erc20, + identifier: 0, + amount: considerationAmount, + recipient: payable(address(this)) + }); + offer = minimumReceived; + + // Set the nftOffer flag to true. + nftOffer = true; + } else { + // Otherwise, if fulfiller is spending ERC721 tokens, calculate the + // amount of ERC20 tokens to pay for N items. + + // Calculate the number of new tokens. + uint256 newNumTokens = tokenIds.length() + maximumSpent.length; + + // Calculate the new balance. k is set above, it's: amount * scale * + // _tokenIds.length. + newBalance = k / (scale * newNumTokens); + + // Calculate the amount of ERC20 tokens to pay for N items. + uint256 paymentAmount = balance - newBalance; + + // Generate the offer and consideration. + offer = new SpentItem[](1); + offer[0] = SpentItem({ + itemType: ItemType.ERC20, + token: erc20, + identifier: 0, + amount: paymentAmount + }); + consideration = _convertSpentErc721sToReceivedItems(maximumSpent); + + // nftOffer is false by default. + } + } + + /** + * @dev Validate that the SpentItem array contains a valid type, then + * iterate over the items and validate them individually. + * + * @param minimumReceived An array of SpentItem structs to validate. + * @param offer A boolean value indicating whether the items are + * part of an offer or consideration. + */ + function _validateSpentItems( + SpentItem[] calldata minimumReceived, + bool offer + ) internal view { + // Store the first item's type. + ItemType homogenousType = minimumReceived[0].itemType; + + // If the first item is an ERC721_WITH_CRITERIA item, set the + // homogenousType to ERC721. + if (homogenousType == ItemType.ERC721_WITH_CRITERIA) { + homogenousType = ItemType.ERC721; + } + + // Check if the item type is valid (either ERC721 or ERC20). + if ( + homogenousType != ItemType.ERC721 && + homogenousType != ItemType.ERC20 + ) { + revert InvalidItemType(); + } + + // Set the `nft` variable to `true` if the item type is ERC721, + // `false` otherwise. + bool nft = homogenousType == ItemType.ERC721; + + // Loop over the remaining items in the `minimumReceived` array and + // validate each one. + for (uint256 i = 1; i < minimumReceived.length; ++i) { + _validateSpentItem(minimumReceived[i], homogenousType, nft, offer); + } + } + + /** @dev Validates each SpentItem. Ensures that the item type is valid, all + * tokens are homogenous, and that the addresses are those we expect. + * + * @param offerItem The item to validate. + * @param homogenousType The item type that should be homogenous. + * @param nft Whether the item is a non-fungible token. + * @param offer A bool indicating whether the minimumReceived + * array is the offer or the consideration. This is + * used to determine if ERC721_WITH_CRITERIA items + * are allowed. + */ + function _validateSpentItem( + SpentItem calldata offerItem, + ItemType homogenousType, + bool nft, + bool offer + ) internal view { + // Ensure that item type is valid. + ItemType offerItemType = offerItem.itemType; + if (offerItemType == ItemType.ERC721_WITH_CRITERIA) { + // maximumSpent items must not be criteria items, since they will + // not be resolved. + if (!offer) { + revert InvalidItemType(); + } + offerItemType = ItemType.ERC721; + } + + // Don't allow mixing of ERC20 and ERC721 items. + if (offerItemType != homogenousType) { + revert InvalidItemType(); + } + + // Validate that the token address is correct. + if (nft) { + if (offerItem.token != erc721) { + revert InvalidToken(); + } + } else { + if (offerItem.token != erc20) { + revert InvalidToken(); + } + } + } + + /** + * @dev Converts a set of SpentItem structs representing ERC721 tokens to + * a set of ReceivedItem structs. + * + * @param spentItems An array of SpentItem structs representing the tokens + * to be converted. + * + * @return receivedItems An array of ReceivedItem structs representing the + * converted tokens. + */ + function _convertSpentErc721sToReceivedItems( + SpentItem[] calldata spentItems + ) internal view returns (ReceivedItem[] memory receivedItems) { + // Initialize array of received items. + receivedItems = new ReceivedItem[](spentItems.length); + // Loop through spent items and convert each to a received item. + for (uint256 i = 0; i < spentItems.length; ++i) { + SpentItem calldata spentItem = spentItems[i]; + // Create new received item and populate with data from spent item. + receivedItems[i] = ReceivedItem({ + itemType: ItemType.ERC721, + token: erc721, + identifier: (spentItem.itemType == + ItemType.ERC721_WITH_CRITERIA) + ? 106 + : spentItem.identifier, + amount: spentItem.amount, + recipient: payable(address(this)) + }); + } + } + + /** + * @dev Transfers the contract's entire ERC20 and ERC721 balances to the + * contract's owner. + */ + function withdrawAll() external onlyOwner { + // Get the contract's ERC20 balance. + IERC20 ierc20 = IERC20(erc20); + uint256 erc20Balance = ierc20.balanceOf(address(this)); + + // Get the contract's owner address. + address owner = owner(); + + // Transfer the ERC20 balance to the contract's owner. + ierc20.transfer(owner, erc20Balance); + + // Transfer each ERC721 token to the contract's owner. + while (tokenIds.length() > 0) { + uint256 tokenId = tokenIds.at(0); + IERC721(erc721).transferFrom(address(this), owner, tokenId); + + // Remove the token from the set. + tokenIds.remove(tokenId); + } + + // Set the contract's ERC20 balance to 0. + balance = 0; + } +} diff --git a/test/foundry/token/ERC721.sol b/test/foundry/token/CustomERC721.sol similarity index 94% rename from test/foundry/token/ERC721.sol rename to test/foundry/token/CustomERC721.sol index 751cebb17..a4346c3a2 100644 --- a/test/foundry/token/ERC721.sol +++ b/test/foundry/token/CustomERC721.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; /// @notice Modern, minimalist, and gas efficient ERC-721 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) /// @notice modified for testing purposes -abstract contract ERC721 { +abstract contract CustomERC721 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ @@ -95,20 +95,14 @@ abstract contract ERC721 { emit ApprovalForAll(msg.sender, operator, approved); } - function isApprovedForAll(address owner, address spender) - public - view - virtual - returns (bool) - { + function isApprovedForAll( + address owner, + address spender + ) public view virtual returns (bool) { return _isApprovedForAll[owner][spender]; } - function transferFrom( - address from, - address to, - uint256 id - ) public virtual { + function transferFrom(address from, address to, uint256 id) public virtual { require(from == _ownerOf[id], "WRONG_FROM"); require( @@ -178,12 +172,9 @@ abstract contract ERC721 { ERC165 LOGIC //////////////////////////////////////////////////////////////*/ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 diff --git a/test/foundry/token/StubERC1155.sol b/test/foundry/token/StubERC1155.sol new file mode 100644 index 000000000..d1a84c3ed --- /dev/null +++ b/test/foundry/token/StubERC1155.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract StubERC1155 { + event TransferSingle( + address indexed operator, + address indexed from, + address indexed to, + uint256 id, + uint256 amount + ); + + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + uint256 amount, + bytes memory + ) public { + emit TransferSingle(msg.sender, from, to, tokenId, amount); + } +} diff --git a/test/foundry/token/StubERC20.sol b/test/foundry/token/StubERC20.sol new file mode 100644 index 000000000..3932c0a83 --- /dev/null +++ b/test/foundry/token/StubERC20.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract StubERC20 { + event Transfer(address indexed from, address indexed to, uint256 amount); + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual returns (bool) { + emit Transfer(from, to, amount); + return true; + } +} diff --git a/test/foundry/token/StubERC721.sol b/test/foundry/token/StubERC721.sol new file mode 100644 index 000000000..ebc32f29f --- /dev/null +++ b/test/foundry/token/StubERC721.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract StubERC721 { + event Transfer(address indexed from, address indexed to, uint256 tokenId); + + function transferFrom(address from, address to, uint256 tokenId) public { + emit Transfer(from, to, tokenId); + } +} diff --git a/test/foundry/utils/ArithmeticUtil.sol b/test/foundry/utils/ArithmeticUtil.sol index 6c59092ba..1d4f550d3 100644 --- a/test/foundry/utils/ArithmeticUtil.sol +++ b/test/foundry/utils/ArithmeticUtil.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; library ArithmeticUtil { ///@dev utility function to avoid overflows when multiplying fuzzed uints with widths <256 diff --git a/test/foundry/utils/BaseConsiderationTest.sol b/test/foundry/utils/BaseConsiderationTest.sol index 2b4d9ed34..efd20fecf 100644 --- a/test/foundry/utils/BaseConsiderationTest.sol +++ b/test/foundry/utils/BaseConsiderationTest.sol @@ -1,19 +1,43 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + ConduitController +} from "../../../contracts/conduit/ConduitController.sol"; + +import { + ReferenceConduitController +} from "../../../reference/conduit/ReferenceConduitController.sol"; + +import { + ConduitControllerInterface +} from "../../../contracts/interfaces/ConduitControllerInterface.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { ItemType } from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + OfferItem, + ConsiderationItem +} from "../../../contracts/lib/ConsiderationStructs.sol"; -import { ConduitController } from "../../../contracts/conduit/ConduitController.sol"; -import { ConsiderationInterface } from "../../../contracts/interfaces/ConsiderationInterface.sol"; -import { OrderType, BasicOrderType, ItemType, Side } from "../../../contracts/lib/ConsiderationEnums.sol"; -import { OfferItem, ConsiderationItem, OrderComponents, BasicOrderParameters } from "../../../contracts/lib/ConsiderationStructs.sol"; -import { Test } from "forge-std/Test.sol"; import { DifferentialTest } from "./DifferentialTest.sol"; + import { StructCopier } from "./StructCopier.sol"; + import { stdStorage, StdStorage } from "forge-std/Test.sol"; -import { ReferenceConduitController } from "../../../reference/conduit/ReferenceConduitController.sol"; -import { ReferenceConsideration } from "../../../reference/ReferenceConsideration.sol"; + import { Conduit } from "../../../contracts/conduit/Conduit.sol"; + import { Consideration } from "../../../contracts/lib/Consideration.sol"; +import { + ReferenceConsideration +} from "../../../reference/ReferenceConsideration.sol"; + /// @dev Base test case that deploys Consideration and its dependencies contract BaseConsiderationTest is DifferentialTest, StructCopier { using stdStorage for StdStorage; @@ -21,16 +45,53 @@ contract BaseConsiderationTest is DifferentialTest, StructCopier { ConsiderationInterface consideration; ConsiderationInterface referenceConsideration; bytes32 conduitKeyOne; - ConduitController conduitController; - ConduitController referenceConduitController; + ConduitControllerInterface conduitController; + ConduitControllerInterface referenceConduitController; Conduit referenceConduit; Conduit conduit; + bool coverage_or_debug; + + function tryEnvBool(string memory envVar) internal view returns (bool) { + try vm.envBool(envVar) returns (bool _value) { + return _value; + } catch { + return false; + } + } + + function tryEnvString( + string memory envVar + ) internal view returns (string memory) { + try vm.envString(envVar) returns (string memory _value) { + return _value; + } catch { + return ""; + } + } + + function stringEq( + string memory a, + string memory b + ) internal pure returns (bool) { + return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); + } + + function debugEnabled() internal view returns (bool) { + return + tryEnvBool("SEAPORT_COVERAGE") || + stringEq(tryEnvString("FOUNDRY_PROFILE"), "debug"); + } function setUp() public virtual { + // conditionally deploy contracts normally or from precompiled source + // deploys normally when SEAPORT_COVERAGE is true for coverage analysis + // or when FOUNDRY_PROFILE is "debug" for debugging with source maps + // deploys from precompiled source when both are false + coverage_or_debug = debugEnabled(); + conduitKeyOne = bytes32(uint256(uint160(address(this))) << 96); _deployAndConfigurePrecompiledOptimizedConsideration(); - emit log("Deploying reference from precompiled source"); _deployAndConfigurePrecompiledReferenceConsideration(); vm.label(address(conduitController), "conduitController"); @@ -46,20 +107,24 @@ contract BaseConsiderationTest is DifferentialTest, StructCopier { } ///@dev deploy optimized consideration contracts from pre-compiled source - // (solc-0.8.14, IR pipeline enabled) + // (solc-0.8.17, IR pipeline enabled, unless running coverage or debug) function _deployAndConfigurePrecompiledOptimizedConsideration() public { - conduitController = ConduitController( - deployCode( - "optimized-out/ConduitController.sol/ConduitController.json" - ) - ); - consideration = ConsiderationInterface( - deployCode( - "optimized-out/Consideration.sol/Consideration.json", - abi.encode(address(conduitController)) - ) - ); - + if (!coverage_or_debug) { + conduitController = ConduitController( + deployCode( + "optimized-out/ConduitController.sol/ConduitController.json" + ) + ); + consideration = ConsiderationInterface( + deployCode( + "optimized-out/Consideration.sol/Consideration.json", + abi.encode(address(conduitController)) + ) + ); + } else { + conduitController = new ConduitController(); + consideration = new Consideration(address(conduitController)); + } //create conduit, update channel conduit = Conduit( conduitController.createConduit(conduitKeyOne, address(this)) @@ -71,19 +136,28 @@ contract BaseConsiderationTest is DifferentialTest, StructCopier { ); } - ///@dev deploy reference consideration contracts from pre-compiled source (solc-0.8.7, IR pipeline disabled) + ///@dev deploy reference consideration contracts from pre-compiled source + /// (solc-0.8.13, IR pipeline disabled, unless running coverage or debug) function _deployAndConfigurePrecompiledReferenceConsideration() public { - referenceConduitController = ConduitController( - deployCode( - "reference-out/ReferenceConduitController.sol/ReferenceConduitController.json" - ) - ); - referenceConsideration = ConsiderationInterface( - deployCode( - "reference-out/ReferenceConsideration.sol/ReferenceConsideration.json", - abi.encode(address(referenceConduitController)) - ) - ); + if (!coverage_or_debug) { + referenceConduitController = ConduitController( + deployCode( + "reference-out/ReferenceConduitController.sol/ReferenceConduitController.json" + ) + ); + referenceConsideration = ConsiderationInterface( + deployCode( + "reference-out/ReferenceConsideration.sol/ReferenceConsideration.json", + abi.encode(address(referenceConduitController)) + ) + ); + } else { + referenceConduitController = new ReferenceConduitController(); + // for debugging + referenceConsideration = new ReferenceConsideration( + address(referenceConduitController) + ); + } //create conduit, update channel referenceConduit = Conduit( @@ -139,7 +213,7 @@ contract BaseConsiderationTest is DifferentialTest, StructCopier { ConsiderationInterface _consideration, uint256 _pkOfSigner, bytes32 _orderHash - ) internal returns (bytes memory) { + ) internal view returns (bytes memory) { (bytes32 r, bytes32 s, uint8 v) = getSignatureComponents( _consideration, _pkOfSigner, @@ -152,7 +226,7 @@ contract BaseConsiderationTest is DifferentialTest, StructCopier { ConsiderationInterface _consideration, uint256 _pkOfSigner, bytes32 _orderHash - ) internal returns (bytes memory) { + ) internal view returns (bytes memory) { (bytes32 r, bytes32 s, uint8 v) = getSignatureComponents( _consideration, _pkOfSigner, @@ -172,14 +246,7 @@ contract BaseConsiderationTest is DifferentialTest, StructCopier { ConsiderationInterface _consideration, uint256 _pkOfSigner, bytes32 _orderHash - ) - internal - returns ( - bytes32, - bytes32, - uint8 - ) - { + ) internal view returns (bytes32, bytes32, uint8) { (, bytes32 domainSeparator, ) = _consideration.information(); (uint8 v, bytes32 r, bytes32 s) = vm.sign( _pkOfSigner, diff --git a/test/foundry/utils/BaseOrderTest.sol b/test/foundry/utils/BaseOrderTest.sol index 084e43dcd..21eeeb547 100644 --- a/test/foundry/utils/BaseOrderTest.sol +++ b/test/foundry/utils/BaseOrderTest.sol @@ -1,37 +1,40 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; -import { BaseConsiderationTest } from "./BaseConsiderationTest.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; -import { ProxyRegistry } from "../interfaces/ProxyRegistry.sol"; -import { OwnableDelegateProxy } from "../interfaces/OwnableDelegateProxy.sol"; -import { OneWord } from "../../../contracts/lib/ConsiderationConstants.sol"; -import { ConsiderationInterface } from "../../../contracts/interfaces/ConsiderationInterface.sol"; -import { BasicOrderType, OrderType } from "../../../contracts/lib/ConsiderationEnums.sol"; -import { BasicOrderParameters, ConsiderationItem, AdditionalRecipient, OfferItem, Fulfillment, FulfillmentComponent, ItemType, Order, OrderComponents, OrderParameters } from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +import { OrderType } from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + BasicOrder_additionalRecipients_data_cdPtr, + TwoWords +} from "../../../contracts/lib/ConsiderationConstants.sol"; +import { + AdditionalRecipient, + Fulfillment, + FulfillmentComponent, + Order, + OrderComponents, + OrderParameters +} from "../../../contracts/lib/ConsiderationStructs.sol"; + import { ArithmeticUtil } from "./ArithmeticUtil.sol"; -import { OfferConsiderationItemAdder } from "./OfferConsiderationItemAdder.sol"; + +import { OrderBuilder } from "./OrderBuilder.sol"; + import { AmountDeriver } from "../../../contracts/lib/AmountDeriver.sol"; /// @dev base test class for cases that depend on pre-deployed token contracts -contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { +contract BaseOrderTest is OrderBuilder, AmountDeriver { using stdStorage for StdStorage; using ArithmeticUtil for uint256; using ArithmeticUtil for uint128; using ArithmeticUtil for uint120; - uint256 internal globalSalt; - - OrderParameters baseOrderParameters; - OrderComponents baseOrderComponents; - - FulfillmentComponent[] offerComponents; - FulfillmentComponent[] considerationComponents; - - FulfillmentComponent[][] offerComponentsArray; - FulfillmentComponent[][] considerationComponentsArray; - - Fulfillment[] fulfillments; FulfillmentComponent firstOrderFirstItem; FulfillmentComponent firstOrderSecondItem; FulfillmentComponent secondOrderFirstItem; @@ -44,9 +47,6 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { Fulfillment secondFulfillment; Fulfillment thirdFulfillment; Fulfillment fourthFulfillment; - FulfillmentComponent fulfillmentComponent; - FulfillmentComponent[] fulfillmentComponents; - Fulfillment fulfillment; AdditionalRecipient[] additionalRecipients; @@ -64,7 +64,7 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { { bool success; assembly { - // Transfer the ETH and store if it succeeded or not. + // Transfer the native token and store if it succeeded or not. success := call(gas(), _addr, 1, 0, 0, 0, 0) } vm.assume(success); @@ -107,10 +107,20 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { ) internal returns (bool) { Order[] memory orders = new Order[](1); orders[0] = order; + return _validateOrders(orders, _consideration); + } + + function _validateOrders( + Order[] memory orders, + ConsiderationInterface _consideration + ) internal returns (bool) { return _consideration.validate(orders); } - function _prepareOrder(uint256 tokenId, uint256 totalConsiderationItems) + function _prepareOrder( + uint256 tokenId, + uint256 totalConsiderationItems + ) internal returns ( Order memory order, @@ -158,6 +168,19 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { } } + function _dirtyFirstAdditionalRecipient( + bytes memory orderCalldata + ) internal pure { + assembly { + let firstAdditionalRecipientOffset := add( + orderCalldata, + add(TwoWords, BasicOrder_additionalRecipients_data_cdPtr) + ) + // Dirty the top byte of the first additional recipient address. + mstore8(firstAdditionalRecipientOffset, 1) + } + } + function _getItemsLengthPointerInOrderCalldata( bytes memory orderCalldata, uint256 relativeOrderParametersOffset, @@ -268,67 +291,6 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { (success, ) = considerationAddress.call(orderCalldata); } - function configureOrderParameters(address offerer) internal { - _configureOrderParameters( - offerer, - address(0), - bytes32(0), - globalSalt++, - false - ); - } - - function _configureOrderParameters( - address offerer, - address zone, - bytes32 zoneHash, - uint256 salt, - bool useConduit - ) internal { - bytes32 conduitKey = useConduit ? conduitKeyOne : bytes32(0); - baseOrderParameters.offerer = offerer; - baseOrderParameters.zone = zone; - baseOrderParameters.offer = offerItems; - baseOrderParameters.consideration = considerationItems; - baseOrderParameters.orderType = OrderType.FULL_OPEN; - baseOrderParameters.startTime = block.timestamp; - baseOrderParameters.endTime = block.timestamp + 1; - baseOrderParameters.zoneHash = zoneHash; - baseOrderParameters.salt = salt; - baseOrderParameters.conduitKey = conduitKey; - baseOrderParameters.totalOriginalConsiderationItems = considerationItems - .length; - } - - function _configureOrderParametersSetEndTime( - address offerer, - address zone, - uint256 endTime, - bytes32 zoneHash, - uint256 salt, - bool useConduit - ) internal { - _configureOrderParameters(offerer, zone, zoneHash, salt, useConduit); - baseOrderParameters.endTime = endTime; - } - - /** - @dev configures order components based on order parameters in storage and counter param - */ - function _configureOrderComponents(uint256 counter) internal { - baseOrderComponents.offerer = baseOrderParameters.offerer; - baseOrderComponents.zone = baseOrderParameters.zone; - baseOrderComponents.offer = baseOrderParameters.offer; - baseOrderComponents.consideration = baseOrderParameters.consideration; - baseOrderComponents.orderType = baseOrderParameters.orderType; - baseOrderComponents.startTime = baseOrderParameters.startTime; - baseOrderComponents.endTime = baseOrderParameters.endTime; - baseOrderComponents.zoneHash = baseOrderParameters.zoneHash; - baseOrderComponents.salt = baseOrderParameters.salt; - baseOrderComponents.conduitKey = baseOrderParameters.conduitKey; - baseOrderComponents.counter = counter; - } - function getMaxConsiderationValue() internal view returns (uint256) { uint256 value = 0; for (uint256 i = 0; i < considerationItems.length; ++i) { @@ -364,31 +326,30 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { ); } - function getOrderParameters(address payable offerer, OrderType orderType) - internal - returns (OrderParameters memory) - { + function getOrderParameters( + address offerer, + OrderType orderType + ) internal returns (OrderParameters memory) { return - OrderParameters( - offerer, - address(0), - offerItems, - considerationItems, - orderType, - block.timestamp, - block.timestamp + 1, - bytes32(0), - globalSalt++, - bytes32(0), - considerationItems.length - ); + OrderParameters({ + offerer: offerer, + zone: address(0), + offer: offerItems, + consideration: considerationItems, + orderType: orderType, + startTime: block.timestamp, + endTime: block.timestamp + 1, + zoneHash: bytes32(0), + salt: globalSalt++, + conduitKey: bytes32(0), + totalOriginalConsiderationItems: considerationItems.length + }); } - function toOrderComponents(OrderParameters memory _params, uint256 nonce) - internal - pure - returns (OrderComponents memory) - { + function toOrderComponents( + OrderParameters memory _params, + uint256 nonce + ) internal pure returns (OrderComponents memory) { return OrderComponents( _params.offerer, @@ -405,67 +366,11 @@ contract BaseOrderTest is OfferConsiderationItemAdder, AmountDeriver { ); } - function toBasicOrderParameters( - Order memory _order, - BasicOrderType basicOrderType - ) internal pure returns (BasicOrderParameters memory) { - return - BasicOrderParameters( - _order.parameters.consideration[0].token, - _order.parameters.consideration[0].identifierOrCriteria, - _order.parameters.consideration[0].endAmount, - payable(_order.parameters.offerer), - _order.parameters.zone, - _order.parameters.offer[0].token, - _order.parameters.offer[0].identifierOrCriteria, - _order.parameters.offer[0].endAmount, - basicOrderType, - _order.parameters.startTime, - _order.parameters.endTime, - _order.parameters.zoneHash, - _order.parameters.salt, - _order.parameters.conduitKey, - _order.parameters.conduitKey, - 0, - new AdditionalRecipient[](0), - _order.signature - ); - } - - function toBasicOrderParameters( - OrderComponents memory _order, - BasicOrderType basicOrderType, - bytes memory signature - ) internal pure returns (BasicOrderParameters memory) { - return - BasicOrderParameters( - _order.consideration[0].token, - _order.consideration[0].identifierOrCriteria, - _order.consideration[0].endAmount, - payable(_order.offerer), - _order.zone, - _order.offer[0].token, - _order.offer[0].identifierOrCriteria, - _order.offer[0].endAmount, - basicOrderType, - _order.startTime, - _order.endTime, - _order.zoneHash, - _order.salt, - _order.conduitKey, - _order.conduitKey, - 0, - new AdditionalRecipient[](0), - signature - ); - } - ///@dev allow signing for this contract since it needs to be recipient of basic order to reenter on receive - function isValidSignature(bytes32, bytes memory) - external - pure - returns (bytes4) - { + function isValidSignature( + bytes32, + bytes memory + ) external pure virtual returns (bytes4) { return 0x1626ba7e; } diff --git a/test/foundry/utils/ConsiderationErrorsWrapper.sol b/test/foundry/utils/ConsiderationErrorsWrapper.sol new file mode 100644 index 000000000..44939840d --- /dev/null +++ b/test/foundry/utils/ConsiderationErrorsWrapper.sol @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { Side } from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + _revertBadFraction, + _revertConsiderationLengthNotEqualToTotalOriginal, + _revertConsiderationNotMet, + _revertCriteriaNotEnabledForItem, + _revertInsufficientNativeTokensSupplied, + _revertInvalidBasicOrderParameterEncoding, + _revertInvalidCallToConduit, + _revertInvalidConduit, + _revertInvalidContractOrder, + _revertInvalidERC721TransferAmount, + _revertInvalidMsgValue, + _revertInvalidNativeOfferItem, + _revertInvalidProof, + _revertInvalidTime, + _revertMismatchedFulfillmentOfferAndConsiderationComponents, + _revertMissingFulfillmentComponentOnAggregation, + _revertMissingOriginalConsiderationItems, + _revertNoReentrantCalls, + _revertNoSpecifiedOrdersAvailable, + _revertOfferAndConsiderationRequiredOnFulfillment, + _revertOrderAlreadyFilled, + _revertOrderCriteriaResolverOutOfRange, + _revertOrderIsCancelled, + _revertOrderPartiallyFilled, + _revertPartialFillsNotEnabledForOrder, + _revertUnresolvedConsiderationCriteria, + _revertUnresolvedOfferCriteria, + _revertUnusedItemParameters +} from "../../../contracts/lib/ConsiderationErrors.sol"; + +contract ConsiderationErrorsWrapper { + /** + * @dev Reverts the current transaction with a "BadFraction" error message. + */ + function __revertBadFraction() external pure { + _revertBadFraction(); + } + + /** + * @dev Reverts the current transaction with a "ConsiderationNotMet" error + * message, including the provided order index, consideration index, and + * shortfall amount. + * + * @param orderIndex The index of the order that did not meet the + * consideration criteria. + * @param considerationIndex The index of the consideration item that did not + * meet its criteria. + * @param shortfallAmount The amount by which the consideration criteria were + * not met. + */ + function __revertConsiderationNotMet( + uint256 orderIndex, + uint256 considerationIndex, + uint256 shortfallAmount + ) external pure { + _revertConsiderationNotMet( + orderIndex, + considerationIndex, + shortfallAmount + ); + } + + /** + * @dev Reverts the current transaction with a "CriteriaNotEnabledForItem" error + * message. + */ + function __revertCriteriaNotEnabledForItem() external pure { + _revertCriteriaNotEnabledForItem(); + } + + /** + * @dev Reverts the current transaction with an "InsufficientNativeTokensSupplied" + * error message. + */ + function __revertInsufficientNativeTokensSupplied() external pure { + _revertInsufficientNativeTokensSupplied(); + } + + /** + * @dev Reverts the current transaction with an + * "InvalidBasicOrderParameterEncoding" error message. + */ + function __revertInvalidBasicOrderParameterEncoding() external pure { + _revertInvalidBasicOrderParameterEncoding(); + } + + /** + * @dev Reverts the current transaction with an "InvalidCallToConduit" error + * message, including the provided address of the conduit that was called + * improperly. + * + * @param conduit The address of the conduit that was called improperly. + */ + function __revertInvalidCallToConduit(address conduit) external pure { + _revertInvalidCallToConduit(conduit); + } + + /** + * @dev Reverts the current transaction with an "InvalidConduit" error message, + * including the provided key and address of the invalid conduit. + * + * @param conduitKey The key of the invalid conduit. + * @param conduit The address of the invalid conduit. + */ + function __revertInvalidConduit( + bytes32 conduitKey, + address conduit + ) external pure { + _revertInvalidConduit(conduitKey, conduit); + } + + /** + * @dev Reverts the current transaction with an "InvalidERC721TransferAmount" + * error message. + * + * @param amount The invalid amount. + */ + function __revertInvalidERC721TransferAmount(uint256 amount) external pure { + _revertInvalidERC721TransferAmount(amount); + } + + /** + * @dev Reverts the current transaction with an "InvalidMsgValue" error message, + * including the invalid value that was sent in the transaction's + * `msg.value` field. + * + * @param value The invalid value that was sent in the transaction's `msg.value` + * field. + */ + function __revertInvalidMsgValue(uint256 value) external pure { + _revertInvalidMsgValue(value); + } + + /** + * @dev Reverts the current transaction with an "InvalidNativeOfferItem" error + * message. + */ + function __revertInvalidNativeOfferItem() external pure { + _revertInvalidNativeOfferItem(); + } + + /** + * @dev Reverts the current transaction with an "InvalidProof" error message. + */ + function __revertInvalidProof() external pure { + _revertInvalidProof(); + } + + /** + * @dev Reverts the current transaction with an "InvalidContractOrder" error + * message. + * + * @param orderHash The hash of the contract order that caused the error. + */ + function __revertInvalidContractOrder(bytes32 orderHash) external pure { + _revertInvalidContractOrder(orderHash); + } + + /** + * @dev Reverts the current transaction with an "InvalidTime" error message. + * + * @param startTime The time at which the order becomes active. + * @param endTime The time at which the order becomes inactive. + */ + function __revertInvalidTime( + uint256 startTime, + uint256 endTime + ) external pure { + _revertInvalidTime(startTime, endTime); + } + + /** + * @dev Reverts execution with a + * "MismatchedFulfillmentOfferAndConsiderationComponents" error message. + * + * @param fulfillmentIndex The index of the fulfillment that caused the + * error. + */ + function __revertMismatchedFulfillmentOfferAndConsiderationComponents( + uint256 fulfillmentIndex + ) external pure { + _revertMismatchedFulfillmentOfferAndConsiderationComponents( + fulfillmentIndex + ); + } + + /** + * @dev Reverts execution with a "MissingFulfillmentComponentOnAggregation" + * error message. + * + * @param side The side of the fulfillment component that is missing (0 for + * offer, 1 for consideration). + * + */ + function __revertMissingFulfillmentComponentOnAggregation( + Side side + ) external pure { + _revertMissingFulfillmentComponentOnAggregation(side); + } + + /** + * @dev Reverts execution with a "MissingOriginalConsiderationItems" error + * message. + */ + function __revertMissingOriginalConsiderationItems() external pure { + _revertMissingOriginalConsiderationItems(); + } + + /** + * @dev Reverts execution with a "NoReentrantCalls" error message. + */ + function __revertNoReentrantCalls() external pure { + _revertNoReentrantCalls(); + } + + /** + * @dev Reverts execution with a "NoSpecifiedOrdersAvailable" error message. + */ + function __revertNoSpecifiedOrdersAvailable() external pure { + _revertNoSpecifiedOrdersAvailable(); + } + + /** + * @dev Reverts execution with a "OfferAndConsiderationRequiredOnFulfillment" + * error message. + */ + function __revertOfferAndConsiderationRequiredOnFulfillment() + external + pure + { + _revertOfferAndConsiderationRequiredOnFulfillment(); + } + + /** + * @dev Reverts execution with an "OrderAlreadyFilled" error message. + * + * @param orderHash The hash of the order that has already been filled. + */ + function __revertOrderAlreadyFilled(bytes32 orderHash) external pure { + _revertOrderAlreadyFilled(orderHash); + } + + /** + * @dev Reverts execution with an "OrderCriteriaResolverOutOfRange" error + * message. + * + * @param side The side of the criteria that is missing (0 for offer, 1 for + * consideration). + * + */ + function __revertOrderCriteriaResolverOutOfRange(Side side) external pure { + _revertOrderCriteriaResolverOutOfRange(side); + } + + /** + * @dev Reverts execution with an "OrderIsCancelled" error message. + * + * @param orderHash The hash of the order that has already been cancelled. + */ + function __revertOrderIsCancelled(bytes32 orderHash) external pure { + _revertOrderIsCancelled(orderHash); + } + + /** + * @dev Reverts execution with an "OrderPartiallyFilled" error message. + * + * @param orderHash The hash of the order that has already been partially + * filled. + */ + function __revertOrderPartiallyFilled(bytes32 orderHash) external pure { + _revertOrderPartiallyFilled(orderHash); + } + + /** + * @dev Reverts execution with a "PartialFillsNotEnabledForOrder" error message. + */ + function __revertPartialFillsNotEnabledForOrder() external pure { + _revertPartialFillsNotEnabledForOrder(); + } + + /** + * @dev Reverts execution with an "UnresolvedConsiderationCriteria" error + * message. + */ + function __revertUnresolvedConsiderationCriteria( + uint256 orderIndex, + uint256 considerationIndex + ) external pure { + _revertUnresolvedConsiderationCriteria(orderIndex, considerationIndex); + } + + /** + * @dev Reverts execution with an "UnresolvedOfferCriteria" error message. + */ + function __revertUnresolvedOfferCriteria( + uint256 orderIndex, + uint256 offerIndex + ) external pure { + _revertUnresolvedOfferCriteria(orderIndex, offerIndex); + } + + /** + * @dev Reverts execution with an "UnusedItemParameters" error message. + */ + function __revertUnusedItemParameters() external pure { + _revertUnusedItemParameters(); + } + + /** + * @dev Reverts execution with a "ConsiderationLengthNotEqualToTotalOriginal" + * error message. + */ + function __revertConsiderationLengthNotEqualToTotalOriginal() + external + pure + { + _revertConsiderationLengthNotEqualToTotalOriginal(); + } +} diff --git a/test/foundry/utils/DifferentialTest.sol b/test/foundry/utils/DifferentialTest.sol index a002a19f7..38e561675 100644 --- a/test/foundry/utils/DifferentialTest.sol +++ b/test/foundry/utils/DifferentialTest.sol @@ -1,35 +1,40 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + import { Test } from "forge-std/Test.sol"; contract DifferentialTest is Test { - // slot where HEVM stores bool of whether or not an assertion has failed - bytes32 HEVM_FAILED_SLOT = - 0x6661696c65640000000000000000000000000000000000000000000000000000; + ///@dev error to supply + error RevertWithFailureStatus(bool status); + error DifferentialTestAssertionFailed(); + + // slot where HEVM stores a bool representing whether or not an assertion has failed + bytes32 HEVM_FAILED_SLOT = bytes32("failed"); // hash of the bytes surfaced by `revert RevertWithFailureStatus(false)` bytes32 PASSING_HASH = - 0xf951c460268b64a0aabc103be9b020b90c4d14012c2d21f9c441a69438400a57; - - error RevertWithFailureStatus(bool status); + keccak256( + abi.encodeWithSelector(RevertWithFailureStatus.selector, false) + ); - ///@dev reverts after function body with the failure status, clearing all state changes made + ///@dev reverts after function body with HEVM failure status, which clears all state changes + /// but still surfaces assertion failure status. modifier stateless() { _; - revertWithFailureStatus(); + revert RevertWithFailureStatus(readHevmFailureSlot()); } + ///@dev revert if the supplied bytes do not match the expected "passing" revert bytes function assertPass(bytes memory reason) internal view { + // hash the reason and compare to the hash of the passing revert bytes if (keccak256(reason) != PASSING_HASH) { - revert(); + revert DifferentialTestAssertionFailed(); } } - function revertWithFailureStatus() internal { - revert RevertWithFailureStatus(readHevmFailureSlot()); - } - - function readHevmFailureSlot() internal returns (bool) { + ///@dev read the failure slot of the HEVM using the vm.load cheatcode + /// Returns true if there was an assertion failure. recorded. + function readHevmFailureSlot() internal view returns (bool) { return vm.load(address(vm), HEVM_FAILED_SLOT) == bytes32(uint256(1)); } } diff --git a/test/foundry/utils/EIP712MerkleTree.sol b/test/foundry/utils/EIP712MerkleTree.sol new file mode 100644 index 000000000..40f7dcbab --- /dev/null +++ b/test/foundry/utils/EIP712MerkleTree.sol @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { MurkyBase } from "murky/common/MurkyBase.sol"; +import { + TypehashDirectory +} from "../../../contracts/lib/TypehashDirectory.sol"; +import { Test } from "forge-std/Test.sol"; +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; +import { + OrderComponents +} from "../../../contracts/lib/ConsiderationStructs.sol"; +import { Math } from "openzeppelin-contracts/contracts/utils/math/Math.sol"; + +///@dev Seaport doesn't sort leaves when hashing for bulk orders, but Murky +/// does, so implement a custom hashLeafPairs function +contract MerkleUnsorted is MurkyBase { + function hashLeafPairs( + bytes32 left, + bytes32 right + ) public pure override returns (bytes32 _hash) { + assembly { + mstore(0x0, left) + mstore(0x20, right) + _hash := keccak256(0x0, 0x40) + } + } +} + +contract EIP712MerkleTree is Test { + // data contract to retrieve bulk order typehashes + TypehashDirectory internal immutable _typehashDirectory; + OrderComponents private emptyOrderComponents; + MerkleUnsorted private merkle; + + constructor() { + _typehashDirectory = new TypehashDirectory(); + merkle = new MerkleUnsorted(); + } + + /// @dev same lookup seaport optimized does + function _lookupBulkOrderTypehash( + uint256 treeHeight + ) internal view returns (bytes32 typeHash) { + TypehashDirectory directory = _typehashDirectory; + assembly { + let typeHashOffset := add(1, shl(0x5, sub(treeHeight, 1))) + extcodecopy(directory, 0, typeHashOffset, 0x20) + typeHash := mload(0) + } + } + + function signBulkOrder( + ConsiderationInterface consideration, + uint256 privateKey, + OrderComponents[] memory orderComponents, + uint24 orderIndex, + bool useCompact2098 + ) public view returns (bytes memory) { + // cache the hash of an empty order components struct to fill out any + // nodes required to make the length a power of 2 + bytes32 emptyComponentsHash = consideration.getOrderHash( + emptyOrderComponents + ); + // declare vars here to avoid stack too deep errors + bytes32[] memory leaves; + bytes32 bulkOrderTypehash; + // block scope to avoid stacc 2 dank + { + // height of merkle tree is log2(length), rounded up to next power + // of 2 + uint256 height = Math.log2(orderComponents.length); + // Murky won't let you compute a merkle tree with only 1 leaf, so + // if height is 0 (length is 1), set height to 1 + if (2 ** height != orderComponents.length || height == 0) { + height += 1; + } + // get the typehash for a bulk order of this height + bulkOrderTypehash = _lookupBulkOrderTypehash(height); + // allocate array for leaf hashes + leaves = new bytes32[](2 ** height); + // hash each original order component + for (uint256 i = 0; i < orderComponents.length; i++) { + leaves[i] = consideration.getOrderHash(orderComponents[i]); + } + // fill out empty node hashes + for (uint256 i = orderComponents.length; i < 2 ** height; i++) { + leaves[i] = emptyComponentsHash; + } + } + + // get the proof for the order index + bytes32[] memory proof = merkle.getProof(leaves, orderIndex); + bytes32 root = merkle.getRoot(leaves); + + return + _getSignature( + consideration, + privateKey, + bulkOrderTypehash, + root, + proof, + orderIndex, + useCompact2098 + ); + } + + function _getSignature( + ConsiderationInterface consideration, + uint256 privateKey, + bytes32 bulkOrderTypehash, + bytes32 root, + bytes32[] memory proof, + uint24 orderIndex, + bool useCompact2098 + ) internal view returns (bytes memory) { + // bulkOrder hash is keccak256 of the specific bulk order typehash and + // the merkle root of the order hashes + bytes32 bulkOrderHash = keccak256(abi.encode(bulkOrderTypehash, root)); + + // get domain separator from the particular seaport instance + (, bytes32 domainSeparator, ) = consideration.information(); + + // declare out here to avoid stack too deep errors + bytes memory signature; + // avoid stacc 2 thicc + { + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + privateKey, + keccak256( + abi.encodePacked( + bytes2(0x1901), + domainSeparator, + bulkOrderHash + ) + ) + ); + // if useCompact2098 is true, encode yParity (v) into s + if (useCompact2098) { + uint256 yParity = (v == 27) ? 0 : 1; + bytes32 yAndS = bytes32(uint256(s) | (yParity << 255)); + signature = abi.encodePacked(r, yAndS); + } else { + signature = abi.encodePacked(r, s, v); + } + } + + // return the packed signature, order index, and proof + // encodePacked will pack everything tightly without lengths + // ie, long-style rsv signatures will have 1 byte for v + // orderIndex will be the next 3 bytes + // then proof will be each element one after another; its offset and + // length will not be encoded + return abi.encodePacked(signature, orderIndex, proof); + } + + function signSparseBulkOrder( + ConsiderationInterface consideration, + uint256 privateKey, + OrderComponents memory orderComponents, + uint256 height, + uint24 orderIndex, + bool useCompact2098 + ) public view returns (bytes memory) { + require(orderIndex < 2 ** height, "orderIndex out of bounds"); + // get hash of actual order + bytes32 orderHash = consideration.getOrderHash(orderComponents); + // get initial empty order components hash + bytes32 emptyComponentsHash = consideration.getOrderHash( + emptyOrderComponents + ); + + // calculate intermediate hashes of a sparse order tree + // this will also serve as our proof + bytes32[] memory emptyHashes = new bytes32[]((height)); + // first layer is empty order hash + emptyHashes[0] = emptyComponentsHash; + for (uint256 i = 1; i < height; i++) { + bytes32 nextHash; + bytes32 lastHash = emptyHashes[i - 1]; + // subsequent layers are hash of emptyHeight+emptyHeight + assembly { + mstore(0, lastHash) + mstore(0x20, lastHash) + nextHash := keccak256(0, 0x40) + } + emptyHashes[i] = nextHash; + } + // begin calculating order tree root + bytes32 root = orderHash; + // hashIndex is the index within the layer of the non-sparse hash + uint24 hashIndex = orderIndex; + + for (uint256 i = 0; i < height; i++) { + // get sparse hash at this height + bytes32 heightEmptyHash = emptyHashes[i]; + assembly { + // if the hashIndex is odd, our "root" is second component + if and(hashIndex, 1) { + mstore(0, heightEmptyHash) + mstore(0x20, root) + } + // else it is even and our "root" is first component + // (this can def be done in a branchless way but who has the time??) + if iszero(and(hashIndex, 1)) { + mstore(0, root) + mstore(0x20, heightEmptyHash) + } + // compute new intermediate hash (or final root) + root := keccak256(0, 0x40) + } + // divide hashIndex by 2 to get index of next layer + // 0 -> 0 + // 1 -> 0 + // 2 -> 1 + // 3 -> 1 + // etc + hashIndex /= 2; + } + + return + _getSignature( + consideration, + privateKey, + _lookupBulkOrderTypehash(height), + root, + emptyHashes, + orderIndex, + useCompact2098 + ); + } +} diff --git a/test/foundry/utils/ERC1155Recipient.sol b/test/foundry/utils/ERC1155Recipient.sol index 075d7d741..8a8313b0f 100644 --- a/test/foundry/utils/ERC1155Recipient.sol +++ b/test/foundry/utils/ERC1155Recipient.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; -import { ERC1155TokenReceiver } from "@rari-capital/solmate/src/tokens/ERC1155.sol"; +import { + ERC1155TokenReceiver +} from "@rari-capital/solmate/src/tokens/ERC1155.sol"; contract ERC1155Recipient is ERC1155TokenReceiver { function onERC1155Received( diff --git a/test/foundry/utils/ERC721Recipient.sol b/test/foundry/utils/ERC721Recipient.sol index 2a09d0f69..255775af7 100644 --- a/test/foundry/utils/ERC721Recipient.sol +++ b/test/foundry/utils/ERC721Recipient.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; -import { ERC721TokenReceiver } from "@rari-capital/solmate/src/tokens/ERC721.sol"; +import { + ERC721TokenReceiver +} from "@rari-capital/solmate/src/tokens/ERC721.sol"; contract ERC721Recipient is ERC721TokenReceiver { function onERC721Received( diff --git a/test/foundry/utils/ExternalCounter.sol b/test/foundry/utils/ExternalCounter.sol index 290563785..376bb8ff7 100644 --- a/test/foundry/utils/ExternalCounter.sol +++ b/test/foundry/utils/ExternalCounter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; contract ExternalCounter { uint256 public value; diff --git a/test/foundry/utils/OfferConsiderationItemAdder.sol b/test/foundry/utils/OfferConsiderationItemAdder.sol index 989378b92..b0f0860ab 100644 --- a/test/foundry/utils/OfferConsiderationItemAdder.sol +++ b/test/foundry/utils/OfferConsiderationItemAdder.sol @@ -1,7 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; + +import { + ConsiderationItem, + OfferItem, + ItemType, + SpentItem +} from "../../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationItem, OfferItem, ItemType } from "../../../contracts/lib/ConsiderationStructs.sol"; import { TestTokenMinter } from "./TestTokenMinter.sol"; contract OfferConsiderationItemAdder is TestTokenMinter { @@ -9,22 +15,49 @@ contract OfferConsiderationItemAdder is TestTokenMinter { ConsiderationItem considerationItem; OfferItem[] offerItems; ConsiderationItem[] considerationItems; + SpentItem[] minimumReceived; + SpentItem[] maximumSpent; - function addConsiderationItem( - address payable recipient, + ///@dev Add an offer item to the offer items array + function addOfferItem(OfferItem memory _offerItem) internal { + offerItems.push(_offerItem); + } + + ///@dev reset the offer items array + function resetOfferItems() internal { + delete offerItems; + } + + ///@dev Construct and an offer item to the offer items array + function addOfferItem( ItemType itemType, + address token, uint256 identifier, - uint256 amt + uint256 startAmount, + uint256 endAmount ) internal { - if (itemType == ItemType.NATIVE) { - addEthConsiderationItem(recipient, amt); - } else if (itemType == ItemType.ERC20) { - addErc20ConsiderationItem(recipient, amt); - } else if (itemType == ItemType.ERC1155) { - addErc1155ConsiderationItem(recipient, identifier, amt); - } else { - addErc721ConsiderationItem(recipient, identifier); - } + offerItem.itemType = itemType; + offerItem.token = token; + offerItem.identifierOrCriteria = identifier; + offerItem.startAmount = startAmount; + offerItem.endAmount = endAmount; + addOfferItem(offerItem); + delete offerItem; + } + + function addOfferItem( + ItemType itemType, + address token, + uint256 identifier, + uint256 amount + ) internal { + addOfferItem({ + itemType: itemType, + token: token, + identifier: identifier, + startAmount: amount, + endAmount: amount + }); } function addOfferItem( @@ -93,9 +126,10 @@ contract OfferConsiderationItemAdder is TestTokenMinter { ); } - function addErc20OfferItem(uint256 startAmount, uint256 endAmount) - internal - { + function addErc20OfferItem( + uint256 startAmount, + uint256 endAmount + ) internal { addOfferItem( ItemType.ERC20, address(token1), @@ -131,6 +165,54 @@ contract OfferConsiderationItemAdder is TestTokenMinter { addEthOfferItem(paymentAmount, paymentAmount); } + ///@dev add a considerationItem to the considerationItems array + function addConsiderationItem( + ConsiderationItem memory _considerationItem + ) internal { + considerationItems.push(_considerationItem); + } + + ///@dev reset the considerationItems array + function resetConsiderationItems() internal { + delete considerationItems; + } + + ///@dev construct a considerationItem and add it to the considerationItems array + function addConsiderationItem( + address payable recipient, + ItemType itemType, + address token, + uint256 identifier, + uint256 startAmount, + uint256 endAmount + ) internal { + considerationItem.itemType = itemType; + considerationItem.token = token; + considerationItem.identifierOrCriteria = identifier; + considerationItem.startAmount = startAmount; + considerationItem.endAmount = endAmount; + considerationItem.recipient = recipient; + addConsiderationItem(considerationItem); + delete considerationItem; + } + + function addConsiderationItem( + address payable recipient, + ItemType itemType, + uint256 identifier, + uint256 amt + ) internal { + if (itemType == ItemType.NATIVE) { + addEthConsiderationItem(recipient, amt); + } else if (itemType == ItemType.ERC20) { + addErc20ConsiderationItem(recipient, amt); + } else if (itemType == ItemType.ERC1155) { + addErc1155ConsiderationItem(recipient, identifier, amt); + } else { + addErc721ConsiderationItem(recipient, identifier); + } + } + function addEthConsiderationItem( address payable recipient, uint256 paymentAmount @@ -210,36 +292,4 @@ contract OfferConsiderationItemAdder is TestTokenMinter { amount ); } - - function addOfferItem( - ItemType itemType, - address token, - uint256 identifier, - uint256 startAmount, - uint256 endAmount - ) internal { - offerItem.itemType = itemType; - offerItem.token = token; - offerItem.identifierOrCriteria = identifier; - offerItem.startAmount = startAmount; - offerItem.endAmount = endAmount; - offerItems.push(offerItem); - } - - function addConsiderationItem( - address payable recipient, - ItemType itemType, - address token, - uint256 identifier, - uint256 startAmount, - uint256 endAmount - ) internal { - considerationItem.itemType = itemType; - considerationItem.token = token; - considerationItem.identifierOrCriteria = identifier; - considerationItem.startAmount = startAmount; - considerationItem.endAmount = endAmount; - considerationItem.recipient = recipient; - considerationItems.push(considerationItem); - } } diff --git a/test/foundry/utils/OrderBuilder.sol b/test/foundry/utils/OrderBuilder.sol new file mode 100644 index 000000000..878399fc1 --- /dev/null +++ b/test/foundry/utils/OrderBuilder.sol @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { OfferConsiderationItemAdder } from "./OfferConsiderationItemAdder.sol"; + +import { + OrderParameters, + OrderComponents, + Order, + BasicOrderParameters, + SpentItem, + AdditionalRecipient, + OfferItem, + ConsiderationItem, + Fulfillment, + FulfillmentComponent, + AdvancedOrder +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + OrderType, + BasicOrderType +} from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +contract OrderBuilder is OfferConsiderationItemAdder { + uint256 internal globalSalt; + + FulfillmentComponent[] offerComponents; + FulfillmentComponent[] considerationComponents; + + Fulfillment fulfillment; + Fulfillment[] fulfillments; + + FulfillmentComponent fulfillmentComponent; + FulfillmentComponent[] fulfillmentComponents; + + FulfillmentComponent[][] offerComponentsArray; + FulfillmentComponent[][] considerationComponentsArray; + + OrderParameters baseOrderParameters; + OrderComponents baseOrderComponents; + + function toSpentItem( + OfferItem memory _offerItem + ) internal pure returns (SpentItem memory) { + return + SpentItem({ + itemType: _offerItem.itemType, + token: _offerItem.token, + identifier: _offerItem.identifierOrCriteria, + amount: _offerItem.startAmount + }); + } + + function toSpentItem( + ConsiderationItem memory _considerationItem + ) internal pure returns (SpentItem memory) { + return + SpentItem({ + itemType: _considerationItem.itemType, + token: _considerationItem.token, + identifier: _considerationItem.identifierOrCriteria, + amount: _considerationItem.startAmount + }); + } + + function toSpentItems( + OfferItem[] memory _offerItems + ) internal pure returns (SpentItem[] memory) { + SpentItem[] memory spentItems = new SpentItem[](_offerItems.length); + for (uint256 i; i < _offerItems.length; ++i) { + spentItems[i] = toSpentItem(_offerItems[i]); + } + return spentItems; + } + + function toSpentItems( + ConsiderationItem[] memory _considerationItems + ) internal pure returns (SpentItem[] memory) { + SpentItem[] memory spentItems = new SpentItem[]( + _considerationItems.length + ); + for (uint256 i; i < _considerationItems.length; ++i) { + spentItems[i] = toSpentItem(_considerationItems[i]); + } + return spentItems; + } + + function toHashedLeaves( + uint256[] memory identifiers + ) internal pure returns (bytes32[] memory) { + bytes32[] memory hashedLeaves = new bytes32[](identifiers.length); + for (uint256 i; i < identifiers.length; ++i) { + hashedLeaves[i] = keccak256(abi.encode(identifiers[i])); + } + return hashedLeaves; + } + + function toAdvancedOrder( + Order memory order + ) internal pure returns (AdvancedOrder memory) { + return + AdvancedOrder({ + parameters: order.parameters, + numerator: 1, + denominator: 1, + signature: order.signature, + extraData: "" + }); + } + + function toAdvancedOrder( + Order memory order, + bytes memory extraData + ) internal pure returns (AdvancedOrder memory) { + return + AdvancedOrder({ + parameters: order.parameters, + numerator: 1, + denominator: 1, + signature: order.signature, + extraData: extraData + }); + } + + function createMirrorOrderAndFulfillments( + ConsiderationInterface _consideration, + OrderParameters memory order1 + ) internal returns (Order memory, Fulfillment[] memory) { + Order memory mirrorOrder = createSignedMirrorOrder( + _consideration, + order1, + "mirror offerer" + ); + return ( + mirrorOrder, + createFulfillmentsFromMirrorOrders(order1, mirrorOrder.parameters) + ); + } + + function createFulfillmentsFromMirrorOrders( + OrderParameters memory order1, + OrderParameters memory order2 + ) internal returns (Fulfillment[] memory) { + delete fulfillments; + for (uint256 i; i < order1.offer.length; ++i) { + createFulfillmentFromComponentsAndAddToFulfillments({ + _offer: FulfillmentComponent({ orderIndex: 0, itemIndex: i }), + _consideration: FulfillmentComponent({ + orderIndex: 1, + itemIndex: i + }) + }); + } + for (uint256 i; i < order2.offer.length; ++i) { + createFulfillmentFromComponentsAndAddToFulfillments({ + _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: i }), + _consideration: FulfillmentComponent({ + orderIndex: 0, + itemIndex: i + }) + }); + } + + return fulfillments; + } + + function createFulfillments( + OrderParameters[] memory orders + ) + internal + returns ( + FulfillmentComponent[][] memory, + FulfillmentComponent[][] memory + ) + { + delete offerComponentsArray; + delete considerationComponentsArray; + for (uint256 i; i < orders.length; ++i) { + addFulfillmentsForOrderParams(orders[i], i); + } + return (offerComponentsArray, considerationComponentsArray); + } + + function addFulfillmentsForOrderParams( + OrderParameters memory params, + uint256 orderIndex + ) internal { + // create individual fulfillments for each offerItem + for (uint256 i; i < params.offer.length; ++i) { + addSingleFulfillmentComponentsTo({ + component: FulfillmentComponent({ + orderIndex: orderIndex, + itemIndex: i + }), + target: offerComponentsArray + }); + } + // create individual fulfillments for each considerationItem + for (uint256 i; i < params.consideration.length; ++i) { + addSingleFulfillmentComponentsTo({ + component: FulfillmentComponent({ + orderIndex: orderIndex, + itemIndex: i + }), + target: considerationComponentsArray + }); + } + } + + function addSingleFulfillmentComponentsTo( + FulfillmentComponent memory component, + FulfillmentComponent[][] storage target + ) internal { + delete fulfillmentComponents; + fulfillmentComponents.push(component); + target.push(fulfillmentComponents); + } + + function createFulfillmentFromComponentsAndAddToFulfillments( + FulfillmentComponent memory _offer, + FulfillmentComponent memory _consideration + ) internal { + delete offerComponents; + delete considerationComponents; + offerComponents.push(_offer); + considerationComponents.push(_consideration); + fulfillment.offerComponents = offerComponents; + fulfillment.considerationComponents = considerationComponents; + fulfillments.push(fulfillment); + } + + function createSignedOrder( + ConsiderationInterface _consideration, + string memory offerer + ) internal returns (Order memory) { + (address offererAddr, uint256 pkey) = makeAddrAndKey(offerer); + configureOrderParameters(offererAddr); + configureOrderComponents(_consideration); + bytes32 orderHash = _consideration.getOrderHash(baseOrderComponents); + + Order memory order = Order({ + parameters: baseOrderParameters, + signature: signOrder(_consideration, pkey, orderHash) + }); + delete offerItems; + delete considerationItems; + delete baseOrderComponents; + delete baseOrderParameters; + return order; + } + + function createSignedMirrorOrder( + ConsiderationInterface _consideration, + OrderParameters memory originalParameters, + string memory mirrorOfferer + ) internal returns (Order memory) { + (address offerer, uint256 pkey) = makeAddrAndKey(mirrorOfferer); + + ( + OfferItem[] memory newOffer, + ConsiderationItem[] memory newConsideration + ) = mirrorOfferAndConsideration( + originalParameters.offer, + originalParameters.consideration, + offerer + ); + baseOrderParameters.offerer = offerer; + baseOrderParameters.zone = originalParameters.zone; + setOfferItems(baseOrderParameters.offer, newOffer); + setConsiderationItems( + baseOrderParameters.consideration, + newConsideration + ); + baseOrderParameters.orderType = originalParameters.orderType; + baseOrderParameters.startTime = originalParameters.startTime; + baseOrderParameters.endTime = originalParameters.endTime; + baseOrderParameters.zoneHash = originalParameters.zoneHash; + baseOrderParameters.salt = globalSalt++; + baseOrderParameters.conduitKey = originalParameters.conduitKey; + baseOrderParameters.totalOriginalConsiderationItems = originalParameters + .offer + .length; + + configureOrderComponents(_consideration); + bytes32 orderHash = _consideration.getOrderHash(baseOrderComponents); + Order memory order = Order({ + parameters: baseOrderParameters, + signature: signOrder(_consideration, pkey, orderHash) + }); + + delete offerItems; + delete considerationItems; + delete baseOrderComponents; + delete baseOrderParameters; + return order; + } + + function mirrorOfferAndConsideration( + OfferItem[] memory _offer, + ConsiderationItem[] memory _consideration, + address mirrorOfferer + ) + internal + pure + returns ( + OfferItem[] memory newOffer, + ConsiderationItem[] memory newConsideration + ) + { + return ( + mirrorConsiderationItems(_consideration), + mirrorOfferItems(_offer, payable(mirrorOfferer)) + ); + } + + function mirrorOfferItems( + OfferItem[] memory _offers, + address payable recipient + ) internal pure returns (ConsiderationItem[] memory) { + ConsiderationItem[] memory newConsideration = new ConsiderationItem[]( + _offers.length + ); + for (uint256 i = 0; i < _offers.length; i++) { + newConsideration[i] = mirrorOfferItem(_offers[i], recipient); + } + return newConsideration; + } + + function mirrorOfferItem( + OfferItem memory _offer, + address payable recipient + ) internal pure returns (ConsiderationItem memory) { + return + ConsiderationItem({ + itemType: _offer.itemType, + token: _offer.token, + identifierOrCriteria: _offer.identifierOrCriteria, + startAmount: _offer.startAmount, + endAmount: _offer.endAmount, + recipient: recipient + }); + } + + function mirrorConsiderationItems( + ConsiderationItem[] memory _considerations + ) internal pure returns (OfferItem[] memory) { + OfferItem[] memory newOffer = new OfferItem[](_considerations.length); + for (uint256 i = 0; i < _considerations.length; i++) { + newOffer[i] = mirrorConsiderationItem(_considerations[i]); + } + return newOffer; + } + + function mirrorConsiderationItem( + ConsiderationItem memory _consideration + ) internal pure returns (OfferItem memory) { + return + OfferItem({ + itemType: _consideration.itemType, + token: _consideration.token, + identifierOrCriteria: _consideration.identifierOrCriteria, + startAmount: _consideration.startAmount, + endAmount: _consideration.endAmount + }); + } + + function configureOrderParameters(address offerer) internal { + configureOrderParameters(offerer, address(0), bytes32(0)); + } + + function configureOrderParameters( + address offerer, + OrderType orderType + ) internal { + configureOrderParameters(offerer, address(0), bytes32(0)); + baseOrderParameters.orderType = orderType; + } + + function configureOrderParameters( + address offerer, + address zone, + bytes32 zoneHash + ) internal { + _configureOrderParameters(offerer, zone, zoneHash, globalSalt++, false); + } + + function _configureOrderParameters( + address offerer, + address zone, + bytes32 zoneHash, + uint256 salt, + bool useConduit + ) internal { + _configureOrderParameters( + offerer, + zone, + zoneHash, + salt, + OrderType.FULL_OPEN, + useConduit + ); + } + + function _configureOrderParameters( + address offerer, + address zone, + bytes32 zoneHash, + uint256 salt, + OrderType orderType, + bool useConduit + ) internal { + bytes32 conduitKey = useConduit ? conduitKeyOne : bytes32(0); + baseOrderParameters.offerer = offerer; + baseOrderParameters.zone = zone; + baseOrderParameters.offer = offerItems; + baseOrderParameters.consideration = considerationItems; + baseOrderParameters.orderType = orderType; + baseOrderParameters.startTime = block.timestamp; + baseOrderParameters.endTime = block.timestamp + 1; + baseOrderParameters.zoneHash = zoneHash; + baseOrderParameters.salt = salt; + baseOrderParameters.conduitKey = conduitKey; + baseOrderParameters.totalOriginalConsiderationItems = considerationItems + .length; + } + + function _configureOrderParametersSetEndTime( + address offerer, + address zone, + uint256 endTime, + bytes32 zoneHash, + uint256 salt, + bool useConduit + ) internal { + _configureOrderParameters(offerer, zone, zoneHash, salt, useConduit); + baseOrderParameters.endTime = endTime; + } + + function configureOrderComponents( + ConsiderationInterface _consideration + ) internal { + configureOrderComponents( + _consideration.getCounter(baseOrderParameters.offerer) + ); + } + + /** + * @dev configures order components based on order parameters in storage and counter param + */ + function configureOrderComponents(uint256 counter) internal { + baseOrderComponents.offerer = baseOrderParameters.offerer; + baseOrderComponents.zone = baseOrderParameters.zone; + baseOrderComponents.offer = baseOrderParameters.offer; + baseOrderComponents.consideration = baseOrderParameters.consideration; + baseOrderComponents.orderType = baseOrderParameters.orderType; + baseOrderComponents.startTime = baseOrderParameters.startTime; + baseOrderComponents.endTime = baseOrderParameters.endTime; + baseOrderComponents.zoneHash = baseOrderParameters.zoneHash; + baseOrderComponents.salt = baseOrderParameters.salt; + baseOrderComponents.conduitKey = baseOrderParameters.conduitKey; + baseOrderComponents.counter = counter; + } + + function toBasicOrderParameters( + Order memory _order, + BasicOrderType basicOrderType + ) internal pure returns (BasicOrderParameters memory) { + AdditionalRecipient[] + memory additionalRecipients = new AdditionalRecipient[]( + _order.parameters.consideration.length - 1 + ); + for (uint256 i = 1; i < _order.parameters.consideration.length; i++) { + additionalRecipients[i - 1] = AdditionalRecipient({ + recipient: _order.parameters.consideration[i].recipient, + amount: _order.parameters.consideration[i].startAmount + }); + } + + return + BasicOrderParameters( + _order.parameters.consideration[0].token, + _order.parameters.consideration[0].identifierOrCriteria, + _order.parameters.consideration[0].endAmount, + payable(_order.parameters.offerer), + _order.parameters.zone, + _order.parameters.offer[0].token, + _order.parameters.offer[0].identifierOrCriteria, + _order.parameters.offer[0].endAmount, + basicOrderType, + _order.parameters.startTime, + _order.parameters.endTime, + _order.parameters.zoneHash, + _order.parameters.salt, + _order.parameters.conduitKey, + _order.parameters.conduitKey, + _order.parameters.totalOriginalConsiderationItems - 1, + additionalRecipients, + _order.signature + ); + } + + function toBasicOrderParameters( + OrderComponents memory _order, + BasicOrderType basicOrderType, + bytes memory signature + ) internal pure returns (BasicOrderParameters memory) { + return + BasicOrderParameters( + _order.consideration[0].token, + _order.consideration[0].identifierOrCriteria, + _order.consideration[0].endAmount, + payable(_order.offerer), + _order.zone, + _order.offer[0].token, + _order.offer[0].identifierOrCriteria, + _order.offer[0].endAmount, + basicOrderType, + _order.startTime, + _order.endTime, + _order.zoneHash, + _order.salt, + _order.conduitKey, + _order.conduitKey, + 0, + new AdditionalRecipient[](0), + signature + ); + } +} diff --git a/test/foundry/utils/PseudoRandom.sol b/test/foundry/utils/PseudoRandom.sol index dc438c569..a72b70b72 100644 --- a/test/foundry/utils/PseudoRandom.sol +++ b/test/foundry/utils/PseudoRandom.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; contract PseudoRandom { bytes32 seedHash; diff --git a/test/foundry/utils/StructCopier.sol b/test/foundry/utils/StructCopier.sol index ac8f08491..ec41a14d4 100644 --- a/test/foundry/utils/StructCopier.sol +++ b/test/foundry/utils/StructCopier.sol @@ -1,7 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.13; -import { BasicOrderParameters, CriteriaResolver, AdvancedOrder, AdditionalRecipient, OfferItem, Order, ConsiderationItem, Fulfillment, FulfillmentComponent, OrderParameters, OrderComponents } from "../../../contracts/lib/ConsiderationStructs.sol"; -import { ConsiderationInterface } from "../../../contracts/interfaces/ConsiderationInterface.sol"; +pragma solidity ^0.8.17; + +import { + BasicOrderParameters, + CriteriaResolver, + AdvancedOrder, + AdditionalRecipient, + OfferItem, + Order, + ConsiderationItem, + Fulfillment, + FulfillmentComponent, + OrderParameters, + OrderComponents +} from "../../../contracts/lib/ConsiderationStructs.sol"; contract StructCopier { Order _tempOrder; @@ -65,9 +77,10 @@ contract StructCopier { } } - function setBytes32Array(bytes32[] storage dest, bytes32[] memory src) - internal - { + function setBytes32Array( + bytes32[] storage dest, + bytes32[] memory src + ) internal { while (dest.length != 0) { dest.pop(); } @@ -149,9 +162,10 @@ contract StructCopier { .totalOriginalConsiderationItems; } - function setOfferItems(OfferItem[] storage dest, OfferItem[] memory src) - internal - { + function setOfferItems( + OfferItem[] storage dest, + OfferItem[] memory src + ) internal { while (dest.length != 0) { dest.pop(); } @@ -172,9 +186,10 @@ contract StructCopier { } } - function setFulfillment(Fulfillment storage dest, Fulfillment memory src) - internal - { + function setFulfillment( + Fulfillment storage dest, + Fulfillment memory src + ) internal { setFulfillmentComponents(dest.offerComponents, src.offerComponents); setFulfillmentComponents( dest.considerationComponents, @@ -246,11 +261,9 @@ contract StructCopier { return considerationItems; } - function toOfferItems(ConsiderationItem[] memory _considerationItems) - internal - pure - returns (OfferItem[] memory) - { + function toOfferItems( + ConsiderationItem[] memory _considerationItems + ) internal pure returns (OfferItem[] memory) { OfferItem[] memory _offerItems = new OfferItem[]( _considerationItems.length ); diff --git a/test/foundry/utils/TestTokenMinter.sol b/test/foundry/utils/TestTokenMinter.sol index 623d63aa1..d904e01a5 100644 --- a/test/foundry/utils/TestTokenMinter.sol +++ b/test/foundry/utils/TestTokenMinter.sol @@ -1,5 +1,5 @@ // SPDX-Identifier: MIT -pragma solidity >=0.8.7; +pragma solidity ^0.8.13; import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; @@ -8,12 +8,12 @@ import { ERC721Recipient } from "./ERC721Recipient.sol"; import { ERC1155Recipient } from "./ERC1155Recipient.sol"; import { ItemType } from "../../../contracts/lib/ConsiderationEnums.sol"; import { BaseConsiderationTest } from "./BaseConsiderationTest.sol"; -import { ERC721 } from "../token/ERC721.sol"; +import { CustomERC721 } from "../token/CustomERC721.sol"; -contract PreapprovedERC721 is ERC721 { +contract PreapprovedERC721 is CustomERC721 { mapping(address => bool) public preapprovals; - constructor(address[] memory preapproved) ERC721("", "") { + constructor(address[] memory preapproved) CustomERC721("", "") { for (uint256 i = 0; i < preapproved.length; i++) { preapprovals[preapproved[i]] = true; } @@ -24,12 +24,10 @@ contract PreapprovedERC721 is ERC721 { return true; } - function isApprovedForAll(address owner, address operator) - public - view - override - returns (bool) - { + function isApprovedForAll( + address owner, + address operator + ) public view override returns (bool) { return preapprovals[operator] || super.isApprovedForAll(owner, operator); } @@ -73,17 +71,25 @@ contract TestTokenMinter is address[] preapprovals; modifier only1155Receiver(address recipient) { - vm.assume(recipient != address(0)); + vm.assume( + recipient != address(0) && + recipient != 0x4c8D290a1B368ac4728d83a9e8321fC3af2b39b1 && + recipient != 0x4e59b44847b379578588920cA78FbF26c0B4956C + ); + if (recipient.code.length > 0) { - try - ERC1155Recipient(recipient).onERC1155Received( + (bool success, bytes memory returnData) = recipient.call( + abi.encodeWithSelector( + ERC1155Recipient.onERC1155Received.selector, address(1), address(1), 1, 1, "" ) - returns (bytes4 response) { + ); + vm.assume(success); + try this.decodeBytes4(returnData) returns (bytes4 response) { vm.assume(response == onERC1155Received.selector); } catch (bytes memory reason) { vm.assume(false); @@ -92,6 +98,10 @@ contract TestTokenMinter is _; } + function decodeBytes4(bytes memory data) external pure returns (bytes4) { + return abi.decode(data, (bytes4)); + } + function setUp() public virtual override { super.setUp(); @@ -118,6 +128,14 @@ contract TestTokenMinter is allocateTokensAndApprovals(cal, uint128(MAX_INT)); } + function makeAddrWithAllocationsAndApprovals( + string memory label + ) internal returns (address) { + address addr = makeAddr(label); + allocateTokensAndApprovals(addr, uint128(MAX_INT)); + return addr; + } + function mintErc721TokenTo(address to, uint256 id) internal { mintErc721TokenTo(to, test721_1, id); } @@ -203,7 +221,7 @@ contract TestTokenMinter is } /** - @dev deploy test token contracts + * @dev deploy test token contracts */ function _deployTestTokenContracts() internal { token1 = new TestERC20(); @@ -221,19 +239,16 @@ contract TestTokenMinter is vm.label(address(test721_1), "test721_1"); vm.label(address(test1155_1), "test1155_1"); vm.label(address(preapproved721), "preapproved721"); - - emit log("Deployed test token contracts"); } /** - @dev allocate amount of each token, 1 of each 721, and 1, 5, and 10 of respective 1155s - */ + * @dev allocate amount of each token, 1 of each 721, and 1, 5, and 10 of respective 1155s + */ function allocateTokensAndApprovals(address _to, uint128 _amount) internal { vm.deal(_to, _amount); for (uint256 i = 0; i < erc20s.length; ++i) { erc20s[i].mint(_to, _amount); } - emit log_named_address("Allocated tokens to", _to); _setApprovals(_to); } @@ -262,13 +277,5 @@ contract TestTokenMinter is } vm.stopPrank(); - emit log_named_address( - "Owner proxy approved for all tokens from", - _owner - ); - emit log_named_address( - "Consideration approved for all tokens from", - _owner - ); } } diff --git a/test/foundry/utils/reentrancy/ReentrantEnums.sol b/test/foundry/utils/reentrancy/ReentrantEnums.sol index e3b03d725..187f4ea27 100644 --- a/test/foundry/utils/reentrancy/ReentrantEnums.sol +++ b/test/foundry/utils/reentrancy/ReentrantEnums.sol @@ -1,11 +1,12 @@ //SPDX-License-Identifier: Unlicense -pragma solidity >=0.8.13; +pragma solidity ^0.8.17; /** * @dev Enum of functions that set the reentrancy guard */ enum EntryPoint { FulfillBasicOrder, + FulfillBasicOrderEfficient, FulfillOrder, FulfillAdvancedOrder, FulfillAvailableOrders, @@ -19,6 +20,7 @@ enum EntryPoint { */ enum ReentryPoint { FulfillBasicOrder, + FulfillBasicOrderEfficient, FulfillOrder, FulfillAdvancedOrder, FulfillAvailableOrders, diff --git a/test/foundry/utils/reentrancy/ReentrantStructs.sol b/test/foundry/utils/reentrancy/ReentrantStructs.sol index 0e089b552..ee3e3b89e 100644 --- a/test/foundry/utils/reentrancy/ReentrantStructs.sol +++ b/test/foundry/utils/reentrancy/ReentrantStructs.sol @@ -1,6 +1,14 @@ //SPDX-License-Identifier: Unlicense -pragma solidity >=0.8.13; -import { BasicOrderParameters, OfferItem, ConsiderationItem, OrderParameters, OrderComponents, Fulfillment, FulfillmentComponent, Execution, Order, AdvancedOrder, OrderStatus, CriteriaResolver } from "../../../../contracts/lib/ConsiderationStructs.sol"; +pragma solidity ^0.8.17; +import { + BasicOrderParameters, + OrderComponents, + Fulfillment, + FulfillmentComponent, + Order, + AdvancedOrder, + CriteriaResolver +} from "../../../../contracts/lib/ConsiderationStructs.sol"; struct FulfillBasicOrderParameters { BasicOrderParameters parameters; diff --git a/test/foundry/zone/PostFulfillmentCheck.t.sol b/test/foundry/zone/PostFulfillmentCheck.t.sol new file mode 100644 index 000000000..6426f5d5a --- /dev/null +++ b/test/foundry/zone/PostFulfillmentCheck.t.sol @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { BaseOrderTest } from "../utils/BaseOrderTest.sol"; + +import { TestZone } from "./impl/TestZone.sol"; + +import { + PostFulfillmentStatefulTestZone +} from "./impl/PostFullfillmentStatefulTestZone.sol"; + +import { + ConsiderationItem, + OfferItem, + ItemType, + AdvancedOrder, + CriteriaResolver, + BasicOrderParameters, + AdditionalRecipient, + FulfillmentComponent +} from "../../../contracts/lib/ConsiderationStructs.sol"; + +import { + OrderType, + Side, + BasicOrderType +} from "../../../contracts/lib/ConsiderationEnums.sol"; + +import { + ConsiderationInterface +} from "../../../contracts/interfaces/ConsiderationInterface.sol"; + +contract PostFulfillmentCheckTest is BaseOrderTest { + TestZone zone = new TestZone(); + PostFulfillmentStatefulTestZone statefulZone = + new PostFulfillmentStatefulTestZone(50); + + struct Context { + ConsiderationInterface consideration; + uint8 numOriginalAdditional; + uint8 numTips; + } + struct EthConsideration { + address payable recipient; + uint256 amount; + } + + function test( + function(Context memory) external fn, + Context memory context + ) internal { + try fn(context) { + fail(); + } catch (bytes memory reason) { + assertPass(reason); + } + } + + function setUp() public override { + super.setUp(); + vm.label(address(zone), "TestZone"); + } + + function testAscendingAmount() public { + test( + this.execAscendingAmount, + Context({ + consideration: consideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + test( + this.execAscendingAmount, + Context({ + consideration: referenceConsideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + } + + function execAscendingAmount(Context memory context) public stateless { + addErc20OfferItem(1, 101); + addErc721ConsiderationItem(alice, 42); + test721_1.mint(address(this), 42); + + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + _configureOrderParameters({ + offerer: alice, + zone: address(statefulZone), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.startTime = 1; + baseOrderParameters.endTime = 101; + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: signature, + extraData: "extradata" + }); + CriteriaResolver[] memory criteriaResolvers; + vm.warp(50); + context.consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + } + + function testResolvedCriteria() public { + test( + this.execResolvedCriteria, + Context({ + consideration: consideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + test( + this.execResolvedCriteria, + Context({ + consideration: referenceConsideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + } + + function execResolvedCriteria(Context memory context) public stateless { + addErc20OfferItem(1, 101); + addErc721ConsiderationItem(alice, 0); + considerationItems[0].itemType = ItemType.ERC721_WITH_CRITERIA; + test721_1.mint(address(this), 42); + + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + _configureOrderParameters({ + offerer: alice, + zone: address(statefulZone), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.startTime = 1; + baseOrderParameters.endTime = 101; + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: signature, + extraData: "extradata" + }); + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](1); + criteriaResolvers[0] = CriteriaResolver({ + orderIndex: 0, + side: Side.CONSIDERATION, + index: 0, + identifier: 42, + criteriaProof: new bytes32[](0) + }); + vm.warp(50); + context.consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + } + + function testStateChange() public { + test( + this.execStateChange, + Context({ + consideration: consideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + test( + this.execStateChange, + Context({ + consideration: referenceConsideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + } + + function execStateChange(Context memory context) public stateless { + addErc20OfferItem(1, 101); + addErc721ConsiderationItem(alice, 0); + considerationItems[0].itemType = ItemType.ERC721_WITH_CRITERIA; + test721_1.mint(address(this), 42); + + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + _configureOrderParameters({ + offerer: alice, + zone: address(statefulZone), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.startTime = 1; + baseOrderParameters.endTime = 101; + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: signature, + extraData: "extradata" + }); + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](1); + criteriaResolvers[0] = CriteriaResolver({ + orderIndex: 0, + side: Side.CONSIDERATION, + index: 0, + identifier: 42, + criteriaProof: new bytes32[](0) + }); + vm.warp(50); + context.consideration.fulfillAdvancedOrder({ + advancedOrder: order, + criteriaResolvers: criteriaResolvers, + fulfillerConduitKey: bytes32(0), + recipient: address(0) + }); + + assertTrue(statefulZone.called()); + } + + function testBasicStateful() public { + test( + this.execBasicStateful, + Context({ + consideration: consideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + test( + this.execBasicStateful, + Context({ + consideration: referenceConsideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + } + + function execBasicStateful(Context memory context) public stateless { + addErc20OfferItem(50); + addErc721ConsiderationItem(alice, 42); + addErc20ConsiderationItem(bob, 1); + addErc20ConsiderationItem(cal, 1); + + test721_1.mint(address(this), 42); + + _configureOrderParameters({ + offerer: alice, + zone: address(statefulZone), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.startTime = 1; + baseOrderParameters.endTime = 101; + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + BasicOrderParameters + memory basicOrderParameters = toBasicOrderParameters( + baseOrderComponents, + BasicOrderType.ERC721_TO_ERC20_FULL_RESTRICTED, + signature + ); + basicOrderParameters.additionalRecipients = new AdditionalRecipient[]( + 2 + ); + basicOrderParameters.additionalRecipients[0] = AdditionalRecipient({ + recipient: bob, + amount: 1 + }); + basicOrderParameters.additionalRecipients[1] = AdditionalRecipient({ + recipient: cal, + amount: 1 + }); + basicOrderParameters.totalOriginalAdditionalRecipients = 2; + vm.warp(50); + context.consideration.fulfillBasicOrder({ + parameters: basicOrderParameters + }); + } + + function testBasicStateful( + uint8 numOriginalAdditional, + uint8 numTips + ) public { + test( + this.execBasicStatefulFuzz, + Context({ + consideration: consideration, + numOriginalAdditional: numOriginalAdditional, + numTips: numTips + }) + ); + test( + this.execBasicStatefulFuzz, + Context({ + consideration: referenceConsideration, + numOriginalAdditional: numOriginalAdditional, + numTips: numTips + }) + ); + } + + function execBasicStatefulFuzz(Context memory context) external stateless { + // keep track of each additional recipient so we can check their balances + address[] memory allAdditional = new address[]( + uint256(context.numOriginalAdditional) + context.numTips + ); + // make new stateful zone with a larger amount so each additional recipient can receive + statefulZone = new PostFulfillmentStatefulTestZone(5000); + // clear storage array just in case + delete additionalRecipients; + + // create core order + addErc20OfferItem(5000); + addErc721ConsiderationItem(alice, 42); + + // loop over original additional + for (uint256 i = 0; i < context.numOriginalAdditional; i++) { + // create specific labeled address + address payable recipient = payable( + makeAddr(string.concat("original additional ", vm.toString(i))) + ); + // add to all additional + allAdditional[i] = recipient; + // add to consideration items that will be hashed with order + addErc20ConsiderationItem(recipient, 1); + // add to the additional recipients array included with the basic order + additionalRecipients.push( + AdditionalRecipient({ recipient: recipient, amount: 1 }) + ); + } + // do the same with additional recipients + for (uint256 i = 0; i < context.numTips; i++) { + // create specific labeled address + address payable recipient = payable( + makeAddr(string.concat("additional ", vm.toString(i))) + ); + // add to all additional + allAdditional[i + context.numOriginalAdditional] = recipient; + // do not add to consideration items that will be hashed with order + // add to the additional recipients array included with the basic order + additionalRecipients.push( + AdditionalRecipient({ recipient: recipient, amount: 1 }) + ); + } + + // mint token to fulfiller + test721_1.mint(address(this), 42); + + // configure order parameters + _configureOrderParameters({ + offerer: alice, + zone: address(statefulZone), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + // override settings parameters + baseOrderParameters.startTime = 1; + baseOrderParameters.endTime = 101; + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + + // configure order components for signing + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + // convert to basic order parameters + BasicOrderParameters + memory basicOrderParameters = toBasicOrderParameters( + baseOrderComponents, + BasicOrderType.ERC721_TO_ERC20_FULL_RESTRICTED, + signature + ); + // update additional recipients + basicOrderParameters.additionalRecipients = additionalRecipients; + basicOrderParameters.totalOriginalAdditionalRecipients = context + .numOriginalAdditional; + context.consideration.fulfillBasicOrder({ + parameters: basicOrderParameters + }); + + // assertions + assertTrue(statefulZone.called()); + for (uint256 i = 0; i < allAdditional.length; i++) { + assertEq( + token1.balanceOf(allAdditional[i]), + 1, + "additional recipient has incorrect balance" + ); + } + } + + function testFulfillAvailableAdvancedAscending() public { + test( + this.execFulfillAvailableAdvancedAscending, + Context({ + consideration: consideration, + numOriginalAdditional: 0, + numTips: 0 + }) + ); + // todo: fix ref impl + // test( + // this.execFulfillAvailableAdvancedAscending, + // Context({ + // consideration: referenceConsideration, + // numOriginalAdditional: 0, + // numTips: 0 + // }) + // ); + } + + function execFulfillAvailableAdvancedAscending( + Context memory context + ) external stateless { + addErc20OfferItem(1, 101); + addErc721ConsiderationItem(alice, 42); + test721_1.mint(address(this), 42); + + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + _configureOrderParameters({ + offerer: alice, + zone: address(statefulZone), + zoneHash: bytes32(0), + salt: 0, + useConduit: false + }); + baseOrderParameters.startTime = 1; + baseOrderParameters.endTime = 101; + baseOrderParameters.orderType = OrderType.FULL_RESTRICTED; + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + alicePk, + orderHash + ); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: 1, + denominator: 1, + signature: signature, + extraData: "extradata" + }); + CriteriaResolver[] memory criteriaResolvers; + + offerComponents.push( + FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }) + ); + offerComponentsArray.push(offerComponents); + + considerationComponents.push( + FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }) + ); + considerationComponentsArray.push(considerationComponents); + AdvancedOrder[] memory orders = new AdvancedOrder[](1); + orders[0] = order; + + vm.warp(50); + context.consideration.fulfillAvailableAdvancedOrders({ + advancedOrders: orders, + criteriaResolvers: criteriaResolvers, + offerFulfillments: offerComponentsArray, + considerationFulfillments: considerationComponentsArray, + fulfillerConduitKey: bytes32(0), + recipient: address(0), + maximumFulfilled: 1 + }); + assertTrue(statefulZone.called()); + } + + // function testMatchAdvancedOrders() external { + // test( + // this.execMatchAdvancedOrders, + // Context({ + // consideration: consideration, + // numOriginalAdditional: 0, + // numTips: 0 + // }) + // ); + // test( + // this.execMatchAdvancedOrders, + // Context({ + // consideration: referenceConsideration, + // numOriginalAdditional: 0, + // numTips: 0 + // }) + // ); + // } + + // function execMatchAdvancedOrders(Context memory context) external { + // addErc20OfferItem(1); + // addErc721ConsiderationItem(payable(address(offerer)), 42); + // addErc721ConsiderationItem(payable(address(offerer)), 43); + // addErc721ConsiderationItem(payable(address(offerer)), 44); + + // _configureOrderParameters({ + // offerer: address(this), + // zone: address(0), + // zoneHash: bytes32(0), + // salt: 0, + // useConduit: false + // }); + // baseOrderParameters.orderType = OrderType.CONTRACT; + + // configureOrderComponents(0); + + // AdvancedOrder memory order = AdvancedOrder({ + // parameters: baseOrderParameters, + // numerator: 1, + // denominator: 1, + // signature: "", + // extraData: "context" + // }); + + // AdvancedOrder memory mirror = createMirrorContractOffererOrder( + // context, + // "mirroroooor", + // order + // ); + + // CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + // AdvancedOrder[] memory orders = new AdvancedOrder[](2); + // orders[0] = order; + // orders[1] = mirror; + + // //match first order offer to second order consideration + // createFulfillmentFromComponentsAndAddToFulfillments({ + // _offer: FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }), + // _consideration: FulfillmentComponent({ + // orderIndex: 1, + // itemIndex: 0 + // }) + // }); + // // match second order first offer to first order first consideration + // createFulfillmentFromComponentsAndAddToFulfillments({ + // _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: 0 }), + // _consideration: FulfillmentComponent({ + // orderIndex: 0, + // itemIndex: 0 + // }) + // }); + // // match second order second offer to first order second consideration + // createFulfillmentFromComponentsAndAddToFulfillments({ + // _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: 1 }), + // _consideration: FulfillmentComponent({ + // orderIndex: 0, + // itemIndex: 1 + // }) + // }); + // // match second order third offer to first order third consideration + // createFulfillmentFromComponentsAndAddToFulfillments({ + // _offer: FulfillmentComponent({ orderIndex: 1, itemIndex: 2 }), + // _consideration: FulfillmentComponent({ + // orderIndex: 0, + // itemIndex: 2 + // }) + // }); + + // context.consideration.matchAdvancedOrders({ + // orders: orders, + // criteriaResolvers: criteriaResolvers, + // fulfillments: fulfillments + // }); + // assertTrue(zone.called()); + // } + + function createMirrorOrder( + Context memory context, + string memory _offerer, + AdvancedOrder memory advancedOrder + ) internal returns (AdvancedOrder memory) { + delete offerItems; + delete considerationItems; + + (address _offererAddr, uint256 pkey) = makeAddrAndKey(_offerer); + test721_1.mint(address(_offererAddr), 42); + + vm.prank(_offererAddr); + test721_1.setApprovalForAll(address(context.consideration), true); + + for (uint256 i; i < advancedOrder.parameters.offer.length; i++) { + OfferItem memory _offerItem = advancedOrder.parameters.offer[i]; + + addConsiderationItem({ + itemType: _offerItem.itemType, + token: _offerItem.token, + identifier: _offerItem.identifierOrCriteria, + startAmount: _offerItem.startAmount, + endAmount: _offerItem.endAmount, + recipient: payable(_offererAddr) + }); + } + // do the same for considerationItem -> offerItem + for ( + uint256 i; + i < advancedOrder.parameters.consideration.length; + i++ + ) { + ConsiderationItem memory _considerationItem = advancedOrder + .parameters + .consideration[i]; + + addOfferItem({ + itemType: _considerationItem.itemType, + token: _considerationItem.token, + identifier: _considerationItem.identifierOrCriteria, + startAmount: _considerationItem.startAmount, + endAmount: _considerationItem.endAmount + }); + } + + _configureOrderParameters({ + offerer: _offererAddr, + zone: advancedOrder.parameters.zone, + zoneHash: advancedOrder.parameters.zoneHash, + salt: advancedOrder.parameters.salt, + useConduit: false + }); + + configureOrderComponents(0); + bytes32 orderHash = context.consideration.getOrderHash( + baseOrderComponents + ); + bytes memory signature = signOrder( + context.consideration, + pkey, + orderHash + ); + + AdvancedOrder memory order = AdvancedOrder({ + parameters: baseOrderParameters, + numerator: advancedOrder.denominator, + denominator: advancedOrder.numerator, + signature: signature, + extraData: "" + }); + + return order; + } + + function _sumConsiderationAmounts() internal view returns (uint256 sum) { + for (uint256 i = 0; i < considerationItems.length; i++) { + sum += considerationItems[i].startAmount; + } + } +} diff --git a/test/foundry/zone/impl/BadZone.sol b/test/foundry/zone/impl/BadZone.sol new file mode 100644 index 000000000..da5a45ed4 --- /dev/null +++ b/test/foundry/zone/impl/BadZone.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { + ZoneParameters, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ZoneInterface +} from "../../../../contracts/interfaces/ZoneInterface.sol"; + +contract BadZone is ZoneInterface { + function validateOrder( + ZoneParameters calldata zoneParameters + ) external pure returns (bytes4 validOrderMagicValue) { + if (zoneParameters.consideration[0].identifier == 1) { + return ZoneInterface.validateOrder.selector; + } else { + assembly { + return(0, 0) + } + } + } + + /** + * @dev Returns the metadata for this zone. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 3003; + schemas[0].metadata = new bytes(0); + + return ("BadZone", schemas); + } +} diff --git a/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol b/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol new file mode 100644 index 000000000..e765f9d93 --- /dev/null +++ b/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { + ZoneParameters, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +import { ItemType } from "../../../../contracts/lib/ConsiderationEnums.sol"; + +import { + ZoneInterface +} from "../../../../contracts/interfaces/ZoneInterface.sol"; + +contract PostFulfillmentStatefulTestZone is ZoneInterface { + error IncorrectAmount(uint256 actual, uint256 expected); + error IncorrectItemType(ItemType actual, ItemType expected); + error IncorrectIdentifier(uint256 actual, uint256 expected); + + uint256 amountToCheck; + + constructor(uint256 amount) { + amountToCheck = amount; + } + + bool public called = false; + + /** + * @dev Validates the order with the given `zoneParameters`. Called by + * Consideration whenever any extraData is provided by the caller. + * + * @param zoneParameters The parameters for the order. + * + * @return validOrderMagicValue The validOrder magic value. + */ + function validateOrder( + ZoneParameters calldata zoneParameters + ) external returns (bytes4 validOrderMagicValue) { + // Check that the amount in the offer is correct. + if (zoneParameters.offer[0].amount != amountToCheck) { + revert IncorrectAmount(zoneParameters.offer[0].amount, 50); + } + + // Check that the item type in the consideration is correct. + if (zoneParameters.consideration[0].itemType != ItemType.ERC721) { + revert IncorrectIdentifier( + uint256(zoneParameters.consideration[0].itemType), + uint256(ItemType.ERC721) + ); + } + + // Check that the token ID in the consideration is correct. + if (zoneParameters.consideration[0].identifier != 42) { + revert IncorrectIdentifier( + zoneParameters.consideration[0].identifier, + 42 + ); + } + + // Set the global called flag to true. + called = true; + + // Return the validOrderMagicValue. + return ZoneInterface.validateOrder.selector; + } + + /** + * @dev Returns the metadata for this zone. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 3003; + schemas[0].metadata = new bytes(0); + + return ("PostFulfillmentStatefulTestZone", schemas); + } +} diff --git a/test/foundry/zone/impl/TestZone.sol b/test/foundry/zone/impl/TestZone.sol new file mode 100644 index 000000000..709607e70 --- /dev/null +++ b/test/foundry/zone/impl/TestZone.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { + ZoneParameters, + Schema +} from "../../../../contracts/lib/ConsiderationStructs.sol"; + +import { + ZoneInterface +} from "../../../../contracts/interfaces/ZoneInterface.sol"; + +contract TestZone is ZoneInterface { + // Called by Consideration whenever any extraData is provided by the caller. + function validateOrder( + ZoneParameters calldata + ) external pure returns (bytes4 validOrderMagicValue) { + return ZoneInterface.validateOrder.selector; + } + + /** + * @dev Returns the metadata for this zone. + */ + function getSeaportMetadata() + external + pure + override + returns ( + string memory name, + Schema[] memory schemas // map to Seaport Improvement Proposal IDs + ) + { + schemas = new Schema[](1); + schemas[0].id = 3003; + schemas[0].metadata = new bytes(0); + + return ("TestZone", schemas); + } +} diff --git a/test/getter.spec.ts b/test/getter.spec.ts new file mode 100644 index 000000000..d8019053a --- /dev/null +++ b/test/getter.spec.ts @@ -0,0 +1,74 @@ +import { expect } from "chai"; +import { ethers, network } from "hardhat"; + +import { randomHex } from "./utils/encoding"; +import { faucet } from "./utils/faucet"; +import { seaportFixture } from "./utils/fixtures"; +import { VERSION } from "./utils/helpers"; + +import type { + ConduitControllerInterface, + ConsiderationInterface, +} from "../typechain-types"; + +const { keccak256, toUtf8Bytes } = ethers.utils; + +describe(`Getter tests (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let conduitController: ConduitControllerInterface; + let directMarketplaceContract: ConsiderationInterface; + let marketplaceContract: ConsiderationInterface; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ conduitController, directMarketplaceContract, marketplaceContract } = + await seaportFixture(owner)); + }); + + it("gets correct name", async () => { + const name = await marketplaceContract.name(); + expect(name).to.equal(process.env.REFERENCE ? "Consideration" : "Seaport"); + + const directName = await directMarketplaceContract.name(); + expect(directName).to.equal("Consideration"); + }); + + it("gets correct version, domain separator and conduit controller", async () => { + const name = process.env.REFERENCE ? "Consideration" : "Seaport"; + const { + version, + domainSeparator, + conduitController: controller, + } = await marketplaceContract.information(); + + const typehash = keccak256( + toUtf8Bytes( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ) + ); + const namehash = keccak256(toUtf8Bytes(name)); + const versionhash = keccak256(toUtf8Bytes(version)); + const { chainId } = await provider.getNetwork(); + const chainIdEncoded = chainId.toString(16).padStart(64, "0"); + const addressEncoded = marketplaceContract.address + .slice(2) + .padStart(64, "0"); + expect(domainSeparator).to.equal( + keccak256( + `0x${typehash.slice(2)}${namehash.slice(2)}${versionhash.slice( + 2 + )}${chainIdEncoded}${addressEncoded}` + ) + ); + expect(controller).to.equal(conduitController.address); + }); +}); diff --git a/test/index.js b/test/index.js deleted file mode 100644 index fabf05103..000000000 --- a/test/index.js +++ /dev/null @@ -1,16334 +0,0 @@ -/* eslint-disable no-unused-expressions */ -const { expect } = require("chai"); -const { - constants, - utils: { parseEther, keccak256, toUtf8Bytes }, -} = require("ethers"); -const { ethers, network } = require("hardhat"); -const { - faucet, - whileImpersonating, - getWalletWithEther, -} = require("./utils/impersonate"); -const { merkleTree } = require("./utils/criteria"); -const { - randomHex, - random128, - toAddress, - toKey, - convertSignatureToEIP2098, - getBasicOrderParameters, - getItemETH, - toBN, - randomBN, - toFulfillment, - toFulfillmentComponents, - getBasicOrderExecutions, - buildResolver, - buildOrderStatus, - defaultBuyNowMirrorFulfillment, - defaultAcceptOfferMirrorFulfillment, -} = require("./utils/encoding"); -const { randomInt } = require("crypto"); -const { - fixtureERC20, - fixtureERC721, - fixtureERC1155, - seaportFixture, -} = require("./utils/fixtures"); -const { deployContract } = require("./utils/contracts"); - -const VERSION = !process.env.REFERENCE ? "1.1" : "rc.1.1"; - -const minRandom = (min) => randomBN(10).add(min); - -const getCustomRevertSelector = (customErrorString) => - ethers.utils - .keccak256(ethers.utils.toUtf8Bytes(customErrorString)) - .slice(0, 10); - -describe(`Consideration (version: ${VERSION}) — initial test suite`, function () { - const provider = ethers.provider; - let zone; - let marketplaceContract; - let testERC20; - let testERC721; - let testERC1155; - let testERC1155Two; - let owner; - let withBalanceChecks; - let EIP1271WalletFactory; - let reenterer; - let stubZone; - let conduitController; - let conduitImplementation; - let conduitOne; - let conduitKeyOne; - let directMarketplaceContract; - let mintAndApproveERC20; - let getTestItem20; - let set721ApprovalForAll; - let mint721; - let mint721s; - let mintAndApprove721; - let getTestItem721; - let getTestItem721WithCriteria; - let set1155ApprovalForAll; - let mint1155; - let mintAndApprove1155; - let getTestItem1155WithCriteria; - let getTestItem1155; - let deployNewConduit; - let createTransferWithApproval; - let createOrder; - let createMirrorBuyNowOrder; - let createMirrorAcceptOfferOrder; - let checkExpectedEvents; - - const simulateMatchOrders = async (orders, fulfillments, caller, value) => { - return marketplaceContract - .connect(caller) - .callStatic.matchOrders(orders, fulfillments, { - value, - }); - }; - - const simulateAdvancedMatchOrders = async ( - orders, - criteriaResolvers, - fulfillments, - caller, - value - ) => { - return marketplaceContract - .connect(caller) - .callStatic.matchAdvancedOrders(orders, criteriaResolvers, fulfillments, { - value, - }); - }; - - after(async () => { - await network.provider.request({ - method: "hardhat_reset", - }); - }); - - before(async () => { - owner = new ethers.Wallet(randomHex(32), provider); - - await Promise.all( - [owner].map((wallet) => faucet(wallet.address, provider)) - ); - - ({ - EIP1271WalletFactory, - reenterer, - conduitController, - conduitImplementation, - conduitKeyOne, - conduitOne, - deployNewConduit, - testERC20, - mintAndApproveERC20, - getTestItem20, - testERC721, - set721ApprovalForAll, - mint721, - mint721s, - mintAndApprove721, - getTestItem721, - getTestItem721WithCriteria, - testERC1155, - set1155ApprovalForAll, - mint1155, - mintAndApprove1155, - getTestItem1155WithCriteria, - getTestItem1155, - testERC1155Two, - createTransferWithApproval, - marketplaceContract, - directMarketplaceContract, - stubZone, - createOrder, - createMirrorBuyNowOrder, - createMirrorAcceptOfferOrder, - withBalanceChecks, - checkExpectedEvents, - } = await seaportFixture(owner)); - }); - - describe("Getter tests", async () => { - it("gets correct name", async () => { - const name = await marketplaceContract.name(); - expect(name).to.equal( - process.env.REFERENCE ? "Consideration" : "Seaport" - ); - - const directName = await directMarketplaceContract.name(); - expect(directName).to.equal("Consideration"); - }); - it("gets correct version, domain separator and conduit controller", async () => { - const name = process.env.REFERENCE ? "Consideration" : "Seaport"; - const { - version, - domainSeparator, - conduitController: controller, - } = await marketplaceContract.information(); - - const typehash = keccak256( - toUtf8Bytes( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ) - ); - const namehash = keccak256(toUtf8Bytes(name)); - const versionhash = keccak256(toUtf8Bytes(version)); - const { chainId } = await provider.getNetwork(); - const chainIdEncoded = chainId.toString(16).padStart(64, "0"); - const addressEncoded = marketplaceContract.address - .slice(2) - .padStart(64, "0"); - expect(domainSeparator).to.equal( - keccak256( - `0x${typehash.slice(2)}${namehash.slice(2)}${versionhash.slice( - 2 - )}${chainIdEncoded}${addressEncoded}` - ) - ); - expect(controller).to.equal(conduitController.address); - }); - }); - - // Buy now or accept offer for a single ERC721 or ERC1155 in exchange for - // ETH, WETH or ERC20 - describe("Basic buy now or accept offer flows", async () => { - let seller; - let sellerContract; - let buyerContract; - let buyer; - - beforeEach(async () => { - // Setup basic buyer/seller wallets with ETH - seller = new ethers.Wallet(randomHex(32), provider); - buyer = new ethers.Wallet(randomHex(32), provider); - zone = new ethers.Wallet(randomHex(32), provider); - - sellerContract = await EIP1271WalletFactory.deploy(seller.address); - buyerContract = await EIP1271WalletFactory.deploy(buyer.address); - - await Promise.all( - [seller, buyer, zone, sellerContract, buyerContract].map((wallet) => - faucet(wallet.address, provider) - ) - ); - }); - - describe("A single ERC721 is to be transferred", async () => { - describe("[Buy now] User fulfills a sell order for a single ERC721", async () => { - it("ERC721 <=> ETH (standard)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - return receipt; - }); - }); - it("ERC721 <=> ETH (standard via conduit)", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (standard with tip)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // Add a tip - order.parameters.consideration.push( - getItemETH(parseEther("1"), parseEther("1"), owner.address) - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value: value.add(parseEther("1")), - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (standard with restricted order)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 2 // FULL_RESTRICTED - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (standard with restricted order and extra data)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 2 // FULL_RESTRICTED - ); - - order.extraData = "0x1234"; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (standard with restricted order, specified recipient and extra data)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 2 // FULL_RESTRICTED - ); - - order.extraData = "0x1234"; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], toKey(false), owner.address, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - recipient: owner.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic, minimal and listed off-chain)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; - - const { order, orderHash, value } = await createOrder( - seller, - constants.AddressZero, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - constants.HashZero, - true // extraCheap - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic, minimal and verified on-chain)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; - - const { order, orderHash, value } = await createOrder( - seller, - constants.AddressZero, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - constants.HashZero, - true // extraCheap - ); - - // Validate the order from any account - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, constants.AddressZero); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (standard, minimal and listed off-chain)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; - - const { order, orderHash, value } = await createOrder( - seller, - constants.AddressZero, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - constants.HashZero, - true // extraCheap - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (standard, minimal and verified on-chain)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(toBN(1), toBN(1), constants.AddressZero), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - constants.AddressZero, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - constants.HashZero, - true // extraCheap - ); - - // Validate the order from any account - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, constants.AddressZero); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (advanced, minimal and listed off-chain)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; - - const { order, orderHash, value } = await createOrder( - seller, - constants.AddressZero, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - constants.HashZero, - true // extraCheap - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (advanced, minimal and verified on-chain)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [getItemETH(toBN(1), toBN(1), seller.address)]; - - const { order, orderHash, value } = await createOrder( - seller, - constants.AddressZero, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - constants.HashZero, - true // extraCheap - ); - - // Validate the order from any account - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, constants.AddressZero); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic with tips)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order, - false, - [ - { - amount: parseEther("2"), - recipient: `0x0000000000000000000000000000000000000001`, - }, - { - amount: parseEther("3"), - recipient: `0x0000000000000000000000000000000000000002`, - }, - { - amount: parseEther("4"), - recipient: `0x0000000000000000000000000000000000000003`, - }, - ] - ); - - order.parameters.consideration.push( - getItemETH( - parseEther("2"), - parseEther("2"), - "0x0000000000000000000000000000000000000001" - ) - ); - - order.parameters.consideration.push( - getItemETH( - parseEther("3"), - parseEther("3"), - "0x0000000000000000000000000000000000000002" - ) - ); - - order.parameters.consideration.push( - getItemETH( - parseEther("4"), - parseEther("4"), - "0x0000000000000000000000000000000000000003" - ) - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: value.add(parseEther("9")), - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic via conduit)", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic with restricted order)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 2 // FULL_RESTRICTED - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic with partial restricted order)", async () => { - // Seller mints nft - const nftId = randomBN(); - await testERC721.mint(seller.address, nftId); - - // Seller approves marketplace contract to transfer NFT - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC721 - .connect(seller) - .setApprovalForAll(marketplaceContract.address, true) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(seller.address, marketplaceContract.address, true); - }); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - { - itemType: 0, // ETH - token: constants.AddressZero, - identifierOrCriteria: 0, // ignored for ETH - startAmount: ethers.utils.parseEther("10"), - endAmount: ethers.utils.parseEther("10"), - recipient: seller.address, - }, - { - itemType: 0, // ETH - token: constants.AddressZero, - identifierOrCriteria: 0, // ignored for ETH - startAmount: ethers.utils.parseEther("1"), - endAmount: ethers.utils.parseEther("1"), - recipient: zone.address, - }, - { - itemType: 0, // ETH - token: constants.AddressZero, - identifierOrCriteria: 0, // ignored for ETH - startAmount: ethers.utils.parseEther("1"), - endAmount: ethers.utils.parseEther("1"), - recipient: owner.address, - }, - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 3 // PARTIAL_RESTRICTED - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await whileImpersonating(buyer.address, provider, async () => { - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { value }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { order, orderHash, fulfiller: buyer.address }, - ]); - - return receipt; - }); - }); - }); - it("ERC721 <=> ETH (basic, already validated)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // Validate the order from any account - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic, EIP-2098 signature)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // Convert signature to EIP 2098 - expect(order.signature.length).to.equal(132); - order.signature = convertSignatureToEIP2098(order.signature); - expect(order.signature.length).to.equal(130); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (basic, extra ether supplied and returned to caller)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: value.add(1), - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ETH (match)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC721 <=> ETH (match via conduit)", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC721 <=> ETH (match, extra eth supplied and returned to caller)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value: value.add(101), - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC721 <=> ERC20 (standard)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - return receipt; - }); - }); - it("ERC721 <=> ERC20 (standard via conduit)", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (basic)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (basic via conduit)", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (basic, EIP-1271 signature)", async () => { - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs( - sellerContract.address, - marketplaceContract.address, - true - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (EIP-1271 signature on non-ECDSA 64 bytes)", async () => { - const sellerContract = await deployContract( - "EIP1271Wallet", - seller, - seller.address - ); - - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs( - sellerContract.address, - marketplaceContract.address, - true - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller - ); - - // Compute the digest based on the order hash - const { domainSeparator } = await marketplaceContract.information(); - const digest = keccak256( - `0x1901${domainSeparator.slice(2)}${orderHash.slice(2)}` - ); - - const signature = `0x`.padEnd(130, "f"); - - const basicOrderParameters = { - ...getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ), - signature, - }; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (EIP-1271 signature on non-ECDSA 65 bytes)", async () => { - const sellerContract = await deployContract( - "EIP1271Wallet", - seller, - seller.address - ); - - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs( - sellerContract.address, - marketplaceContract.address, - true - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller - ); - - // Compute the digest based on the order hash - const { domainSeparator } = await marketplaceContract.information(); - const digest = keccak256( - `0x1901${domainSeparator.slice(2)}${orderHash.slice(2)}` - ); - - await sellerContract.registerDigest(digest, true); - - const signature = `0x`.padEnd(132, "f"); - - const basicOrderParameters = { - ...getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ), - signature, - }; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - - await sellerContract.registerDigest(digest, false); - }); - it("ERC721 <=> ERC20 (basic, EIP-1271 signature w/ non-standard length)", async () => { - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs( - sellerContract.address, - marketplaceContract.address, - true - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller - ); - - const basicOrderParameters = { - ...getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ), - signature: "0x", - }; - - // Fails before seller contract approves the digest (note that any - // non-standard signature length is treated as a contract signature) - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.revertedWith("BadContractSignature"); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.reverted; - } - - // Compute the digest based on the order hash - const { domainSeparator } = await marketplaceContract.information(); - const digest = keccak256( - `0x1901${domainSeparator.slice(2)}${orderHash.slice(2)}` - ); - - // Seller approves the digest - await sellerContract.connect(seller).registerDigest(digest, true); - - // Now it succeeds - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (match)", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC721 <=> ERC20 (match via conduit)", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - describe("[Accept offer] User accepts a buy offer on a single ERC721", async () => { - // Note: ETH is not a possible case - it("ERC721 <=> ERC20 (standard)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves marketplace contract to transfer NFT - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Buyer approves marketplace contract to transfer ERC20 tokens too - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (standard, via conduit)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves marketplace contract to transfer NFT - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20(seller, conduitOne.address, tokenAmount); - - // Buyer approves marketplace contract to transfer ERC20 tokens - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (standard, fulfilled via conduit)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves conduit contract to transfer NFT - await set721ApprovalForAll(buyer, conduitOne.address, true); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Buyer approves conduit to transfer ERC20 tokens - await expect( - testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, conduitOne.address, tokenAmount); - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, conduitKeyOne); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: conduitKeyOne, - }, - ]); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (basic)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves marketplace contract to transfer NFT - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - // Seller mints ERC20 - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 4, // ERC721ForERC20 - order - ); - - await withBalanceChecks([order], toBN(0), null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ], - getBasicOrderExecutions(order, buyer.address) - ); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (basic, many via conduit)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves marketplace contract to transfer NFT - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - // Seller mints ERC20 - const tokenAmount = toBN(random128()); - await mintAndApproveERC20(seller, conduitOne.address, tokenAmount); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(1, 1, zone.address), - ]; - - for (let i = 1; i <= 50; ++i) { - consideration.push( - getTestItem20(i, i, toAddress(parseInt(i) + 10000)) - ); - } - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const basicOrderParameters = getBasicOrderParameters( - 4, // ERC721ForERC20 - order - ); - - await withBalanceChecks([order], toBN(0), null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ], - getBasicOrderExecutions(order, buyer.address) - ); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (basic, fulfilled via conduit)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves conduit contract to transfer NFT - await set721ApprovalForAll(buyer, conduitOne.address, true); - - // Seller mints ERC20 - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 4, // ERC721ForERC20 - order, - conduitKeyOne - ); - - await withBalanceChecks([order], toBN(0), null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: conduitKeyOne, - }, - ], - getBasicOrderExecutions(order, buyer.address, conduitKeyOne) - ); - - return receipt; - }); - }); - it("ERC721 <=> ERC20 (match)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves marketplace contract to transfer NFT - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder(buyer, zone, order); - - const fulfillments = defaultAcceptOfferMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC721 <=> ERC20 (match via conduit)", async () => { - // Buyer mints nft - const nftId = await mint721(buyer); - - // Buyer approves conduit contract to transfer NFT - await set721ApprovalForAll(buyer, conduitOne.address, true); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem721(nftId, 1, 1, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - [], - conduitKeyOne - ); - - const fulfillments = defaultAcceptOfferMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - }); - - describe("A single ERC1155 is to be transferred", async () => { - describe("[Buy now] User fulfills a sell order for a single ERC1155", async () => { - it("ERC1155 <=> ETH (standard)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ETH (standard via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - conduitOne.address - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ETH (basic)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 1, // EthForERC1155 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ETH (basic via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - conduitOne.address - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const basicOrderParameters = getBasicOrderParameters( - 1, // EthForERC1155 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ETH (match)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC1155 <=> ETH (match via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - conduitOne.address - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC1155 <=> ERC20 (standard)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (standard via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - conduitOne.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (basic)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 3, // ERC20ForERC1155 - order - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ], - getBasicOrderExecutions(order, buyer.address) - ); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (basic via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - conduitOne.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const basicOrderParameters = getBasicOrderParameters(3, order); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (match)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC1155 <=> ERC20 (match via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - conduitOne.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const offer = [getTestItem1155(nftId, amount, amount)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - describe("[Accept offer] User accepts a buy offer on a single ERC1155", async () => { - // Note: ETH is not a possible case - it("ERC1155 <=> ERC20 (standard)", async () => { - // Buyer mints nft - const { nftId, amount } = await mintAndApprove1155( - buyer, - marketplaceContract.address - ); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Buyer approves marketplace contract to transfer ERC20 tokens too - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem1155(nftId, amount, amount, undefined, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false)); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (standard, fulfilled via conduit)", async () => { - // Buyer mints nft - const { nftId, amount } = await mintAndApprove1155( - buyer, - conduitOne.address - ); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Buyer approves conduit to transfer ERC20 tokens - await expect( - testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, conduitOne.address, tokenAmount); - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem1155(nftId, amount, amount, undefined, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, conduitKeyOne); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: conduitKeyOne, - }, - ]); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (basic)", async () => { - // Buyer mints nft - const { nftId, amount } = await mintAndApprove1155( - buyer, - marketplaceContract.address - ); - - // Seller mints ERC20 - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem1155(nftId, amount, amount, undefined, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 5, // ERC1155ForERC20 - order - ); - - await withBalanceChecks([order], toBN(0), null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ], - getBasicOrderExecutions(order, buyer.address) - ); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (basic, fulfilled via conduit)", async () => { - // Buyer mints nft - const { nftId, amount } = await mintAndApprove1155( - buyer, - conduitOne.address - ); - - // Seller mints ERC20 - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem1155(nftId, amount, amount, undefined, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 5, // ERC1155ForERC20 - order, - conduitKeyOne - ); - - await withBalanceChecks([order], toBN(0), null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters); - - const executions = getBasicOrderExecutions( - order, - buyer.address, - conduitKeyOne - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: conduitKeyOne, - }, - ], - executions - ); - - return receipt; - }); - }); - it("ERC1155 <=> ERC20 (match)", async () => { - // Buyer mints nft - const { nftId, amount } = await mintAndApprove1155( - buyer, - marketplaceContract.address - ); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem1155(nftId, amount, amount, undefined, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder(buyer, zone, order); - - const fulfillments = defaultAcceptOfferMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("ERC1155 <=> ERC20 (match via conduit)", async () => { - // Buyer mints nft - const { nftId, amount } = await mintAndApprove1155( - buyer, - conduitOne.address - ); - - // Seller mints ERC20 - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // NOTE: Buyer does not need to approve marketplace for ERC20 tokens - - const offer = [ - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - getTestItem1155(nftId, amount, amount, undefined, seller.address), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - [], - conduitKeyOne - ); - - const fulfillments = defaultAcceptOfferMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - }); - }); - - describe("Validate, cancel, and increment counter flows", async () => { - let seller; - let buyer; - - beforeEach(async () => { - // Setup basic buyer/seller wallets with ETH - seller = new ethers.Wallet(randomHex(32), provider); - buyer = new ethers.Wallet(randomHex(32), provider); - zone = new ethers.Wallet(randomHex(32), provider); - await Promise.all( - [seller, buyer, zone].map((wallet) => faucet(wallet.address, provider)) - ); - }); - - describe("Validate", async () => { - it("Validate signed order and fill it with no signature", async () => { - // Seller mints an nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const signature = order.signature; - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.eq( - buildOrderStatus(false, false, 0, 0) - ); - - // cannot fill it with no signature yet - order.signature = "0x"; - - if (!process.env.REFERENCE) { - const expectedRevertReason = - getCustomRevertSelector("InvalidSignature()"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - - // cannot validate it with no signature from a random account - await expect(marketplaceContract.connect(owner).validate([order])).to - .be.reverted; - - tx = await marketplaceContract - .connect(owner) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - - // cannot validate it with no signature from a random account - await expect(marketplaceContract.connect(owner).validate([order])).to - .be.reverted; - } - - // can validate it once you add the signature back - order.signature = signature; - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.eq( - buildOrderStatus(true, false, 0, 0) - ); - - // Can validate it repeatedly, but no event after the first time - await marketplaceContract.connect(owner).validate([order, order]); - - // Fulfill the order without a signature - order.signature = "0x"; - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - - const finalStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...finalStatus }).to.deep.eq( - buildOrderStatus(true, false, 1, 1) - ); - - // cannot validate it once it's been fully filled - await expect( - marketplaceContract.connect(owner).validate([order]) - ).to.be.revertedWith("OrderAlreadyFilled", orderHash); - }); - it("Validate unsigned order from offerer and fill it with no signature", async () => { - // Seller mints an nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - order.signature = "0x"; - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.eq( - buildOrderStatus(false, false, 0, 0) - ); - - if (!process.env.REFERENCE) { - // cannot fill it with no signature yet - const expectedRevertReason = - getCustomRevertSelector("InvalidSignature()"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - - // cannot validate it with no signature from a random account - tx = await marketplaceContract - .connect(owner) - .populateTransaction.validate([order]); - returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect(marketplaceContract.connect(owner).validate([order])).to - .be.reverted; - } else { - // cannot fill it with no signature yet - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - - // cannot validate it with no signature from a random account - await expect(marketplaceContract.connect(owner).validate([order])).to - .be.reverted; - } - - // can validate it from the seller - await expect(marketplaceContract.connect(seller).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.eq( - buildOrderStatus(true, false, 0, 0) - ); - - // Fulfill the order without a signature - order.signature = "0x"; - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - - const finalStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...finalStatus }).to.deep.eq( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Cannot validate a cancelled order", async () => { - // Seller mints an nft - const nftId = randomBN(); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const signature = order.signature; - - order.signature = "0x"; - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.eq( - buildOrderStatus(false, false, 0, 0) - ); - - if (!process.env.REFERENCE) { - // cannot fill it with no signature yet - const expectedRevertReason = - getCustomRevertSelector("InvalidSignature()"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - - tx = await marketplaceContract - .connect(owner) - .populateTransaction.validate([order]); - returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - // cannot validate it with no signature from a random account - await expect(marketplaceContract.connect(owner).validate([order])).to - .be.reverted; - } else { - // cannot fill it with no signature yet - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - - // cannot validate it with no signature from a random account - await expect(marketplaceContract.connect(owner).validate([order])).to - .be.reverted; - } - - // can cancel it - await expect( - marketplaceContract.connect(seller).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHash, seller.address, zone.address); - - // cannot validate it from the seller - await expect( - marketplaceContract.connect(seller).validate([order]) - ).to.be.revertedWith(`OrderIsCancelled("${orderHash}")`); - - // cannot validate it with a signature either - order.signature = signature; - await expect( - marketplaceContract.connect(owner).validate([order]) - ).to.be.revertedWith(`OrderIsCancelled("${orderHash}")`); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.eq( - buildOrderStatus(false, true, 0, 0) - ); - }); - }); - - describe("Cancel", async () => { - it("Can cancel an order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // cannot cancel it from a random account - await expect( - marketplaceContract.connect(owner).cancel([orderComponents]) - ).to.be.revertedWith("InvalidCanceller"); - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.eq( - buildOrderStatus(false, false, 0, 0) - ); - - // can cancel it - await expect( - marketplaceContract.connect(seller).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHash, seller.address, zone.address); - - // cannot fill the order anymore - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith(`OrderIsCancelled("${orderHash}")`); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.eq( - buildOrderStatus(false, true, 0, 0) - ); - }); - it("Can cancel a validated order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // cannot cancel it from a random account - await expect( - marketplaceContract.connect(owner).cancel([orderComponents]) - ).to.be.revertedWith("InvalidCanceller"); - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // Can validate it - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.equal( - buildOrderStatus(true, false, 0, 0) - ); - - // can cancel it - await expect( - marketplaceContract.connect(seller).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHash, seller.address, zone.address); - - // cannot fill the order anymore - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith(`OrderIsCancelled("${orderHash}")`); - - const finalStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...finalStatus }).to.deep.equal( - buildOrderStatus(false, true, 0, 0) - ); - }); - it("Can cancel an order from the zone", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // cannot cancel it from a random account - await expect( - marketplaceContract.connect(owner).cancel([orderComponents]) - ).to.be.revertedWith("InvalidCanceller"); - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // can cancel it from the zone - await expect( - marketplaceContract.connect(zone).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHash, seller.address, zone.address); - - // cannot fill the order anymore - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith(`OrderIsCancelled("${orderHash}")`); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.equal( - buildOrderStatus(false, true, 0, 0) - ); - }); - it("Can cancel a validated order from a zone", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const initialStatus = await marketplaceContract.getOrderStatus( - orderHash - ); - expect({ ...initialStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // Can validate it - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - // cannot cancel it from a random account - await expect( - marketplaceContract.connect(owner).cancel([orderComponents]) - ).to.be.revertedWith("InvalidCanceller"); - - const newStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...newStatus }).to.deep.equal( - buildOrderStatus(true, false, 0, 0) - ); - - // can cancel it from the zone - await expect( - marketplaceContract.connect(zone).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHash, seller.address, zone.address); - - // cannot fill the order anymore - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith(`OrderIsCancelled("${orderHash}")`); - - const finalStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...finalStatus }).to.deep.equal( - buildOrderStatus(false, true, 0, 0) - ); - }); - }); - - describe("Increment Counter", async () => { - it("Can increment the counter", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - let { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const counter = await marketplaceContract.getCounter(seller.address); - expect(counter).to.equal(0); - expect(orderComponents.counter).to.equal(counter); - - // can increment the counter - await expect(marketplaceContract.connect(seller).incrementCounter()) - .to.emit(marketplaceContract, "CounterIncremented") - .withArgs(1, seller.address); - - const newCounter = await marketplaceContract.getCounter(seller.address); - expect(newCounter).to.equal(1); - - if (!process.env.REFERENCE) { - // Cannot fill order anymore - const expectedRevertReason = - getCustomRevertSelector("InvalidSigner()"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } else { - // Cannot fill order anymore - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } - - const newOrderDetails = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - order = newOrderDetails.order; - orderHash = newOrderDetails.orderHash; - value = newOrderDetails.value; - orderComponents = newOrderDetails.orderComponents; - - expect(orderComponents.counter).to.equal(newCounter); - - // Can fill order with new counter - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("Can increment the counter and implicitly cancel a validated order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - let { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const counter = await marketplaceContract.getCounter(seller.address); - expect(counter).to.equal(0); - expect(orderComponents.counter).to.equal(counter); - - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - // can increment the counter - await expect(marketplaceContract.connect(seller).incrementCounter()) - .to.emit(marketplaceContract, "CounterIncremented") - .withArgs(1, seller.address); - - const newCounter = await marketplaceContract.getCounter(seller.address); - expect(newCounter).to.equal(1); - - if (!process.env.REFERENCE) { - // Cannot fill order anymore - const expectedRevertReason = - getCustomRevertSelector("InvalidSigner()"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } else { - // Cannot fill order anymore - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } - - const newOrderDetails = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - order = newOrderDetails.order; - orderHash = newOrderDetails.orderHash; - value = newOrderDetails.value; - orderComponents = newOrderDetails.orderComponents; - - expect(orderComponents.counter).to.equal(newCounter); - - // Can fill order with new counter - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - it("Can increment the counter as the zone and implicitly cancel a validated order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - let { order, orderHash, value, orderComponents } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const counter = await marketplaceContract.getCounter(seller.address); - expect(counter).to.equal(0); - expect(orderComponents.counter).to.equal(counter); - - await expect(marketplaceContract.connect(owner).validate([order])) - .to.emit(marketplaceContract, "OrderValidated") - .withArgs(orderHash, seller.address, zone.address); - - // can increment the counter as the offerer - await expect(marketplaceContract.connect(seller).incrementCounter()) - .to.emit(marketplaceContract, "CounterIncremented") - .withArgs(1, seller.address); - - const newCounter = await marketplaceContract.getCounter(seller.address); - expect(newCounter).to.equal(1); - - if (!process.env.REFERENCE) { - // Cannot fill order anymore - const expectedRevertReason = - getCustomRevertSelector("InvalidSigner()"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillOrder(order, toKey(false), { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } else { - // Cannot fill order anymore - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } - - const newOrderDetails = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - order = newOrderDetails.order; - orderHash = newOrderDetails.orderHash; - value = newOrderDetails.value; - orderComponents = newOrderDetails.orderComponents; - - expect(orderComponents.counter).to.equal(newCounter); - - // Can fill order with new counter - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ]); - - return receipt; - }); - }); - }); - }); - - describe("Advanced orders", async () => { - let seller; - let buyer; - - beforeEach(async () => { - // Setup basic buyer/seller wallets with ETH - seller = new ethers.Wallet(randomHex(32), provider); - buyer = new ethers.Wallet(randomHex(32), provider); - zone = new ethers.Wallet(randomHex(32), provider); - await Promise.all( - [seller, buyer, zone].map((wallet) => faucet(wallet.address, provider)) - ); - }); - - describe("Partial fills", async () => { - it("Partial fills (standard)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 2; // fill two tenths or one fifth - order.denominator = 10; // fill two tenths or one fifth - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 2, 10) - ); - - order.numerator = 1; // fill one half - order.denominator = 2; // fill one half - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 14, 20) - ); - - // Fill remaining; only 3/10ths will be fillable - order.numerator = 1; // fill one half - order.denominator = 2; // fill one half - - const ordersClone = JSON.parse(JSON.stringify([order])); - for (const [, clonedOrder] of Object.entries(ordersClone)) { - clonedOrder.parameters.startTime = order.parameters.startTime; - clonedOrder.parameters.endTime = order.parameters.endTime; - - for (const [j, offerItem] of Object.entries( - clonedOrder.parameters.offer - )) { - offerItem.startAmount = order.parameters.offer[j].startAmount; - offerItem.endAmount = order.parameters.offer[j].endAmount; - } - - for (const [j, considerationItem] of Object.entries( - clonedOrder.parameters.consideration - )) { - considerationItem.startAmount = - order.parameters.consideration[j].startAmount; - considerationItem.endAmount = - order.parameters.consideration[j].endAmount; - } - } - - ordersClone[0].numerator = 3; - ordersClone[0].denominator = 10; - - await withBalanceChecks(ordersClone, 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: ordersClone[0], - orderHash, - fulfiller: buyer.address, - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 40, 40) - ); - }); - it("Partial fills (standard, additional permutations)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 2; // fill two tenths or one fifth - order.denominator = 10; // fill two tenths or one fifth - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 2, 10) - ); - - order.numerator = 1; // fill one tenth - order.denominator = 10; // fill one tenth - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 3, 10) - ); - - // Fill all available; only 7/10ths will be fillable - order.numerator = 1; // fill all available - order.denominator = 1; // fill all available - - const ordersClone = JSON.parse(JSON.stringify([order])); - for (const [, clonedOrder] of Object.entries(ordersClone)) { - clonedOrder.parameters.startTime = order.parameters.startTime; - clonedOrder.parameters.endTime = order.parameters.endTime; - - for (const [j, offerItem] of Object.entries( - clonedOrder.parameters.offer - )) { - offerItem.startAmount = order.parameters.offer[j].startAmount; - offerItem.endAmount = order.parameters.offer[j].endAmount; - } - - for (const [j, considerationItem] of Object.entries( - clonedOrder.parameters.consideration - )) { - considerationItem.startAmount = - order.parameters.consideration[j].startAmount; - considerationItem.endAmount = - order.parameters.consideration[j].endAmount; - } - } - - ordersClone[0].numerator = 7; - ordersClone[0].denominator = 10; - - await withBalanceChecks(ordersClone, 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: ordersClone[0], - orderHash, - fulfiller: buyer.address, - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 10, 10) - ); - }); - it("Partial fills (match)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 2; // fill two tenths or one fifth - order.denominator = 10; // fill two tenths or one fifth - - let mirrorObject; - mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - let executions = await simulateAdvancedMatchOrders( - [order, mirrorObject.mirrorOrder], - [], // no criteria resolvers - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract.connect(owner).matchAdvancedOrders( - [order, mirrorObject.mirrorOrder], - [], // no criteria resolvers - fulfillments, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: mirrorObject.mirrorOrder, - orderHash: mirrorObject.mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 2, 10) - ); - - order.numerator = 1; // fill one tenth - order.denominator = 10; // fill one tenth - - mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); - - executions = await simulateAdvancedMatchOrders( - [order, mirrorObject.mirrorOrder], - [], // no criteria resolvers - fulfillments, - owner, - value - ); - - const tx2 = marketplaceContract.connect(owner).matchAdvancedOrders( - [order, mirrorObject.mirrorOrder], - [], // no criteria resolvers - fulfillments, - { - value, - } - ); - const receipt2 = await (await tx2).wait(); - await checkExpectedEvents( - tx2, - receipt2, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorObject.mirrorOrder, - orderHash: mirrorObject.mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 3, 10) - ); - - // Fill all available; only 7/10ths will be fillable - order.numerator = 7; // fill all available - order.denominator = 10; // fill all available - - mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); - - executions = await simulateAdvancedMatchOrders( - [order, mirrorObject.mirrorOrder], - [], // no criteria resolvers - fulfillments, - owner, - value - ); - - const tx3 = await marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorObject.mirrorOrder], - [], // no criteria resolvers - fulfillments, - { - value, - } - ); - const receipt3 = await tx3.wait(); - await checkExpectedEvents( - tx3, - receipt3, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorObject.mirrorOrder, - orderHash: mirrorObject.mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 10, 10) - ); - }); - - it("Simplifies fraction when numerator/denominator would overflow", async () => { - const numer1 = toBN(2).pow(100); - const denom1 = toBN(2).pow(101); - const numer2 = toBN(2).pow(20); - const denom2 = toBN(2).pow(22); - const amt = 8; - await mintAndApproveERC20(buyer, marketplaceContract.address, amt); - // Seller mints nft - const { nftId } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000, - undefined, - amt - ); - - const offer = [getTestItem1155(nftId, amt, amt)]; - - const consideration = [getTestItem20(amt, amt, seller.address)]; - const { order, orderHash, value } = await createOrder( - seller, - undefined, - offer, - consideration, - 1, // PARTIAL_OPEN - undefined, - undefined, - undefined, - undefined, - undefined, - true - ); - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // 1/2 - order.numerator = numer1; - order.denominator = denom1; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], toKey(false), buyer.address, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, numer1, denom1) - ); - - order.numerator = numer2; - order.denominator = denom2; - - await marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], toKey(false), buyer.address, { - value, - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, toBN(3), toBN(4)) - ); - }); - - it("Reverts when numerator/denominator overflow", async () => { - const prime1 = toBN(2).pow(7).sub(1); - const prime2 = toBN(2).pow(61).sub(1); - const prime3 = toBN(2).pow(107).sub(1); - const amt = prime1.mul(prime2).mul(prime3); - await mintAndApproveERC20(buyer, marketplaceContract.address, amt); - // Seller mints nft - const { nftId } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000, - undefined, - amt - ); - - const offer = [getTestItem1155(nftId, amt, amt)]; - - const consideration = [getTestItem20(amt, amt, seller.address)]; - const { order, orderHash, value } = await createOrder( - seller, - undefined, - offer, - consideration, - 1, // PARTIAL_OPEN - undefined, - undefined, - undefined, - undefined, - undefined, - true - ); - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // 1/2 - order.numerator = 1; - order.denominator = prime2; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], toKey(false), buyer.address, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, toBN(1), prime2) - ); - - order.numerator = prime1; - order.denominator = prime3; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], toKey(false), buyer.address, { - value, - }) - ).to.be.revertedWith( - "0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)" - ); - }); - }); - - describe("Criteria-based orders", async () => { - it("Criteria-based offer item ERC721 (standard)", async () => { - // Seller mints nfts - const [nftId, secondNFTId, thirdNFTId] = await mint721s(seller, 3); - - const tokenIds = [nftId, secondNFTId, thirdNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await withBalanceChecks([order], 0, criteriaResolvers, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - }); - }); - it("Criteria-based offer item ERC1155 (standard)", async () => { - // Seller mints nfts - const { nftId, amount } = await mint1155(seller); - - // Seller approves marketplace contract to transfer NFTs - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree([nftId]); - - const offer = [getTestItem1155WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await withBalanceChecks([order], 0, criteriaResolvers, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - }); - }); - it("Criteria-based offer item (standard, collection-level)", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - await testERC721.mint(seller.address, thirdNFTId); - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - getTestItem721WithCriteria(constants.HashZero, toBN(1), toBN(1)), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [buildResolver(0, 0, 0, nftId, [])]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await withBalanceChecks([order], 0, criteriaResolvers, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - }); - }); - it("Criteria-based offer item ERC721 (match)", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - await testERC721.mint(seller.address, thirdNFTId); - - const tokenIds = [nftId, secondNFTId, thirdNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = [ - [[[1, 0]], [[0, 0]]], - [[[0, 0]], [[1, 0]]], - [[[1, 1]], [[0, 1]]], - [[[1, 2]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - ], - executions, - criteriaResolvers - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("Criteria-based offer item ERC1155 (match)", async () => { - // Seller mints nfts - const { nftId, amount } = await mint1155(seller); - - // Seller approves marketplace contract to transfer NFTs - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree([nftId]); - - const offer = [getTestItem1155WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = [ - [[[1, 0]], [[0, 0]]], - [[[0, 0]], [[1, 0]]], - [[[1, 1]], [[0, 1]]], - [[[1, 2]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - ], - executions, - criteriaResolvers - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("Criteria-based offer item (match, collection-level)", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - await testERC721.mint(seller.address, thirdNFTId); - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - getTestItem721WithCriteria(constants.HashZero, toBN(1), toBN(1)), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [buildResolver(0, 0, 0, nftId, [])]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = [ - [[[1, 0]], [[0, 0]]], - [[[0, 0]], [[1, 0]]], - [[[1, 1]], [[0, 1]]], - [[[1, 2]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - ], - executions, - criteriaResolvers - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("Criteria-based consideration item (standard)", async () => { - // buyer mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(buyer.address, nftId); - await testERC721.mint(buyer.address, secondNFTId); - await testERC721.mint(buyer.address, thirdNFTId); - - const tokenIds = [nftId, secondNFTId, thirdNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem721WithCriteria(root, toBN(1), toBN(1), seller.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await withBalanceChecks( - [order], - value.mul(-1), - criteriaResolvers, - async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - } - ); - }); - it("Criteria-based consideration item ERC1155 (standard)", async () => { - // buyer mints nfts - const { nftId, amount } = await mint1155(buyer); - - // Seller approves marketplace contract to transfer NFTs - await set1155ApprovalForAll(buyer, marketplaceContract.address, true); - - const { root, proofs } = merkleTree([nftId]); - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem1155WithCriteria(root, toBN(1), toBN(1), seller.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await withBalanceChecks( - [order], - value.mul(-1), - criteriaResolvers, - async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - } - ); - }); - it("Criteria-based wildcard consideration item (standard)", async () => { - // buyer mints nft - const nftId = await mintAndApprove721( - buyer, - marketplaceContract.address - ); - const tokenAmount = minRandom(100); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem721WithCriteria( - constants.HashZero, - toBN(1), - toBN(1), - seller.address - ), - ]; - - const criteriaResolvers = [buildResolver(0, 1, 0, nftId, [])]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await withBalanceChecks( - [order], - value.mul(-1), - criteriaResolvers, - async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - } - ); - }); - it("Criteria-based consideration item ERC721 (match)", async () => { - // Fulfiller mints nft - const nftId = await mint721(buyer); - const tokenAmount = minRandom(100); - - // Fulfiller approves marketplace contract to transfer NFT - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - // Offerer mints ERC20 - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Fulfiller mints ERC20 - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const { root, proofs } = merkleTree([nftId]); - - const offer = [ - // Offerer (Seller) - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - // Fulfiller (Buyer) - { - itemType: 4, // ERC721WithCriteria - token: testERC721.address, - identifierOrCriteria: root, - startAmount: toBN(1), - endAmount: toBN(1), - recipient: seller.address, - }, - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = defaultAcceptOfferMirrorFulfillment; - - const executions = await simulateAdvancedMatchOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - ], - executions, - criteriaResolvers - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("Criteria-based consideration item ERC1155 (match)", async () => { - // Fulfiller mints nft - const { nftId, amount } = await mint1155(buyer); - const tokenAmount = minRandom(100); - - // Fulfiller approves marketplace contract to transfer NFT - await set1155ApprovalForAll(buyer, marketplaceContract.address, true); - - // Offerer mints ERC20 - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Fulfiller mints ERC20 - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const { root, proofs } = merkleTree([nftId]); - - const offer = [ - // Offerer (Seller) - getTestItem20(tokenAmount.sub(100), tokenAmount.sub(100)), - ]; - - const consideration = [ - // Fulfiller (Buyer) - { - itemType: 5, // ERC1155_WITH_CRITERIA - token: testERC1155.address, - identifierOrCriteria: root, - startAmount: toBN(1), - endAmount: toBN(1), - recipient: seller.address, - }, - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 1, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder, mirrorOrderHash } = - await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = defaultAcceptOfferMirrorFulfillment; - - const executions = await simulateAdvancedMatchOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - ], - executions, - criteriaResolvers - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - - describe("Ascending / Descending amounts", async () => { - it("Ascending offer amount (standard)", async () => { - // Seller mints nft - const nftId = randomBN(); - const startAmount = toBN(randomBN(2)); - const endAmount = startAmount.mul(2); - await testERC1155.mint(seller.address, nftId, endAmount.mul(10)); - - // Seller approves marketplace contract to transfer NFTs - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, startAmount, endAmount, undefined), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Ascending consideration amount (standard)", async () => { - // Seller mints ERC20 - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - seller, - marketplaceContract.address, - tokenAmount - ); - - // Buyer mints nft - const nftId = randomBN(); - const startAmount = toBN(randomBN(2)); - const endAmount = startAmount.mul(2); - await testERC1155.mint(buyer.address, nftId, endAmount.mul(10)); - - // Buyer approves marketplace contract to transfer NFTs - await set1155ApprovalForAll(buyer, marketplaceContract.address, true); - - // Buyer needs to approve marketplace to transfer ERC20 tokens too (as it's a standard fulfillment) - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem1155( - nftId, - startAmount, - endAmount, - undefined, - seller.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Ascending offer amount (match)", async () => { - // Seller mints nft - const nftId = randomBN(); - const startAmount = toBN(randomBN(2)); - const endAmount = startAmount.mul(2); - await testERC1155.mint(seller.address, nftId, endAmount.mul(10)); - - // Seller approves marketplace contract to transfer NFTs - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, startAmount, endAmount, undefined), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - }); - - describe("Sequenced Orders", async () => { - it("Match A => B => C => A", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - const secondNFTId = await mintAndApprove721( - buyer, - marketplaceContract.address - ); - const thirdNFTId = await mintAndApprove721( - owner, - marketplaceContract.address - ); - - const offerOne = [ - getTestItem721( - nftId, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const considerationOne = [ - getTestItem721( - secondNFTId, - toBN(1), - toBN(1), - seller.address, - testERC721.address - ), - ]; - - const { order: orderOne, orderHash: orderHashOne } = await createOrder( - seller, - zone, - offerOne, - considerationOne, - 0 // FULL_OPEN - ); - - const offerTwo = [ - getTestItem721( - secondNFTId, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const considerationTwo = [ - getTestItem721( - thirdNFTId, - toBN(1), - toBN(1), - buyer.address, - testERC721.address - ), - ]; - - const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( - buyer, - zone, - offerTwo, - considerationTwo, - 0 // FULL_OPEN - ); - - const offerThree = [ - getTestItem721( - thirdNFTId, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const considerationThree = [ - getTestItem721( - nftId, - toBN(1), - toBN(1), - owner.address, - testERC721.address - ), - ]; - - const { order: orderThree, orderHash: orderHashThree } = - await createOrder( - owner, - zone, - offerThree, - considerationThree, - 0 // FULL_OPEN - ); - - const fulfillments = [ - [[[1, 0]], [[0, 0]]], - [[[0, 0]], [[2, 0]]], - [[[2, 0]], [[1, 0]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [orderOne, orderTwo, orderThree], - [], // no criteria resolvers - fulfillments, - owner, - 0 // no value - ); - - expect(executions.length).to.equal(fulfillments.length); - - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [orderOne, orderTwo, orderThree], - [], - fulfillments, - { - value: 0, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderTwo, - orderHash: orderHashTwo, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderThree, - orderHash: orderHashThree, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - }); - it("Match with fewer executions when one party has multiple orders that coincide", async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - const secondNFTId = await mintAndApprove721( - buyer, - marketplaceContract.address - ); - - const offerOne = [ - getTestItem721( - nftId, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const considerationOne = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order: orderOne, orderHash: orderHashOne } = await createOrder( - seller, - zone, - offerOne, - considerationOne, - 0 // FULL_OPEN - ); - - const offerTwo = [getItemETH(parseEther("10"), parseEther("10"))]; - - const considerationTwo = [ - getTestItem721( - secondNFTId, - toBN(1), - toBN(1), - seller.address, - testERC721.address - ), - ]; - - const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( - seller, - zone, - offerTwo, - considerationTwo, - 0 // FULL_OPEN - ); - - const offerThree = [ - getTestItem721( - secondNFTId, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const considerationThree = [ - getTestItem721( - nftId, - toBN(1), - toBN(1), - buyer.address, - testERC721.address - ), - ]; - - const { order: orderThree, orderHash: orderHashThree } = - await createOrder( - buyer, - zone, - offerThree, - considerationThree, - 0 // FULL_OPEN - ); - - const fulfillments = [ - [[[1, 0]], [[0, 0]]], - [[[0, 0]], [[2, 0]]], - [[[2, 0]], [[1, 0]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [orderOne, orderTwo, orderThree], - [], // no criteria resolvers - fulfillments, - owner, - 0 // no value - ); - - expect(executions.length).to.equal(fulfillments.length - 1); - - const tx = marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [orderOne, orderTwo, orderThree], - [], - fulfillments, - { - value: 0, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: constants.AddressZero, - }, - { - order: orderTwo, - orderHash: orderHashTwo, - fulfiller: constants.AddressZero, - }, - { - order: orderThree, - orderHash: orderHashThree, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - - describe("Order groups", async () => { - it("Multiple offer components at once", async () => { - // Seller mints NFTs - const { nftId, amount } = await mint1155(seller, 2); - - // Seller approves marketplace contract to transfer NFT - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - // Buyer mints ERC20s - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount.mul(2) - ); - - const offerOne = [getTestItem1155(nftId, amount, amount)]; - - const considerationOne = [ - getTestItem20(tokenAmount, tokenAmount, seller.address), - ]; - - const { order: orderOne, orderHash: orderHashOne } = await createOrder( - seller, - zone, - offerOne, - considerationOne, - 0 // FULL_OPEN - ); - - const offerTwo = [getTestItem1155(nftId, amount, amount)]; - - const considerationTwo = [ - getTestItem20(tokenAmount, tokenAmount, seller.address), - ]; - - const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( - seller, - zone, - offerTwo, - considerationTwo, - 0 // FULL_OPEN - ); - - const offerThree = [ - getTestItem20(tokenAmount.mul(2), tokenAmount.mul(2)), - ]; - - const considerationThree = [ - getTestItem1155( - nftId, - amount.mul(2), - amount.mul(2), - undefined, - buyer.address - ), - ]; - - const { order: orderThree, orderHash: orderHashThree } = - await createOrder( - buyer, - zone, - offerThree, - considerationThree, - 0 // FULL_OPEN - ); - - const fulfillments = [ - [ - [ - [0, 0], - [1, 0], - ], - [[2, 0]], - ], - [[[2, 0]], [[0, 0]]], - [[[2, 0]], [[1, 0]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [orderOne, orderTwo, orderThree], - [], // no criteria resolvers - fulfillments, - owner, - 0 // no value - ); - - expect(executions.length).to.equal(fulfillments.length); - - const tx = marketplaceContract - .connect(buyer) - .matchAdvancedOrders( - [orderOne, orderTwo, orderThree], - [], - fulfillments, - { - value: 0, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: constants.AddressZero, - }, - { - order: orderTwo, - orderHash: orderHashTwo, - fulfiller: constants.AddressZero, - }, - { - order: orderThree, - orderHash: orderHashThree, - fulfiller: constants.AddressZero, - }, - ], - executions, - [], - true - ); - - expect( - toBN("0x" + receipt.events[3].data.slice(66)).toString() - ).to.equal(amount.mul(2).toString()); - - return receipt; - }); - it("Multiple consideration components at once", async () => { - // Seller mints NFTs - const { nftId, amount } = await mint1155(seller, 2); - - // Seller approves marketplace contract to transfer NFT - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - // Buyer mints ERC20s - const tokenAmount = toBN(random128()); - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount.mul(2) - ); - - const offerOne = [ - getTestItem1155(nftId, amount.mul(2), amount.mul(2), undefined), - ]; - - const considerationOne = [ - getTestItem20(tokenAmount.mul(2), tokenAmount.mul(2), seller.address), - ]; - - const { order: orderOne, orderHash: orderHashOne } = await createOrder( - seller, - zone, - offerOne, - considerationOne, - 0 // FULL_OPEN - ); - - const offerTwo = [getTestItem20(tokenAmount, tokenAmount)]; - - const considerationTwo = [ - getTestItem1155(nftId, amount, amount, undefined, buyer.address), - ]; - - const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( - buyer, - zone, - offerTwo, - considerationTwo, - 0 // FULL_OPEN - ); - - const offerThree = [getTestItem20(tokenAmount, tokenAmount)]; - - const considerationThree = [ - getTestItem1155(nftId, amount, amount, undefined, buyer.address), - ]; - - const { order: orderThree, orderHash: orderHashThree } = - await createOrder( - buyer, - zone, - offerThree, - considerationThree, - 0 // FULL_OPEN - ); - - const fulfillments = [ - [ - [[0, 0]], - [ - [1, 0], - [2, 0], - ], - ], - [[[1, 0]], [[0, 0]]], - [[[2, 0]], [[0, 0]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateAdvancedMatchOrders( - [orderOne, orderTwo, orderThree], - [], // no criteria resolvers - fulfillments, - owner, - 0 // no value - ); - - expect(executions.length).to.equal(fulfillments.length); - - await whileImpersonating(buyer.address, provider, async () => { - const tx = marketplaceContract - .connect(buyer) - .matchAdvancedOrders( - [orderOne, orderTwo, orderThree], - [], - fulfillments, - { - value: 0, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: constants.AddressZero, - }, - { - order: orderTwo, - orderHash: orderHashTwo, - fulfiller: constants.AddressZero, - }, - { - order: orderThree, - orderHash: orderHashThree, - fulfiller: constants.AddressZero, - }, - ], - executions, - [], - true - ); - - // TODO: include balance checks on the duplicate ERC20 transfers - - return receipt; - }); - }); - }); - - describe("Complex ERC1155 transfers", async () => { - it("ERC1155 <=> ETH (match)", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const { nftId: secondNftId, amount: secondAmount } = - await mintAndApprove1155(seller, marketplaceContract.address); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155(secondNftId, secondAmount, secondAmount), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(5); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("ERC1155 <=> ETH (match, three items)", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - seller - ); - - // Seller mints third nft - const { nftId: thirdNftId, amount: thirdAmount } = - await mintAndApprove1155(seller, marketplaceContract.address); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155(secondNftId, secondAmount, secondAmount), - getTestItem1155(thirdNftId, thirdAmount, thirdAmount), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[0, 2]], [[1, 2]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(6); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("ERC1155 <=> ETH (match via conduit)", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const { nftId: secondNftId, amount: secondAmount } = - await mintAndApprove1155(seller, conduitOne.address); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155(secondNftId, secondAmount, secondAmount), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(5); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("ERC1155 <=> ETH (match, single item)", async () => { - // Seller mints first nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount, undefined)]; - - const consideration = []; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [toFulfillment([[0, 0]], [[1, 0]])]; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(1); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("ERC1155 <=> ETH (match, single 1155)", async () => { - // Seller mints first nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount, undefined)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("ERC1155 <=> ETH (match, two different 1155 contracts)", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const secondNftId = toBN(randomBN(4)); - const secondAmount = toBN(randomBN(4)); - await testERC1155Two.mint(seller.address, secondNftId, secondAmount); - - // Seller approves marketplace contract to transfer NFTs - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - await expect( - testERC1155Two - .connect(seller) - .setApprovalForAll(marketplaceContract.address, true) - ) - .to.emit(testERC1155Two, "ApprovalForAll") - .withArgs(seller.address, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155( - secondNftId, - secondAmount, - secondAmount, - testERC1155Two.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(5); - - await marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - }); - it("ERC1155 <=> ETH (match, one single and one with two 1155's)", async () => { - // Seller mints first nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - // Seller mints second nft - const secondNftId = toBN(randomBN(4)); - const secondAmount = toBN(randomBN(4)); - await testERC1155Two.mint(seller.address, secondNftId, secondAmount); - - // Seller mints third nft - const { nftId: thirdNftId, amount: thirdAmount } = await mint1155( - seller - ); - - // Seller approves marketplace contract to transfer NFTs - - await expect( - testERC1155Two - .connect(seller) - .setApprovalForAll(marketplaceContract.address, true) - ) - .to.emit(testERC1155Two, "ApprovalForAll") - .withArgs(seller.address, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155( - secondNftId, - secondAmount, - secondAmount, - testERC1155Two.address - ), - getTestItem1155(thirdNftId, thirdAmount, thirdAmount), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[0, 2]], [[1, 2]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(6); - - await marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - }); - it("ERC1155 <=> ETH (match, two different groups of 1155's)", async () => { - // Seller mints first nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - // Seller mints second nft - const secondNftId = toBN(randomBN(4)); - const secondAmount = toBN(randomBN(4)); - await testERC1155Two.mint(seller.address, secondNftId, secondAmount); - - // Seller mints third nft - const { nftId: thirdNftId, amount: thirdAmount } = await mint1155( - seller - ); - - // Seller mints fourth nft - const fourthNftId = toBN(randomBN(4)); - const fourthAmount = toBN(randomBN(4)); - await testERC1155Two.mint(seller.address, fourthNftId, fourthAmount); - - // Seller approves marketplace contract to transfer NFTs - - await expect( - testERC1155Two - .connect(seller) - .setApprovalForAll(marketplaceContract.address, true) - ) - .to.emit(testERC1155Two, "ApprovalForAll") - .withArgs(seller.address, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155( - secondNftId, - secondAmount, - secondAmount, - testERC1155Two.address - ), - getTestItem1155(thirdNftId, thirdAmount, thirdAmount), - getTestItem1155( - fourthNftId, - fourthAmount, - fourthAmount, - testERC1155Two.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[0, 2]], [[1, 2]]], - [[[0, 3]], [[1, 3]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(7); - - await marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - }); - }); - - describe("Fulfill Available Orders", async () => { - it("Can fulfill a single order via fulfillAvailableOrders", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address, - 10 - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [toFulfillmentComponents([[0, 0]])]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]].map( - toFulfillmentComponents - ); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [order], - offerComponents, - considerationComponents, - toKey(false), - 100, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Can fulfill a single order via fulfillAvailableAdvancedOrders", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address, - 11 - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [[[0, 0]]]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Can fulfill a single order via fulfillAvailableAdvancedOrders with recipient specified", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [[[0, 0]]]; - - const considerationComponents = [[[0, 0]], [[0, 1]]]; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - owner.address, - 100, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - recipient: owner.address, - }, - ]); - - return receipt; - }); - }); - it("Can fulfill and aggregate multiple orders via fulfillAvailableOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 1, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - toFulfillmentComponents([ - [0, 0], - [1, 0], - ]), - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - ], - [ - [0, 1], - [1, 1], - ], - [ - [0, 2], - [1, 2], - ], - ].map(toFulfillmentComponents); - - await whileImpersonating(buyer.address, provider, async () => { - await withBalanceChecks( - [orderOne, orderTwo], - 0, - null, - async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [orderOne, orderTwo], - offerComponents, - considerationComponents, - toKey(false), - 100, - { - value: value.mul(2), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - { - order: orderTwo, - orderHash: orderHashTwo, - fulfiller: buyer.address, - }, - ], - [], - [], - false, - 2 - ); - return receipt; - }, - 2 - ); - }); - }); - it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 2, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - toFulfillmentComponents([ - [0, 0], - [1, 0], - ]), - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - ], - [ - [0, 1], - [1, 1], - ], - [ - [0, 2], - [1, 2], - ], - ].map(toFulfillmentComponents); - - await whileImpersonating(buyer.address, provider, async () => { - await withBalanceChecks( - [orderOne, orderTwo], - 0, - null, - async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [orderOne, orderTwo], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value: value.mul(2), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - { - order: orderTwo, - orderHash: orderHashTwo, - fulfiller: buyer.address, - }, - ], - [], - [], - false, - 2 - ); - return receipt; - }, - 2 - ); - }); - }); - it("Can fulfill and aggregate a max number of multiple orders via fulfillAvailableOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 3, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { order: orderTwo } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [0, 0], - [1, 0], - ], - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - ], - [ - [0, 1], - [1, 1], - ], - [ - [0, 2], - [1, 2], - ], - ]; - - await whileImpersonating(buyer.address, provider, async () => { - await withBalanceChecks( - [orderOne], - 0, - null, - async () => { - const { executions } = await marketplaceContract - .connect(buyer) - .callStatic.fulfillAvailableOrders( - [orderOne, orderTwo], - offerComponents, - considerationComponents, - toKey(false), - 1, - { - value: value.mul(2), - } - ); - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [orderOne, orderTwo], - offerComponents, - considerationComponents, - toKey(false), - 1, - { - value: value.mul(2), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - ], - executions - ); - - return receipt; - }, - 1 - ); - }); - }); - it("Can fulfill and aggregate a max number of multiple orders via fulfillAvailableAdvancedOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 4, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { order: orderTwo } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [0, 0], - [1, 0], - ], - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - ], - [ - [0, 1], - [1, 1], - ], - [ - [0, 2], - [1, 2], - ], - ]; - - await whileImpersonating(buyer.address, provider, async () => { - await withBalanceChecks( - [orderOne], - 0, - null, - async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [orderOne, orderTwo], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 1, - { - value: value.mul(2), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - ], - [], - [], - false, - 1 - ); - - return receipt; - }, - 1 - ); - }); - }); - it("Can fulfill and aggregate multiple orders via fulfillAvailableOrders with failing orders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 5, - 100000 - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // second order is expired - const { order: orderTwo } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "EXPIRED" - ); - - // third order will be cancelled - const { - order: orderThree, - orderHash: orderHashThree, - orderComponents, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // can cancel it - await expect( - marketplaceContract.connect(seller).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHashThree, seller.address, zone.address); - - // fourth order will be filled - const { order: orderFour, orderHash: orderHashFour } = - await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // can fill it - await withBalanceChecks([orderFour], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(orderFour, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order: orderFour, - orderHash: orderHashFour, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - - const offerComponents = [ - [ - [0, 0], - [1, 0], - [2, 0], - [3, 0], - ], - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - [2, 0], - [3, 0], - ], - [ - [0, 1], - [1, 1], - [2, 1], - [3, 1], - ], - [ - [0, 2], - [1, 2], - [2, 2], - [3, 2], - ], - ]; - - await withBalanceChecks([orderOne], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [orderOne, orderTwo, orderThree, orderFour], - offerComponents, - considerationComponents, - toKey(false), - 100, - { - value: value.mul(4), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders with failing orders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 6, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // second order is expired - const { order: orderTwo } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "EXPIRED" - ); - - // third order will be cancelled - const { - order: orderThree, - orderHash: orderHashThree, - orderComponents, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // can cancel it - await expect( - marketplaceContract.connect(seller).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHashThree, seller.address, zone.address); - - // fourth order will be filled - const { order: orderFour, orderHash: orderHashFour } = - await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // can fill it - await withBalanceChecks([orderFour], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(orderFour, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order: orderFour, - orderHash: orderHashFour, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - - const offerComponents = [ - [ - [0, 0], - [1, 0], - [2, 0], - [3, 0], - ], - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - [2, 0], - [3, 0], - ], - [ - [0, 1], - [1, 1], - [2, 1], - [3, 1], - ], - [ - [0, 2], - [1, 2], - [2, 2], - [3, 2], - ], - ]; - - await withBalanceChecks([orderOne], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [orderOne, orderTwo, orderThree, orderFour], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value: value.mul(4), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Can fulfill and aggregate multiple orders via fulfillAvailableAdvancedOrders with failing components including criteria", async () => { - // Seller mints first nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 1, - 7, - 10000 - ); - - // Seller mints second nft - - // Seller mints nfts for criteria-based item - const criteriaNftId = randomBN(); - const secondCriteriaNFTId = randomBN(); - const thirdCriteriaNFTId = randomBN(); - - await testERC721.mint(seller.address, criteriaNftId); - await testERC721.mint(seller.address, secondCriteriaNFTId); - await testERC721.mint(seller.address, thirdCriteriaNFTId); - - const tokenIds = [ - criteriaNftId, - secondCriteriaNFTId, - thirdCriteriaNFTId, - ]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem1155(nftId, amount, amount, undefined)]; - - const offerTwo = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver( - 1, - 0, - 0, - criteriaNftId, - proofs[criteriaNftId.toString()] - ), - ]; - - const { - order: orderOne, - orderHash: orderHashOne, - value, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // second order is expired - const { order: orderTwo } = await createOrder( - seller, - zone, - offerTwo, - consideration, - 0, // FULL_OPEN - criteriaResolvers, - "EXPIRED" - ); - - const offerComponents = [[[0, 0]], [[1, 0]]]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - ], - [ - [0, 1], - [1, 1], - ], - [ - [0, 2], - [1, 2], - ], - ]; - - await withBalanceChecks([orderOne], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [orderOne, orderTwo], - criteriaResolvers, - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value: value.mul(2), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order: orderOne, - orderHash: orderHashOne, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - }); - }); - - describe("Conduit tests", async () => { - let seller; - let buyer; - let sellerContract; - let buyerContract; - let tempConduit; - - beforeEach(async () => { - // Setup basic buyer/seller wallets with ETH - seller = new ethers.Wallet(randomHex(32), provider); - buyer = new ethers.Wallet(randomHex(32), provider); - zone = new ethers.Wallet(randomHex(32), provider); - - sellerContract = await EIP1271WalletFactory.deploy(seller.address); - buyerContract = await EIP1271WalletFactory.deploy(buyer.address); - - // Deploy a new conduit - tempConduit = await deployNewConduit(owner); - - await Promise.all( - [seller, buyer, zone, sellerContract, buyerContract].map((wallet) => - faucet(wallet.address, provider) - ) - ); - }); - - it("Adds a channel, and executes transfers (ERC1155 with batch)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - - await testERC1155.mint(seller.address, nftId, amount.mul(2)); - await testERC1155.mint(seller.address, secondNftId, secondAmount.mul(2)); - await set1155ApprovalForAll(seller, tempConduit.address, true); - - await tempConduit.connect(seller).executeWithBatch1155( - [], - [ - { - token: testERC1155.address, - from: seller.address, - to: buyer.address, - ids: [nftId, secondNftId], - amounts: [amount, secondAmount], - }, - { - token: testERC1155.address, - from: seller.address, - to: buyer.address, - ids: [secondNftId, nftId], - amounts: [secondAmount, amount], - }, - ] - ); - }); - - it("Adds a channel, and executes only batch transfers (ERC1155 with batch)", async () => { - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - - await testERC1155.mint(seller.address, nftId, amount.mul(2)); - await testERC1155.mint(seller.address, secondNftId, secondAmount.mul(2)); - await set1155ApprovalForAll(seller, tempConduit.address, true); - - await tempConduit.connect(seller).executeBatch1155([ - { - token: testERC1155.address, - from: seller.address, - to: buyer.address, - ids: [nftId, secondNftId], - amounts: [amount, secondAmount], - }, - { - token: testERC1155.address, - from: seller.address, - to: buyer.address, - ids: [secondNftId, nftId], - amounts: [secondAmount, amount], - }, - ]); - }); - - it("Adds a channel, and executes transfers (ERC721)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - // Seller mints nft - const nftId = randomBN(); - await testERC721.mint(seller.address, nftId); - - const secondNftId = randomBN(); - await testERC721.mint(seller.address, secondNftId); - - // Check ownership - expect(await testERC721.ownerOf(nftId)).to.equal(seller.address); - expect(await testERC721.ownerOf(secondNftId)).to.equal(seller.address); - - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC721 - .connect(seller) - .setApprovalForAll(tempConduit.address, true) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(seller.address, tempConduit.address, true); - }); - - await tempConduit.connect(seller).execute([ - { - itemType: 2, // ERC721 - token: testERC721.address, - from: seller.address, - to: buyer.address, - identifier: nftId, - amount: ethers.BigNumber.from(1), - }, - { - itemType: 2, // ERC721 - token: testERC721.address, - from: seller.address, - to: buyer.address, - identifier: secondNftId, - amount: ethers.BigNumber.from(1), - }, - ]); - - // Check ownership - expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address); - expect(await testERC721.ownerOf(secondNftId)).to.equal(buyer.address); - }); - - it("Adds a channel, and executes transfers (ERC721 + ERC20)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - // Seller mints nft - const nftId = randomBN(); - await testERC721.mint(seller.address, nftId); - - // Check ownership - expect(await testERC721.ownerOf(nftId)).to.equal(seller.address); - - // Set approval of nft - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC721 - .connect(seller) - .setApprovalForAll(tempConduit.address, true) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(seller.address, tempConduit.address, true); - }); - - const tokenAmount = minRandom(100); - await testERC20.mint(seller.address, tokenAmount); - - // Check balance - expect(await testERC20.balanceOf(seller.address)).to.equal(tokenAmount); - - // Seller approves conduit contract to transfer tokens - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC20.connect(seller).approve(tempConduit.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(seller.address, tempConduit.address, tokenAmount); - }); - - // Send an ERC721 and (token amount - 100) ERC20 tokens - await tempConduit.connect(seller).execute([ - { - itemType: 2, // ERC721 - token: testERC721.address, - from: seller.address, - to: buyer.address, - identifier: nftId, - amount: ethers.BigNumber.from(1), - }, - { - itemType: 1, // ERC20 - token: testERC20.address, - from: seller.address, - to: buyer.address, - identifier: 0, - amount: tokenAmount.sub(100), - }, - ]); - - // Check ownership - expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address); - // Check balance - expect(await testERC20.balanceOf(seller.address)).to.equal(100); - expect(await testERC20.balanceOf(buyer.address)).to.equal( - tokenAmount.sub(100) - ); - }); - - it("Adds a channel, and executes transfers (ERC721 + ERC1155)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - // Seller mints nft - const nftId = randomBN(); - await testERC721.mint(seller.address, nftId); - - // Check ownership - expect(await testERC721.ownerOf(nftId)).to.equal(seller.address); - - // Set approval of nft - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC721 - .connect(seller) - .setApprovalForAll(tempConduit.address, true) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(seller.address, tempConduit.address, true); - }); - - const secondNftId = random128(); - const amount = random128().add(1); - await testERC1155.mint(seller.address, secondNftId, amount); - - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC1155 - .connect(seller) - .setApprovalForAll(tempConduit.address, true) - ) - .to.emit(testERC1155, "ApprovalForAll") - .withArgs(seller.address, tempConduit.address, true); - }); - - // Check ownership - expect(await testERC1155.balanceOf(seller.address, secondNftId)).to.equal( - amount - ); - - // Send an ERC721 and ERC1155 - await tempConduit.connect(seller).execute([ - { - itemType: 2, // ERC721 - token: testERC721.address, - from: seller.address, - to: buyer.address, - identifier: nftId, - amount: ethers.BigNumber.from(1), - }, - { - itemType: 3, // ERC1155 - token: testERC1155.address, - from: seller.address, - to: buyer.address, - identifier: secondNftId, - amount: amount.sub(10), - }, - ]); - - // Check ownership - expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address); - // Check balance - expect(await testERC1155.balanceOf(seller.address, secondNftId)).to.equal( - 10 - ); - expect(await testERC1155.balanceOf(buyer.address, secondNftId)).to.equal( - amount.sub(10) - ); - }); - - it("Adds a channel, and executes transfers (ERC20 + ERC1155)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - // Seller mints nft - const tokenAmount = minRandom(100).div(100); - await testERC20.mint(seller.address, tokenAmount); - - // Check balance - expect(await testERC20.balanceOf(seller.address)).to.equal(tokenAmount); - - // Seller approves conduit contract to transfer tokens - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC20.connect(seller).approve(tempConduit.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(seller.address, tempConduit.address, tokenAmount); - }); - - const nftId = random128(); - const erc1155amount = random128().add(1); - await testERC1155.mint(seller.address, nftId, erc1155amount); - - await whileImpersonating(seller.address, provider, async () => { - await expect( - testERC1155 - .connect(seller) - .setApprovalForAll(tempConduit.address, true) - ) - .to.emit(testERC1155, "ApprovalForAll") - .withArgs(seller.address, tempConduit.address, true); - }); - - // Check ownership - expect(await testERC1155.balanceOf(seller.address, nftId)).to.equal( - erc1155amount - ); - - // Send an ERC20 and ERC1155 - await tempConduit.connect(seller).execute([ - { - itemType: 1, // ERC20 - token: testERC20.address, - from: seller.address, - to: buyer.address, - identifier: 0, - amount: tokenAmount.sub(100), - }, - { - itemType: 3, // ERC1155 - token: testERC1155.address, - from: seller.address, - to: buyer.address, - identifier: nftId, - amount: erc1155amount.sub(10), - }, - ]); - - // Check balance - expect(await testERC20.balanceOf(seller.address)).to.equal(100); - expect(await testERC20.balanceOf(buyer.address)).to.equal( - tokenAmount.sub(100) - ); - expect(await testERC1155.balanceOf(seller.address, nftId)).to.equal(10); - expect(await testERC1155.balanceOf(buyer.address, nftId)).to.equal( - erc1155amount.sub(10) - ); - }); - - it("Adds a channel, and executes transfers (ERC20 + ERC721 + ERC1155)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - // Create/Approve X amount of ERC20s - const erc20Transfer = await createTransferWithApproval( - testERC20, - seller, - 1, - tempConduit.address, - seller.address, - buyer.address - ); - - // Create/Approve Y amount of ERC721s - const erc721Transfer = await createTransferWithApproval( - testERC721, - seller, - 2, - tempConduit.address, - seller.address, - buyer.address - ); - - // Create/Approve Z amount of ERC1155s - const erc1155Transfer = await createTransferWithApproval( - testERC1155, - seller, - 3, - tempConduit.address, - seller.address, - buyer.address - ); - - // Send an ERC20, ERC721, and ERC1155 - await tempConduit - .connect(seller) - .execute([erc20Transfer, erc721Transfer, erc1155Transfer]); - - // Check ownership - expect(await testERC721.ownerOf(erc721Transfer.identifier)).to.equal( - buyer.address - ); - // Check balance - expect(await testERC20.balanceOf(seller.address)).to.equal(0); - expect(await testERC20.balanceOf(buyer.address)).to.equal( - erc20Transfer.amount - ); - expect( - await testERC1155.balanceOf(seller.address, erc1155Transfer.identifier) - ).to.equal(0); - expect( - await testERC1155.balanceOf(buyer.address, erc1155Transfer.identifier) - ).to.equal(erc1155Transfer.amount); - }); - - it("Adds a channel, and executes transfers (many token types)", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - // Get 3 Numbers that's value adds to Item Amount and minimum 1. - const itemsToCreate = 64; - const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); - const numEC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); - const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numEC721s); - - const erc20Contracts = [numERC20s]; - const erc20Transfers = [numERC20s]; - - const erc721Contracts = [numEC721s]; - const erc721Transfers = [numEC721s]; - - const erc1155Contracts = [numERC1155s]; - const erc1155Transfers = [numERC1155s]; - - // Create numERC20s amount of ERC20 objects - for (let i = 0; i < numERC20s; i++) { - // Deploy Contract - const { testERC20: tempERC20Contract } = await fixtureERC20(owner); - // Create/Approve X amount of ERC20s - const erc20Transfer = await createTransferWithApproval( - tempERC20Contract, - seller, - 1, - tempConduit.address, - seller.address, - buyer.address - ); - erc20Contracts[i] = tempERC20Contract; - erc20Transfers[i] = erc20Transfer; - } - - // Create numEC721s amount of ERC20 objects - for (let i = 0; i < numEC721s; i++) { - // Deploy Contract - const { testERC721: tempERC721Contract } = await fixtureERC721(owner); - // Create/Approve numEC721s amount of ERC721s - const erc721Transfer = await createTransferWithApproval( - tempERC721Contract, - seller, - 2, - tempConduit.address, - seller.address, - buyer.address - ); - erc721Contracts[i] = tempERC721Contract; - erc721Transfers[i] = erc721Transfer; - } - - // Create numERC1155s amount of ERC1155 objects - for (let i = 0; i < numERC1155s; i++) { - // Deploy Contract - const { testERC1155: tempERC1155Contract } = await fixtureERC1155( - owner - ); - // Create/Approve numERC1155s amount of ERC1155s - const erc1155Transfer = await createTransferWithApproval( - tempERC1155Contract, - seller, - 3, - tempConduit.address, - seller.address, - buyer.address - ); - erc1155Contracts[i] = tempERC1155Contract; - erc1155Transfers[i] = erc1155Transfer; - } - - const transfers = erc20Transfers.concat( - erc721Transfers, - erc1155Transfers - ); - const contracts = erc20Contracts.concat( - erc721Contracts, - erc1155Contracts - ); - // Send the transfers - await tempConduit.connect(seller).execute(transfers); - - // Loop through all transfer to do ownership/balance checks - for (let i = 0; i < transfers.length; i++) { - // Get Itemtype, token, from, to, amount, identifier - itemType = transfers[i].itemType; - token = contracts[i]; - from = transfers[i].from; - to = transfers[i].to; - amount = transfers[i].amount; - identifier = transfers[i].identifier; - - switch (itemType) { - case 1: // ERC20 - // Check balance - expect(await token.balanceOf(from)).to.equal(0); - expect(await token.balanceOf(to)).to.equal(amount); - break; - case 2: // ERC721 - case 4: // ERC721_WITH_CRITERIA - expect(await token.ownerOf(identifier)).to.equal(to); - break; - case 3: // ERC1155 - case 5: // ERC1155_WITH_CRITERIA - // Check balance - expect(await token.balanceOf(from, identifier)).to.equal(0); - expect(await token.balanceOf(to, identifier)).to.equal(amount); - break; - } - } - }); - - it("Reverts on calls to batch transfer 1155 items with no contract on a conduit", async () => { - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, owner.address, true); - }); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - - await set1155ApprovalForAll(owner, tempConduit.address, true); - - await expect( - tempConduit.connect(owner).executeWithBatch1155( - [], - [ - { - token: constants.AddressZero, - from: owner.address, - to: buyer.address, - ids: [nftId, secondNftId], - amounts: [amount, secondAmount], - }, - ] - ) - ).to.be.revertedWith("NoContract"); - }); - - it("Reverts on calls to only batch transfer 1155 items with no contract on a conduit", async () => { - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, owner.address, true); - }); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - - await set1155ApprovalForAll(owner, tempConduit.address, true); - - await expect( - tempConduit.connect(owner).executeBatch1155([ - { - token: constants.AddressZero, - from: owner.address, - to: buyer.address, - ids: [nftId, secondNftId], - amounts: [amount, secondAmount], - }, - ]) - ).to.be.revertedWith("NoContract"); - }); - - it("ERC1155 batch transfer reverts with revert data if it has sufficient gas", async () => { - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - await expect( - tempConduit.connect(seller).executeWithBatch1155( - [], - [ - { - token: testERC1155.address, - from: seller.address, - to: buyer.address, - ids: [1], - amounts: [1], - }, - ] - ) - ).to.be.revertedWith("NOT_AUTHORIZED"); - }); - if (!process.env.REFERENCE) { - it("ERC1155 batch transfer sends no data", async () => { - const receiver = await deployContract("ERC1155BatchRecipient", owner); - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - const { nftId: thirdNftId, amount: thirdAmount } = await mint1155( - owner, - 2 - ); - - await testERC1155.mint(seller.address, nftId, amount.mul(2)); - await testERC1155.mint( - seller.address, - secondNftId, - secondAmount.mul(2) - ); - await testERC1155.mint(seller.address, thirdNftId, thirdAmount.mul(2)); - await set1155ApprovalForAll(seller, tempConduit.address, true); - - await tempConduit.connect(seller).executeWithBatch1155( - [], - [ - { - token: testERC1155.address, - from: seller.address, - to: receiver.address, - ids: [nftId, secondNftId, thirdNftId], - amounts: [amount, secondAmount, thirdAmount], - }, - { - token: testERC1155.address, - from: seller.address, - to: receiver.address, - ids: [secondNftId, nftId], - amounts: [secondAmount, amount], - }, - ] - ); - }); - - it("ERC1155 batch transfer reverts with generic error if it has insufficient gas to copy revert data", async () => { - const receiver = await deployContract( - "ExcessReturnDataRecipient", - owner - ); - // Owner updates conduit channel to allow seller access - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, seller.address, true); - }); - - await expect( - tempConduit.connect(seller).executeWithBatch1155( - [], - [ - { - token: receiver.address, - from: seller.address, - to: receiver.address, - ids: [1], - amounts: [1], - }, - ] - ) - ).to.be.revertedWith( - `ERC1155BatchTransferGenericFailure("${receiver.address}", "${seller.address}", "${receiver.address}", [1], [1])` - ); - }); - } - - it("Makes batch transfer 1155 items through a conduit", async () => { - const tempConduitKey = owner.address + "ff00000000000000000000f1"; - - const { conduit: tempConduitAddress } = - await conduitController.getConduit(tempConduitKey); - - await conduitController - .connect(owner) - .createConduit(tempConduitKey, owner.address); - - const tempConduit = conduitImplementation.attach(tempConduitAddress); - - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, owner.address, true); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - - const { nftId: thirdNftId, amount: thirdAmount } = await mint1155( - owner, - 2 - ); - - const { nftId: nftId4, amount: amount4 } = await mint1155(owner, 2); - - const { nftId: nftId5, amount: amount5 } = await mint1155(owner, 2); - - const { nftId: nftId6, amount: amount6 } = await mint1155(owner, 2); - - const { nftId: nftId7, amount: amount7 } = await mint1155(owner, 2); - - const { nftId: nftId8, amount: amount8 } = await mint1155(owner, 2); - - const { nftId: nftId9, amount: amount9 } = await mint1155(owner, 2); - - const { nftId: nftId10, amount: amount10 } = await mint1155(owner, 2); - - await set1155ApprovalForAll(owner, tempConduit.address, true); - - await tempConduit.connect(owner).executeWithBatch1155( - [], - [ - { - token: testERC1155.address, - from: owner.address, - to: buyer.address, - ids: [ - nftId, - secondNftId, - thirdNftId, - nftId4, - nftId5, - nftId6, - nftId7, - nftId8, - nftId9, - nftId10, - ], - amounts: [ - amount, - secondAmount, - thirdAmount, - amount4, - amount5, - amount6, - amount7, - amount8, - amount9, - amount10, - ], - }, - ] - ); - }); - - it("Performs complex batch transfer through a conduit", async () => { - const tempConduitKey = owner.address + "f100000000000000000000f1"; - - const { conduit: tempConduitAddress } = - await conduitController.getConduit(tempConduitKey); - - await conduitController - .connect(owner) - .createConduit(tempConduitKey, owner.address); - - const tempConduit = conduitImplementation.attach(tempConduitAddress); - - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, owner.address, true); - - const { nftId, amount } = await mint1155(owner, 2); - - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - owner, - 2 - ); - - const { nftId: thirdNftId, amount: thirdAmount } = await mint1155( - owner, - 2 - ); - - const { nftId: nftId4, amount: amount4 } = await mint1155(owner, 2); - - const { nftId: nftId5, amount: amount5 } = await mint1155( - owner, - 2, - testERC1155Two - ); - - const { nftId: nftId6, amount: amount6 } = await mint1155( - owner, - 2, - testERC1155Two - ); - - const { nftId: nftId7, amount: amount7 } = await mint1155( - owner, - 2, - testERC1155Two - ); - - const { nftId: nftId8, amount: amount8 } = await mint1155( - owner, - 2, - testERC1155Two - ); - - const amount9 = toBN(randomBN(4)).add(1); - await mintAndApproveERC20(owner, tempConduit.address, amount9.mul(2)); - - const nftId10 = await mint721(owner); - - await set1155ApprovalForAll(owner, tempConduit.address, true); - - await expect( - testERC1155Two - .connect(owner) - .setApprovalForAll(tempConduit.address, true) - ) - .to.emit(testERC1155Two, "ApprovalForAll") - .withArgs(owner.address, tempConduit.address, true); - - await set721ApprovalForAll(owner, tempConduit.address, true); - - const newAddress = toAddress(12345); - - await tempConduit.connect(owner).executeWithBatch1155( - [ - { - itemType: 1, - token: testERC20.address, - from: owner.address, - to: newAddress, - identifier: toBN(0), - amount: amount9, - }, - { - itemType: 2, - token: testERC721.address, - from: owner.address, - to: newAddress, - identifier: nftId10, - amount: toBN(1), - }, - ], - [ - { - token: testERC1155.address, - from: owner.address, - to: newAddress, - ids: [nftId, secondNftId, thirdNftId, nftId4], - amounts: [amount, secondAmount, thirdAmount, amount4], - }, - { - token: testERC1155Two.address, - from: owner.address, - to: newAddress, - ids: [nftId5, nftId6, nftId7, nftId8], - amounts: [amount5, amount6, amount7, amount8], - }, - ] - ); - - expect(await testERC1155.balanceOf(newAddress, nftId)).to.equal(amount); - expect(await testERC1155.balanceOf(newAddress, secondNftId)).to.equal( - secondAmount - ); - expect(await testERC1155.balanceOf(newAddress, thirdNftId)).to.equal( - thirdAmount - ); - expect(await testERC1155.balanceOf(newAddress, nftId4)).to.equal(amount4); - - expect(await testERC1155Two.balanceOf(newAddress, nftId5)).to.equal( - amount5 - ); - expect(await testERC1155Two.balanceOf(newAddress, nftId6)).to.equal( - amount6 - ); - expect(await testERC1155Two.balanceOf(newAddress, nftId7)).to.equal( - amount7 - ); - expect(await testERC1155Two.balanceOf(newAddress, nftId8)).to.equal( - amount8 - ); - - expect(await testERC20.balanceOf(newAddress)).to.equal(amount9); - expect(await testERC721.ownerOf(nftId10)).to.equal(newAddress); - }); - - it("ERC1155 <=> ETH (match, two different groups of 1155's)", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const secondNftId = toBN(randomBN(4)); - const secondAmount = toBN(randomBN(4)); - await testERC1155Two.mint(seller.address, secondNftId, secondAmount); - - // Seller mints third nft - const { nftId: thirdNftId, amount: thirdAmount } = await mint1155(seller); - - // Seller mints fourth nft - const fourthNftId = toBN(randomBN(4)); - const fourthAmount = toBN(randomBN(4)); - await testERC1155Two.mint(seller.address, fourthNftId, fourthAmount); - - // Seller approves marketplace contract to transfer NFTs - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - await expect( - testERC1155Two - .connect(seller) - .setApprovalForAll(marketplaceContract.address, true) - ) - .to.emit(testERC1155Two, "ApprovalForAll") - .withArgs(seller.address, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, amount, amount), - getTestItem1155( - secondNftId, - secondAmount, - secondAmount, - testERC1155Two.address - ), - getTestItem1155(thirdNftId, thirdAmount, thirdAmount), - getTestItem1155( - fourthNftId, - fourthAmount, - fourthAmount, - testERC1155Two.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[0, 2]], [[1, 2]]], - [[[0, 3]], [[1, 3]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(7); - - await marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - }); - - it("Reverts when attempting to update a conduit channel when call is not from controller", async () => { - await expect( - conduitOne.connect(owner).updateChannel(constants.AddressZero, true) - ).to.be.revertedWith("InvalidController"); - }); - - it("Reverts when attempting to execute transfers on a conduit when not called from a channel", async () => { - let expectedRevertReason = - getCustomRevertSelector("ChannelClosed(address)") + - owner.address.slice(2).padStart(64, "0").toLowerCase(); - - let tx = await conduitOne.connect(owner).populateTransaction.execute([]); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect(conduitOne.connect(owner).execute([])).to.be.reverted; - }); - - it("Reverts when attempting to execute with 1155 transfers on a conduit when not called from a channel", async () => { - await expect( - conduitOne.connect(owner).executeWithBatch1155([], []) - ).to.be.revertedWith("ChannelClosed", owner); - }); - - it("Reverts when attempting to execute batch 1155 transfers on a conduit when not called from a channel", async () => { - await expect( - conduitOne.connect(owner).executeBatch1155([]) - ).to.be.revertedWith("ChannelClosed", owner); - }); - - it("Retrieves the owner of a conduit", async () => { - const ownerOf = await conduitController.ownerOf(conduitOne.address); - expect(ownerOf).to.equal(owner.address); - - await expect( - conduitController.connect(owner).ownerOf(buyer.address) - ).to.be.revertedWith("NoConduit"); - }); - - it("Retrieves the key of a conduit", async () => { - const key = await conduitController.getKey(conduitOne.address); - expect(key.toLowerCase()).to.equal(conduitKeyOne.toLowerCase()); - - await expect( - conduitController.connect(owner).getKey(buyer.address) - ).to.be.revertedWith("NoConduit"); - }); - - it("Retrieves the status of a conduit channel", async () => { - let isOpen = await conduitController.getChannelStatus( - conduitOne.address, - marketplaceContract.address - ); - expect(isOpen).to.be.true; - - isOpen = await conduitController.getChannelStatus( - conduitOne.address, - seller.address - ); - expect(isOpen).to.be.false; - - await expect( - conduitController - .connect(owner) - .getChannelStatus(buyer.address, seller.address) - ).to.be.revertedWith("NoConduit"); - }); - - it("Retrieves conduit channels from the controller", async () => { - const totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(1); - - await expect( - conduitController.connect(owner).getTotalChannels(buyer.address) - ).to.be.revertedWith("NoConduit"); - - const firstChannel = await conduitController.getChannel( - conduitOne.address, - 0 - ); - expect(firstChannel).to.equal(marketplaceContract.address); - - await expect( - conduitController - .connect(owner) - .getChannel(buyer.address, totalChannels - 1) - ).to.be.revertedWith("NoConduit"); - - await expect( - conduitController.connect(owner).getChannel(conduitOne.address, 1) - ).to.be.revertedWith("ChannelOutOfRange", conduitOne.address); - - await expect( - conduitController.connect(owner).getChannel(conduitOne.address, 2) - ).to.be.revertedWith("ChannelOutOfRange", conduitOne.address); - - const channels = await conduitController.getChannels(conduitOne.address); - expect(channels.length).to.equal(1); - expect(channels[0]).to.equal(marketplaceContract.address); - - await expect( - conduitController.connect(owner).getChannels(buyer.address) - ).to.be.revertedWith("NoConduit"); - }); - - it("Adds and removes channels", async () => { - // Get number of open channels - let totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(1); - - let isOpen = await conduitController.getChannelStatus( - conduitOne.address, - marketplaceContract.address - ); - expect(isOpen).to.be.true; - - // No-op - await expect( - conduitController - .connect(owner) - .updateChannel(conduitOne.address, marketplaceContract.address, true) - ).to.be.reverted; // ChannelStatusAlreadySet - - isOpen = await conduitController.getChannelStatus( - conduitOne.address, - marketplaceContract.address - ); - expect(isOpen).to.be.true; - - // Get number of open channels - totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(1); - - await conduitController - .connect(owner) - .updateChannel(conduitOne.address, seller.address, true); - - isOpen = await conduitController.getChannelStatus( - conduitOne.address, - seller.address - ); - expect(isOpen).to.be.true; - - // Get number of open channels - totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(2); - - await conduitController - .connect(owner) - .updateChannel(conduitOne.address, marketplaceContract.address, false); - - isOpen = await conduitController.getChannelStatus( - conduitOne.address, - marketplaceContract.address - ); - expect(isOpen).to.be.false; - - // Get number of open channels - totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(1); - - await conduitController - .connect(owner) - .updateChannel(conduitOne.address, seller.address, false); - - isOpen = await conduitController.getChannelStatus( - conduitOne.address, - seller.address - ); - expect(isOpen).to.be.false; - - // Get number of open channels - totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(0); - - await conduitController - .connect(owner) - .updateChannel(conduitOne.address, marketplaceContract.address, true); - - isOpen = await conduitController.getChannelStatus( - conduitOne.address, - marketplaceContract.address - ); - expect(isOpen).to.be.true; - - // Get number of open channels - totalChannels = await conduitController.getTotalChannels( - conduitOne.address - ); - expect(totalChannels).to.equal(1); - }); - - it("Reverts on an attempt to move an unsupported item", async () => { - await conduitController - .connect(owner) - .updateChannel(conduitOne.address, seller.address, true); - - const isOpen = await conduitController.getChannelStatus( - conduitOne.address, - seller.address - ); - expect(isOpen).to.be.true; - - await expect( - conduitOne.connect(seller).executeWithBatch1155( - [ - { - itemType: 0, // NATIVE (invalid) - token: constants.AddressZero, - from: conduitOne.address, - to: seller.address, - identifier: 0, - amount: 0, - }, - ], - [] - ) - ).to.be.revertedWith("InvalidItemType"); - }); - - it("Reverts when attempting to create a conduit not scoped to the creator", async () => { - await expect( - conduitController - .connect(owner) - .createConduit(constants.HashZero, owner.address) - ).to.be.revertedWith("InvalidCreator"); - }); - - it("Reverts when attempting to create a conduit that already exists", async () => { - await expect( - conduitController - .connect(owner) - .createConduit(conduitKeyOne, owner.address) - ).to.be.revertedWith(`ConduitAlreadyExists("${conduitOne.address}")`); - }); - - it("Reverts when attempting to update a channel for an unowned conduit", async () => { - await expect( - conduitController - .connect(buyer) - .updateChannel(conduitOne.address, buyer.address, true) - ).to.be.revertedWith(`CallerIsNotOwner("${conduitOne.address}")`); - }); - - it("Retrieves no initial potential owner for new conduit", async () => { - const potentialOwner = await conduitController.getPotentialOwner( - conduitOne.address - ); - expect(potentialOwner).to.equal(constants.AddressZero); - - await expect( - conduitController.connect(owner).getPotentialOwner(buyer.address) - ).to.be.revertedWith("NoConduit"); - }); - - it("Lets the owner transfer ownership via a two-stage process", async () => { - await expect( - conduitController - .connect(buyer) - .transferOwnership(conduitOne.address, buyer.address) - ).to.be.revertedWith("CallerIsNotOwner", conduitOne.address); - - await expect( - conduitController - .connect(owner) - .transferOwnership(conduitOne.address, constants.AddressZero) - ).to.be.revertedWith( - "NewPotentialOwnerIsZeroAddress", - conduitOne.address - ); - - await expect( - conduitController - .connect(owner) - .transferOwnership(seller.address, buyer.address) - ).to.be.revertedWith("NoConduit"); - - let potentialOwner = await conduitController.getPotentialOwner( - conduitOne.address - ); - expect(potentialOwner).to.equal(constants.AddressZero); - - await conduitController.transferOwnership( - conduitOne.address, - buyer.address - ); - - potentialOwner = await conduitController.getPotentialOwner( - conduitOne.address - ); - expect(potentialOwner).to.equal(buyer.address); - - await expect( - conduitController - .connect(owner) - .transferOwnership(conduitOne.address, buyer.address) - ).to.be.revertedWith( - "NewPotentialOwnerAlreadySet", - conduitOne.address, - buyer.address - ); - - await expect( - conduitController - .connect(buyer) - .cancelOwnershipTransfer(conduitOne.address) - ).to.be.revertedWith("CallerIsNotOwner", conduitOne.address); - - await expect( - conduitController.connect(owner).cancelOwnershipTransfer(seller.address) - ).to.be.revertedWith("NoConduit"); - - await conduitController.cancelOwnershipTransfer(conduitOne.address); - - potentialOwner = await conduitController.getPotentialOwner( - conduitOne.address - ); - expect(potentialOwner).to.equal(constants.AddressZero); - - await expect( - conduitController - .connect(owner) - .cancelOwnershipTransfer(conduitOne.address) - ).to.be.revertedWith("NoPotentialOwnerCurrentlySet", conduitOne.address); - - await conduitController.transferOwnership( - conduitOne.address, - buyer.address - ); - - potentialOwner = await conduitController.getPotentialOwner( - conduitOne.address - ); - expect(potentialOwner).to.equal(buyer.address); - - await expect( - conduitController.connect(buyer).acceptOwnership(seller.address) - ).to.be.revertedWith("NoConduit"); - - await expect( - conduitController.connect(seller).acceptOwnership(conduitOne.address) - ).to.be.revertedWith("CallerIsNotNewPotentialOwner", conduitOne.address); - - await conduitController - .connect(buyer) - .acceptOwnership(conduitOne.address); - - potentialOwner = await conduitController.getPotentialOwner( - conduitOne.address - ); - expect(potentialOwner).to.equal(constants.AddressZero); - - const ownerOf = await conduitController.ownerOf(conduitOne.address); - expect(ownerOf).to.equal(buyer.address); - }); - }); - - describe("TransferHelper tests", async () => { - let sender; - let recipient; - let senderContract; - let recipientContract; - let tempTransferHelper; - let tempConduit; - let tempConduitKey; - - beforeEach(async () => { - // Setup basic buyer/seller wallets with ETH - sender = new ethers.Wallet(randomHex(32), provider); - recipient = new ethers.Wallet(randomHex(32), provider); - zone = new ethers.Wallet(randomHex(32), provider); - - senderContract = await EIP1271WalletFactory.deploy(sender.address); - recipientContract = await EIP1271WalletFactory.deploy(recipient.address); - - tempConduitKey = owner.address + randomHex(12).slice(2); - tempConduit = await deployNewConduit(owner, tempConduitKey); - - await Promise.all( - [sender, recipient, zone, senderContract, recipientContract].map( - (wallet) => faucet(wallet.address, provider) - ) - ); - - // Deploy a new TransferHelper with the tempConduitController address - const transferHelperFactory = await ethers.getContractFactory( - "TransferHelper" - ); - tempTransferHelper = await transferHelperFactory.deploy( - conduitController.address - ); - - await whileImpersonating(owner.address, provider, async () => { - await conduitController - .connect(owner) - .updateChannel(tempConduit.address, tempTransferHelper.address, true); - }); - }); - - it("Executes transfers (many token types) with a conduit", async () => { - // Get 3 Numbers that's value adds to Item Amount and minimum 1. - const itemsToCreate = 10; - const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); - const numEC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); - const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numEC721s); - - const erc20Contracts = [numERC20s]; - const erc20Transfers = [numERC20s]; - - const erc721Contracts = [numEC721s]; - const erc721Transfers = [numEC721s]; - - const erc1155Contracts = [numERC1155s]; - const erc1155Transfers = [numERC1155s]; - - // Create numERC20s amount of ERC20 objects - for (let i = 0; i < numERC20s; i++) { - // Deploy Contract - const { testERC20: tempERC20Contract } = await fixtureERC20(owner); - // Create/Approve X amount of ERC20s - const erc20Transfer = await createTransferWithApproval( - tempERC20Contract, - sender, - 1, - tempConduit.address - ); - erc20Contracts[i] = tempERC20Contract; - erc20Transfers[i] = erc20Transfer; - } - - // Create numEC721s amount of ERC20 objects - for (let i = 0; i < numEC721s; i++) { - // Deploy Contract - const { testERC721: tempERC721Contract } = await fixtureERC721(owner); - // Create/Approve numEC721s amount of ERC721s - const erc721Transfer = await createTransferWithApproval( - tempERC721Contract, - sender, - 2, - tempConduit.address - ); - erc721Contracts[i] = tempERC721Contract; - erc721Transfers[i] = erc721Transfer; - } - - // Create numERC1155s amount of ERC1155 objects - for (let i = 0; i < numERC1155s; i++) { - // Deploy Contract - const { testERC1155: tempERC1155Contract } = await fixtureERC1155( - owner - ); - // Create/Approve numERC1155s amount of ERC1155s - const erc1155Transfer = await createTransferWithApproval( - tempERC1155Contract, - sender, - 3, - tempConduit.address - ); - erc1155Contracts[i] = tempERC1155Contract; - erc1155Transfers[i] = erc1155Transfer; - } - - const transfers = erc20Transfers.concat( - erc721Transfers, - erc1155Transfers - ); - const contracts = erc20Contracts.concat( - erc721Contracts, - erc1155Contracts - ); - // Send the bulk transfers - await tempTransferHelper - .connect(sender) - .bulkTransfer(transfers, recipient.address, tempConduitKey); - // Loop through all transfer to do ownership/balance checks - for (let i = 0; i < transfers.length; i++) { - // Get Itemtype, token, amount, identifier - const { itemType, amount, identifier } = transfers[i]; - const token = contracts[i]; - - switch (itemType) { - case 1: // ERC20 - // Check balance - expect(await token.balanceOf(sender.address)).to.equal(0); - expect(await token.balanceOf(recipient.address)).to.equal(amount); - break; - case 2: // ERC721 - case 4: // ERC721_WITH_CRITERIA - expect(await token.ownerOf(identifier)).to.equal(recipient.address); - break; - case 3: // ERC1155 - case 5: // ERC1155_WITH_CRITERIA - // Check balance - expect(await token.balanceOf(sender.address, identifier)).to.equal( - 0 - ); - expect( - await token.balanceOf(recipient.address, identifier) - ).to.equal(amount); - break; - } - } - }); - - it("Executes transfers (many token types) without a conduit", async () => { - // Get 3 Numbers that's value adds to Item Amount and minimum 1. - const itemsToCreate = 10; - const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); - const numEC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); - const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numEC721s); - - const erc20Contracts = [numERC20s]; - const erc20Transfers = [numERC20s]; - - const erc721Contracts = [numEC721s]; - const erc721Transfers = [numEC721s]; - - const erc1155Contracts = [numERC1155s]; - const erc1155Transfers = [numERC1155s]; - - // Create numERC20s amount of ERC20 objects - for (let i = 0; i < numERC20s; i++) { - // Deploy Contract - const { testERC20: tempERC20Contract } = await fixtureERC20(owner); - // Create/Approve X amount of ERC20s - const erc20Transfer = await createTransferWithApproval( - tempERC20Contract, - sender, - 1, - tempTransferHelper.address - ); - erc20Contracts[i] = tempERC20Contract; - erc20Transfers[i] = erc20Transfer; - } - - // Create numEC721s amount of ERC20 objects - for (let i = 0; i < numEC721s; i++) { - // Deploy Contract - const { testERC721: tempERC721Contract } = await fixtureERC721(owner); - // Create/Approve numEC721s amount of ERC721s - const erc721Transfer = await createTransferWithApproval( - tempERC721Contract, - sender, - 2, - tempTransferHelper.address - ); - erc721Contracts[i] = tempERC721Contract; - erc721Transfers[i] = erc721Transfer; - } - - // Create numERC1155s amount of ERC1155 objects - for (let i = 0; i < numERC1155s; i++) { - // Deploy Contract - const { testERC1155: tempERC1155Contract } = await fixtureERC1155( - owner - ); - // Create/Approve numERC1155s amount of ERC1155s - const erc1155Transfer = await createTransferWithApproval( - tempERC1155Contract, - sender, - 3, - tempTransferHelper.address - ); - erc1155Contracts[i] = tempERC1155Contract; - erc1155Transfers[i] = erc1155Transfer; - } - - const transfers = erc20Transfers.concat( - erc721Transfers, - erc1155Transfers - ); - const contracts = erc20Contracts.concat( - erc721Contracts, - erc1155Contracts - ); - // Send the bulk transfers - await tempTransferHelper - .connect(sender) - .bulkTransfer( - transfers, - recipient.address, - ethers.utils.formatBytes32String("") - ); - // Loop through all transfer to do ownership/balance checks - for (let i = 0; i < transfers.length; i++) { - // Get Itemtype, token, amount, identifier - const { itemType, amount, identifier } = transfers[i]; - const token = contracts[i]; - - switch (itemType) { - case 1: // ERC20 - // Check balance - expect(await token.balanceOf(sender.address)).to.equal(0); - expect(await token.balanceOf(recipient.address)).to.equal(amount); - break; - case 2: // ERC721 - case 4: // ERC721_WITH_CRITERIA - expect(await token.ownerOf(identifier)).to.equal(recipient.address); - break; - case 3: // ERC1155 - case 5: // ERC1155_WITH_CRITERIA - // Check balance - expect(await token.balanceOf(sender.address, identifier)).to.equal( - 0 - ); - expect( - await token.balanceOf(recipient.address, identifier) - ).to.equal(amount); - break; - } - } - }); - - it("Reverts on native token transfers", async () => { - const ethTransferHelperItems = [ - { - itemType: 0, - token: ethers.constants.AddressZero, - identifier: 0, - amount: 10, - }, - { - itemType: 0, - token: ethers.constants.AddressZero, - identifier: 0, - amount: 20, - }, - ]; - await expect( - tempTransferHelper - .connect(sender) - .bulkTransfer( - ethTransferHelperItems, - recipient.address, - ethers.utils.formatBytes32String("") - ) - ).to.be.revertedWith("InvalidItemType"); - }); - }); - - describe("Reverts", async () => { - let seller; - let buyer; - let sellerContract; - let buyerContract; - - beforeEach(async () => { - // Setup basic buyer/seller wallets with ETH - seller = new ethers.Wallet(randomHex(32), provider); - buyer = new ethers.Wallet(randomHex(32), provider); - zone = new ethers.Wallet(randomHex(32), provider); - - sellerContract = await EIP1271WalletFactory.deploy(seller.address); - buyerContract = await EIP1271WalletFactory.deploy(buyer.address); - - await Promise.all( - [seller, buyer, zone, sellerContract, buyerContract].map((wallet) => - faucet(wallet.address, provider) - ) - ); - }); - - describe("Misconfigured orders", async () => { - it("Reverts on bad fraction amounts", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 0; - order.denominator = 10; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("BadFraction"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 0; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("BadFraction"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 2; - order.denominator = 1; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("BadFraction"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 2; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 2) - ); - }); - it("Reverts on inexact fraction amounts", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 8191; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("InexactFraction"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 2; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 2) - ); - }); - it("Reverts on partial fill attempt when not supported by order", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 2; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("PartialFillsNotEnabledForOrder"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 1; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Reverts on partially filled order via basic fulfillment", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 2; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 2) - ); - - const basicOrderParameters = getBasicOrderParameters( - 1, // EthForERC1155 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith(`OrderPartiallyFilled("${orderHash}")`); - }); - it("Reverts on fully filled order", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - order.numerator = 1; - order.denominator = 1; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`OrderAlreadyFilled("${orderHash}")`); - }); - it("Reverts on non-zero unused item parameters (identifier set on native, basic)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - consideration[0].identifierOrCriteria = amount; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 1, // EthForERC1155 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith(`UnusedItemParameters`); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - }); - it("Reverts on non-zero unused item parameters (identifier set on ERC20, basic)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - consideration[0].identifierOrCriteria = amount; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 3, // ERC20ForERC1155 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith(`UnusedItemParameters`); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - }); - it("Reverts on non-zero unused item parameters (token set on native, standard)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - consideration[0].token = seller.address; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`UnusedItemParameters`); - }); - it("Reverts on non-zero unused item parameters (identifier set on native, standard)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - consideration[0].identifierOrCriteria = amount; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`UnusedItemParameters`); - }); - it("Reverts on non-zero unused item parameters (identifier set on ERC20, standard)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - consideration[0].identifierOrCriteria = amount; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`UnusedItemParameters`); - }); - it("Reverts on inadequate consideration items", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 1 // PARTIAL_OPEN - ); - - // Remove a consideration item, but do not reduce - // totalOriginalConsiderationItems as MissingOriginalConsiderationItems - // is being tested for - order.parameters.consideration.pop(); - - const orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("MissingOriginalConsiderationItems"); - }); - it("Reverts on invalid submitter when required by order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 2 // FULL_RESTRICTED - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - zone, - value - ); - - expect(executions.length).to.equal(4); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith(`InvalidRestrictedOrder("${orderHash}")`); - } else { - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.reverted; - } - - const tx = marketplaceContract - .connect(zone) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("Reverts on invalid signatures", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const originalSignature = order.signature; - - // set an invalid V value - order.signature = order.signature.slice(0, -2) + "01"; - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - let expectedRevertReason = - getCustomRevertSelector("BadSignatureV(uint8)") + - "1".padStart(64, "0"); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillBasicOrder(basicOrderParameters, { - value, - }); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.reverted; - - // construct an invalid signature - basicOrderParameters.signature = "0x".padEnd(130, "f") + "1c"; - - expectedRevertReason = getCustomRevertSelector("InvalidSigner()"); - - tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillBasicOrder(basicOrderParameters, { - value, - }); - expect(provider.call(tx)).to.be.revertedWith("InvalidSigner"); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.reverted; - - basicOrderParameters.signature = originalSignature; - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Reverts on invalid 1271 signature", async () => { - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(sellerContract.address, marketplaceContract.address, true); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves marketplace contract to transfer tokens - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(40, 40, zone.address), - getTestItem20(40, 40, owner.address), - ]; - - const { order } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - zone // wrong signer - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.revertedWith("BAD SIGNER"); - }); - it("Reverts on invalid contract 1271 signature and contract does not supply a revert reason", async () => { - await sellerContract.connect(owner).revertWithMessage(false); - - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(sellerContract.address, marketplaceContract.address, true); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves marketplace contract to transfer tokens - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - zone // wrong signer - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.revertedWith("BadContractSignature"); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.reverted; - } - }); - it("Reverts on invalid contract 1271 signature and contract does not return magic value", async () => { - await sellerContract.connect(owner).setValid(false); - - // Seller mints nft to contract - const nftId = await mint721(sellerContract); - - // Seller approves marketplace contract to transfer NFT - await expect( - sellerContract - .connect(seller) - .approveNFT(testERC721.address, marketplaceContract.address) - ) - .to.emit(testERC721, "ApprovalForAll") - .withArgs(sellerContract.address, marketplaceContract.address, true); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves marketplace contract to transfer tokens - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getTestItem20( - tokenAmount.sub(100), - tokenAmount.sub(100), - sellerContract.address - ), - getTestItem20(50, 50, zone.address), - getTestItem20(50, 50, owner.address), - ]; - - const { order } = await createOrder( - sellerContract, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20ForERC721 - order - ); - - if (!process.env.REFERENCE) { - const expectedRevertReason = getCustomRevertSelector( - "BadContractSignature()" - ); - - let tx = await marketplaceContract - .connect(buyer) - .populateTransaction.fulfillBasicOrder(basicOrderParameters); - let returnData = await provider.call(tx); - expect(returnData).to.equal(expectedRevertReason); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.reverted; - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters) - ).to.be.reverted; - } - - await sellerContract.connect(owner).setValid(true); - }); - it("Reverts on restricted order where isValidOrder reverts with no data", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 2, // FULL_RESTRICTED, - [], - null, - seller, - "0x".padEnd(65, "0") + "2" - ); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith(`InvalidRestrictedOrder("${orderHash}")`); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } - - order.extraData = "0x0102030405"; - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`InvalidRestrictedOrder("${orderHash}")`); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; - } - }); - it("Reverts on restricted order where isValidOrder returns non-magic value", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - stubZone, - offer, - consideration, - 2, // FULL_RESTRICTED, - [], - null, - seller, - "0x".padEnd(65, "0") + "3" - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith(`InvalidRestrictedOrder("${orderHash}")`); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.reverted; - } - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith(`InvalidRestrictedOrder("${orderHash}")`); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } - - order.extraData = "0x01"; - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`InvalidRestrictedOrder("${orderHash}")`); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; - } - }); - it("Reverts on missing offer or consideration components", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - let fulfillments = [ - { - offerComponents: [], - considerationComponents: [], - }, - ]; - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { value }) - ).to.be.revertedWith("OfferAndConsiderationRequiredOnFulfillment"); - - fulfillments = [ - { - offerComponents: [], - considerationComponents: [ - { - orderIndex: 0, - itemIndex: 0, - }, - ], - }, - ]; - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { value }) - ).to.be.revertedWith("OfferAndConsiderationRequiredOnFulfillment"); - - fulfillments = [ - { - offerComponents: [ - { - orderIndex: 0, - itemIndex: 0, - }, - ], - considerationComponents: [], - }, - ]; - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("OfferAndConsiderationRequiredOnFulfillment"); - - fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("Reverts on mismatched offer and consideration components", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - let fulfillments = [toFulfillment([[0, 0]], [[0, 0]])]; - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith( - "MismatchedFulfillmentOfferAndConsiderationComponents" - ); - - fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("Reverts on mismatched offer components", async () => { - // Seller mints nft - const nftId = await mint721(seller); - - const secondNFTId = await mint721(seller); - - // Seller approves marketplace contract to transfer NFT - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - { - itemType: 2, // ERC721 - token: testERC721.address, - identifierOrCriteria: nftId, - startAmount: toBN(1), - endAmount: toBN(1), - }, - { - itemType: 2, // ERC721 - token: testERC721.address, - identifierOrCriteria: secondNFTId, - startAmount: toBN(1), - endAmount: toBN(1), - }, - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [ - [ - [0, 0], - [0, 1], - ], - [[1, 0]], - ], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on mismatched consideration components", async () => { - // Seller mints nft - const nftId = await mint721(seller); - - const secondNFTId = await mint721(seller); - - // Seller approves marketplace contract to transfer NFT - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - { - itemType: 2, // ERC721 - token: testERC721.address, - identifierOrCriteria: nftId, - startAmount: toBN(1), - endAmount: toBN(1), - }, - { - itemType: 2, // ERC721 - token: testERC721.address, - identifierOrCriteria: secondNFTId, - startAmount: toBN(1), - endAmount: toBN(1), - }, - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getTestItem20(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [ - [[0, 0]], - [ - [1, 0], - [1, 1], - ], - ], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillment component with out-of-range order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [ - [[2, 0]], - [ - [1, 0], - [1, 1], - ], - ], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillment component with out-of-range offer item", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 5]], [[1, 0]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillment component with out-of-range initial order on fulfillAvailableOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [ - getTestItem1155(nftId, amount.div(2), amount.div(2)), - getTestItem1155(nftId, amount.div(2), amount.div(2)), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [5, 0], - [0, 0], - ], - ]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [order], - offerComponents, - considerationComponents, - toKey(false), - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillment component with out-of-range initial offer item on fulfillAvailableOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [ - getTestItem1155(nftId, amount.div(2), amount.div(2)), - getTestItem1155(nftId, amount.div(2), amount.div(2)), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [0, 5], - [0, 0], - ], - ]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - let success = false; - - try { - const tx = await marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [order], - offerComponents, - considerationComponents, - toKey(false), - 100, - { - value, - } - ); - - const receipt = await tx.wait(); - success = receipt.status; - } catch (err) {} - - expect(success).to.be.false; // TODO: fix out-of-gas - }); - it("Reverts on fulfillment component with out-of-range subsequent offer item on fulfillAvailableOrders", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [ - getTestItem1155(nftId, amount.div(2), amount.div(2)), - getTestItem1155(nftId, amount.div(2), amount.div(2)), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [0, 0], - [0, 5], - ], - ]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [order], - offerComponents, - considerationComponents, - toKey(false), - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillment component with out-of-range consideration item", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 5]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on unmet consideration items", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith( - `ConsiderationNotMet(0, 2, ${parseEther("1").toString()}` - ); - }); - it("Reverts on fulfillAvailableAdvancedOrders with empty fulfillment component", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [[]]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ) - ).to.be.revertedWith("MissingFulfillmentComponentOnAggregation(0)"); - }); - it("Reverts on fulfillAvailableAdvancedOrders with out-of-range initial offer order", async () => { - // Seller mints nft - const { nftId, amount } = await mint1155(seller, 2); - - // Seller approves marketplace contract to transfer NFT - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155(nftId, amount, amount, undefined), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [2, 0], - [0, 0], - ], - ]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillAvailableAdvancedOrders with out-of-range offer order", async () => { - // Seller mints nft - const { nftId, amount } = await mint1155(seller, 2); - - // Seller approves marketplace contract to transfer NFT - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155(nftId, amount, amount, undefined), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [0, 0], - [2, 0], - ], - ]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillAvailableAdvancedOrders with mismatched offer components", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId), getTestItem20(1, 1)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [ - [ - [0, 0], - [0, 1], - ], - ]; - - const considerationComponents = [[[0, 0]], [[0, 1]], [[0, 2]]]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillAvailableAdvancedOrders with out-of-range consideration order", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [[[0, 0]]]; - - const considerationComponents = [ - [ - [0, 0], - [2, 1], - ], - [[2, 2]], - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillAvailableAdvancedOrders with mismatched consideration components", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - { - itemType: 2, // ERC721 - token: testERC721.address, - identifierOrCriteria: nftId, - startAmount: toBN(1), - endAmount: toBN(1), - recipient: zone.address, - }, - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const offerComponents = [[[0, 0]]]; - - const considerationComponents = [ - [ - [0, 0], - [0, 1], - ], - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value, - } - ) - ).to.be.revertedWith("InvalidFulfillmentComponentData"); - }); - it("Reverts on fulfillAvailableAdvancedOrders no available components", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - // first order is expired - const { order: orderOne, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "EXPIRED" - ); - - // second order will be cancelled - const { - order: orderTwo, - orderHash: orderHashTwo, - orderComponents, - } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // can cancel it - await expect( - marketplaceContract.connect(seller).cancel([orderComponents]) - ) - .to.emit(marketplaceContract, "OrderCancelled") - .withArgs(orderHashTwo, seller.address, zone.address); - - // third order will be filled - const { order: orderThree, orderHash: orderHashThree } = - await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // can fill it - await withBalanceChecks([orderThree], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillOrder(orderThree, toKey(false), { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order: orderThree, - orderHash: orderHashThree, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - - const offerComponents = [ - [ - [0, 0], - [1, 0], - [2, 0], - ], - ]; - - const considerationComponents = [ - [ - [0, 0], - [1, 0], - [2, 0], - ], - [ - [0, 1], - [1, 1], - [2, 1], - ], - [ - [0, 2], - [1, 2], - [2, 2], - ], - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [orderOne, orderTwo, orderThree], - [], - offerComponents, - considerationComponents, - toKey(false), - constants.AddressZero, - 100, - { - value: value.mul(3), - } - ) - ).to.be.revertedWith("NoSpecifiedOrdersAvailable"); - }); - it("Reverts on out-of-range criteria resolvers", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - await testERC721.mint(seller.address, thirdNFTId); - - const tokenIds = [nftId, secondNFTId, thirdNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - let criteriaResolvers = [ - buildResolver(3, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("OrderCriteriaResolverOutOfRange"); - - criteriaResolvers = [ - buildResolver(0, 0, 5, nftId, proofs[nftId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("OfferCriteriaResolverOutOfRange"); - - criteriaResolvers = [ - buildResolver(0, 1, 5, nftId, proofs[nftId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("ConsiderationCriteriaResolverOutOfRange"); - - criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - await withBalanceChecks([order], 0, criteriaResolvers, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - }); - }); - if (process.env.REFERENCE) { - it("Reverts on out-of-range criteria resolver (match)", async () => { - // Seller mints nfts - const nftId = await mint721(seller); - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree([nftId]); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - let criteriaResolvers = [ - buildResolver(3, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder } = await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = [toFulfillment([[1, 0]], [[0, 0]])]; - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ) - ).to.be.revertedWith("OrderCriteriaResolverOutOfRange"); - - criteriaResolvers = [ - buildResolver(0, 0, 5, nftId, proofs[nftId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ) - ).to.be.revertedWith("OfferCriteriaResolverOutOfRange"); - - criteriaResolvers = [ - buildResolver(0, 1, 5, nftId, proofs[nftId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ) - ).to.be.revertedWith("ConsiderationCriteriaResolverOutOfRange"); - }); - } - it("Reverts on unresolved criteria items", async () => { - // Seller and buyer both mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(buyer.address, secondNFTId); - - const tokenIds = [nftId, secondNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - // Buyer approves marketplace contract to transfer NFTs - await set721ApprovalForAll(buyer, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getTestItem721WithCriteria(root, toBN(1), toBN(1), owner.address), - ]; - - let criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("UnresolvedConsiderationCriteria"); - - criteriaResolvers = [ - buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("UnresolvedOfferCriteria"); - - criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), - ]; - - await withBalanceChecks([order], 0, criteriaResolvers, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - }); - }); - if (process.env.REFERENCE) { - it("Reverts on unresolved criteria items (match)", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - - const tokenIds = [nftId, secondNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getTestItem721WithCriteria(root, toBN(1), toBN(1), owner.address), - ]; - - let criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { mirrorOrder } = await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = [toFulfillment([[1, 0]], [[0, 0]])]; - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ) - ).to.be.revertedWith("UnresolvedConsiderationCriteria"); - - criteriaResolvers = [ - buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), - ]; - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ) - ).to.be.revertedWith("UnresolvedOfferCriteria"); - - criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), - ]; - }); - } - it("Reverts on attempts to resolve criteria for non-criteria item", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - await testERC721.mint(seller.address, thirdNFTId); - - const tokenIds = [nftId, secondNFTId, thirdNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { proofs } = merkleTree(tokenIds); - - const offer = [ - getTestItem721( - nftId, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("CriteriaNotEnabledForItem"); - }); - if (process.env.REFERENCE) { - it("Reverts on attempts to resolve criteria for non-criteria item (match)", async () => { - // Seller mints nfts - const nftId = await mint721(seller); - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree([nftId]); - - const offer = [ - getTestItem721( - root, - toBN(1), - toBN(1), - undefined, - testERC721.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - const { mirrorOrder } = await createMirrorAcceptOfferOrder( - buyer, - zone, - order, - criteriaResolvers - ); - - const fulfillments = [toFulfillment([[1, 0]], [[0, 0]])]; - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders( - [order, mirrorOrder], - criteriaResolvers, - fulfillments, - { - value, - } - ) - ).to.be.revertedWith("CriteriaNotEnabledForItem"); - }); - } - it("Reverts on offer amount overflow", async () => { - const { testERC20: testERC20Two } = await fixtureERC20(owner); - // Buyer mints nfts - const nftId = await mintAndApprove721( - buyer, - marketplaceContract.address - ); - - await testERC20Two.mint(seller.address, constants.MaxUint256); - // Seller approves marketplace contract to transfer NFTs - await testERC20Two - .connect(seller) - .approve(marketplaceContract.address, constants.MaxUint256); - - const offer = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - undefined, - testERC20Two.address - ), - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - undefined, - testERC20Two.address - ), - ]; - - const consideration = [getTestItem721(nftId, 1, 1, seller.address)]; - - const offer2 = [getTestItem721(nftId, 1, 1)]; - const consideration2 = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - buyer.address, - testERC20Two.address - ), - ]; - - const fulfillments = [ - toFulfillment( - [ - [0, 0], - [0, 1], - ], - [[1, 0]] - ), - toFulfillment([[1, 0]], [[0, 0]]), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 1 - ); - - const { order: order2 } = await createOrder( - buyer, - zone, - offer2, - consideration2, - 1 - ); - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders([order, order2], [], fulfillments) - ).to.be.revertedWith( - "panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)" - ); - }); - - it("Reverts on offer amount overflow when another amount is 0", async () => { - const { testERC20: testERC20Two } = await fixtureERC20(owner); - // Buyer mints nfts - const nftId = await mintAndApprove721( - buyer, - marketplaceContract.address - ); - - await testERC20Two.mint(seller.address, constants.MaxUint256); - // Seller approves marketplace contract to transfer NFTs - await testERC20Two - .connect(seller) - .approve(marketplaceContract.address, constants.MaxUint256); - - const offer = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - undefined, - testERC20Two.address - ), - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - undefined, - testERC20Two.address - ), - getTestItem20(0, 0, undefined, testERC20Two.address), - ]; - - const consideration = [getTestItem721(nftId, 1, 1, seller.address)]; - - const offer2 = [getTestItem721(nftId, 1, 1)]; - const consideration2 = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - buyer.address, - testERC20Two.address - ), - ]; - - const fulfillments = [ - toFulfillment( - [ - [0, 0], - [0, 1], - [0, 2], - ], - [[1, 0]] - ), - toFulfillment([[1, 0]], [[0, 0]]), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 1 - ); - - const { order: order2 } = await createOrder( - buyer, - zone, - offer2, - consideration2, - 1 - ); - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders([order, order2], [], fulfillments) - ).to.be.revertedWith( - "panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)" - ); - }); - - it("Reverts on consideration amount overflow", async () => { - const { testERC20: testERC20Two } = await fixtureERC20(owner); - // Buyer mints nfts - const nftId = await mintAndApprove721( - buyer, - marketplaceContract.address - ); - - await testERC20Two.mint(seller.address, constants.MaxUint256); - // Seller approves marketplace contract to transfer NFTs - await testERC20Two - .connect(seller) - .approve(marketplaceContract.address, constants.MaxUint256); - - const offer = [getTestItem721(nftId, 1, 1)]; - - const consideration = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - seller.address, - testERC20Two.address - ), - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - seller.address, - testERC20Two.address - ), - ]; - - const offer2 = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - undefined, - testERC20Two.address - ), - ]; - const consideration2 = [getTestItem721(nftId, 1, 1, buyer.address)]; - - const fulfillments = [ - toFulfillment( - [[1, 0]], - [ - [0, 0], - [0, 1], - ] - ), - toFulfillment([[0, 0]], [[1, 0]]), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 1 - ); - - const { order: order2 } = await createOrder( - buyer, - zone, - offer2, - consideration2, - 1 - ); - - await expect( - marketplaceContract - .connect(owner) - .matchAdvancedOrders([order, order2], [], fulfillments) - ).to.be.revertedWith( - "panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)" - ); - }); - - it("Reverts on consideration amount overflow when another amount is 0", async () => { - const { testERC20: testERC20Two } = await fixtureERC20(owner); - // Buyer mints nfts - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - await testERC20Two.mint(buyer.address, constants.MaxUint256); - // Seller approves marketplace contract to transfer NFTs - await testERC20Two - .connect(buyer) - .approve(marketplaceContract.address, constants.MaxUint256); - - const offer = [getTestItem721(nftId, 1, 1)]; - - const consideration = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - seller.address, - testERC20Two.address - ), - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - seller.address, - testERC20Two.address - ), - getTestItem20(0, 0, seller.address, testERC20Two.address), - ]; - - const offer2 = [ - getTestItem20( - constants.MaxUint256, - constants.MaxUint256, - undefined, - testERC20Two.address - ), - ]; - const consideration2 = [getTestItem721(nftId, 1, 1, buyer.address)]; - - const fulfillments = [ - toFulfillment( - [[1, 0]], - [ - [0, 0], - [0, 1], - [0, 2], - ] - ), - toFulfillment([[0, 0]], [[1, 0]]), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 1 - ); - - const { order: order2 } = await createOrder( - buyer, - zone, - offer2, - consideration2, - 1 - ); - - await expect( - marketplaceContract.matchAdvancedOrders( - [order, order2], - [], - fulfillments - ) - ).to.be.revertedWith( - "panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)" - ); - }); - - it("Reverts on invalid criteria proof", async () => { - // Seller mints nfts - const nftId = randomBN(); - const secondNFTId = randomBN(); - const thirdNFTId = randomBN(); - - await testERC721.mint(seller.address, nftId); - await testERC721.mint(seller.address, secondNFTId); - await testERC721.mint(seller.address, thirdNFTId); - - const tokenIds = [nftId, secondNFTId, thirdNFTId]; - - // Seller approves marketplace contract to transfer NFTs - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - const { root, proofs } = merkleTree(tokenIds); - - const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const criteriaResolvers = [ - buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - criteriaResolvers - ); - - criteriaResolvers[0].identifier = - criteriaResolvers[0].identifier.add(1); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("InvalidProof"); - - criteriaResolvers[0].identifier = - criteriaResolvers[0].identifier.sub(1); - - await withBalanceChecks([order], 0, criteriaResolvers, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - criteriaResolvers, - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - criteriaResolvers - ); - - return receipt; - }); - }); - it("Reverts on attempts to transfer >1 ERC721 in single transfer", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [ - getTestItem721( - nftId, - toBN(2), - toBN(2), - undefined, - testERC721.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith("InvalidERC721TransferAmount"); - }); - it("Reverts on attempts to transfer >1 ERC721 in single transfer (basic)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [ - getTestItem721( - nftId, - toBN(2), - toBN(2), - undefined, - testERC721.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith("InvalidERC721TransferAmount"); - }); - it("Reverts on attempts to transfer >1 ERC721 in single transfer via conduit", async () => { - const nftId = await mintAndApprove721(seller, conduitOne.address, true); - - const offer = [ - getTestItem721( - nftId, - toBN(2), - toBN(2), - undefined, - testERC721.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith("InvalidERC721TransferAmount"); - }); - }); - - describe("Out of timespan", async () => { - it("Reverts on orders that have not started (standard)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "NOT_STARTED" - ); - - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith("InvalidTime"); - }); - it("Reverts on orders that have expired (standard)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "EXPIRED" - ); - - await expect( - marketplaceContract.connect(buyer).fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith("InvalidTime"); - }); - it("Reverts on orders that have not started (basic)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "NOT_STARTED" - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith("InvalidTime"); - }); - it("Reverts on orders that have expired (basic)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "EXPIRED" - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith("InvalidTime"); - }); - it("Reverts on orders that have not started (match)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "NOT_STARTED" - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], defaultBuyNowMirrorFulfillment, { - value, - }) - ).to.be.revertedWith("InvalidTime"); - }); - it("Reverts on orders that have expired (match)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - "EXPIRED" - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], defaultBuyNowMirrorFulfillment, { - value, - }) - ).to.be.revertedWith("InvalidTime"); - }); - }); - - describe("Insufficient amounts and bad items", async () => { - it("Reverts when no ether is supplied (basic)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: toBN(0), - }) - ).to.be.revertedWith("InvalidMsgValue"); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Reverts when not enough ether is supplied (basic)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: toBN(1), - }) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: value.sub(1), - }) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - await withBalanceChecks([order], 0, null, async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents(tx, receipt, [ - { - order, - orderHash, - fulfiller: buyer.address, - }, - ]); - - return receipt; - }); - }); - it("Reverts when not enough ether is supplied as offer item (match)", async () => { - // NOTE: this is a ridiculous scenario, buyer is paying the seller's offer - const offer = [getItemETH(parseEther("10"), parseEther("10"))]; - - const consideration = [ - getItemETH(parseEther("1"), parseEther("1"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - await expect( - marketplaceContract - .connect(buyer) - .matchOrders([order, mirrorOrder], fulfillments, { - value: toBN(1), - }) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - await expect( - marketplaceContract - .connect(buyer) - .matchOrders([order, mirrorOrder], fulfillments, { - value: parseEther("9.999999"), - }) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - await marketplaceContract - .connect(buyer) - .matchOrders([order, mirrorOrder], fulfillments, { - value: parseEther("13"), - }); - }); - it("Reverts when not enough ether is supplied (standard + advanced)", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(amount.mul(1000), amount.mul(1000), seller.address), - getItemETH(amount.mul(10), amount.mul(10), zone.address), - getItemETH(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value: toBN(1), - } - ) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value: value.sub(1), - } - ) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // fulfill with a tiny bit extra to test for returning eth - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value: value.add(1), - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Reverts when not enough ether is supplied (match)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = defaultBuyNowMirrorFulfillment; - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(4); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value: toBN(1), - }) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value: value.sub(1), - }) - ).to.be.revertedWith("InsufficientEtherSupplied"); - - await whileImpersonating(owner.address, provider, async () => { - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - }); - it("Reverts when ether is supplied to a non-payable route (basic)", async () => { - // Seller mints nft - const nftId = randomBN(); - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH( - parseEther("1"), - parseEther("1"), - marketplaceContract.address - ), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 2, // ERC20_TO_ERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: 1, - }) - ).to.be.revertedWith("InvalidMsgValue(1)"); - }); - - it(`Reverts when ether transfer fails (returndata)${ - process.env.REFERENCE ? " — SKIPPED ON REFERENCE" : "" - }`, async () => { - if (process.env.REFERENCE) { - return; - } - - const recipient = await ( - await ethers.getContractFactory("ExcessReturnDataRecipient") - ).deploy(); - const setup = async () => { - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Seller approves marketplace contract to transfer NFT - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - // Buyer approves marketplace contract to transfer tokens - - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), recipient.address), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - return basicOrderParameters; - }; - let basicOrderParameters = await setup(); - const baseGas = await marketplaceContract - .connect(buyer) - .estimateGas.fulfillBasicOrder(basicOrderParameters, { - value: parseEther("12"), - }); - - // TODO: clean *this* up - basicOrderParameters = await setup(); - await recipient.setRevertDataSize(1); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: parseEther("12"), - gasLimit: hre.__SOLIDITY_COVERAGE_RUNNING - ? baseGas.add(35000) - : baseGas.add(1000), - }) - ).to.be.revertedWith("EtherTransferGenericFailure"); - }); - - it("Reverts when ether transfer fails (basic)", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Seller approves marketplace contract to transfer NFT - await set721ApprovalForAll(seller, marketplaceContract.address, true); - - // Buyer approves marketplace contract to transfer tokens - - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH( - parseEther("1"), - parseEther("1"), - marketplaceContract.address - ), - ]; - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value: parseEther("12"), - }) - ).to.be.revertedWith( - `EtherTransferGenericFailure("${ - marketplaceContract.address - }", ${parseEther("1").toString()})` - ); - }); - it("Reverts when tokens are not approved", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; // panic code thrown by underlying 721 - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // Buyer approves marketplace contract to transfer tokens - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Reverts when 1155 token transfer reverts", async () => { - // Seller mints nft - const { nftId, amount } = await mint1155(seller, 10000); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("NOT_AUTHORIZED"); - }); - it("Reverts when 1155 token transfer reverts (via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mint1155(seller, 10000); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`NOT_AUTHORIZED`); - }); - - // Skip this test when testing the reference contract - if (!process.env.REFERENCE) { - it("Reverts when 1155 token transfer reverts (via conduit, returndata)", async () => { - const recipient = await ( - await ethers.getContractFactory("ExcessReturnDataRecipient") - ).deploy(); - - const setup = async () => { - // seller mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(seller.address, tokenAmount); - - // Seller approves conduit contract to transfer tokens - await expect( - testERC20.connect(seller).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(seller.address, conduitOne.address, tokenAmount); - - // Buyer mints nft - const nftId = randomBN(); - const amount = toBN(randomBN(2)); - await testERC1155.mint(buyer.address, nftId, amount.mul(10000)); - - // Buyer approves conduit contract to transfer NFTs - await expect( - testERC1155 - .connect(buyer) - .setApprovalForAll(conduitOne.address, true) - ) - .to.emit(testERC1155, "ApprovalForAll") - .withArgs(buyer.address, conduitOne.address, true); - - const offer = [getTestItem20(tokenAmount, tokenAmount)]; - - const consideration = [ - getTestItem1155( - nftId, - amount.mul(10), - amount.mul(10), - undefined, - recipient.address - ), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - return { - order, - value, - }; - }; - - const { order: initialOrder, value } = await setup(); - const baseGas = await marketplaceContract - .connect(buyer) - .estimateGas.fulfillAdvancedOrder( - initialOrder, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - } - ); - - // TODO: clean *this* up - const { order } = await setup(); - await recipient.setRevertDataSize(1); - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - gasLimit: baseGas.add(74000), - } - ) - ).to.be.revertedWith("InvalidCallToConduit"); - }); - } - - it("Reverts when transferred item amount is zero", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves marketplace contract to transfer tokens - - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem1155(nftId, 0, 0, undefined)]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith("MissingItemAmount"); - }); - it("Reverts when ERC20 tokens return falsey values", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves marketplace contract to transfer tokens - - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // block transfers - await testERC20.blockTransfer(true); - - expect(await testERC20.blocked()).to.be.true; - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; // TODO: hardhat can't find error msg on IR pipeline - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await testERC20.blockTransfer(false); - - expect(await testERC20.blocked()).to.be.false; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Works when ERC20 tokens return falsey values", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address, - 10000 - ); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves marketplace contract to transfer tokens - - await expect( - testERC20 - .connect(buyer) - .approve(marketplaceContract.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, marketplaceContract.address, tokenAmount); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await testERC20.setNoReturnData(true); - - expect(await testERC20.noReturnData()).to.be.true; - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: toKey(false), - }, - ], - null, - [] - ); - - return receipt; - }); - - const orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - - await testERC20.setNoReturnData(false); - - expect(await testERC20.noReturnData()).to.be.false; - }); - it("Reverts when ERC20 tokens return falsey values (via conduit)", async () => { - // Seller mints nft - const { nftId, amount } = await mint1155(seller, 10000); - - // Seller approves conduit contract to transfer NFTs - await set1155ApprovalForAll(seller, conduitOne.address, true); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves conduit contract to transfer tokens - - await expect( - testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, conduitOne.address, tokenAmount); - - // Seller approves conduit contract to transfer tokens - await expect( - testERC20.connect(seller).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(seller.address, conduitOne.address, tokenAmount); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - // block transfers - await testERC20.blockTransfer(true); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith( - `BadReturnValueFromERC20OnTransfer("${testERC20.address}", "${ - buyer.address - }", "${seller.address}", ${amount.mul(1000).toString()})` - ); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; - } - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await testERC20.blockTransfer(false); - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: conduitKeyOne, - }, - ], - null, - [] - ); - - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Reverts when providing non-existent conduit", async () => { - // Seller mints nft - const { nftId, amount } = await mint1155(seller, 10000); - - // Seller approves conduit contract to transfer NFTs - await set1155ApprovalForAll(seller, conduitOne.address, true); - - // Buyer mints ERC20 - const tokenAmount = minRandom(100); - await testERC20.mint(buyer.address, tokenAmount); - - // Buyer approves conduit contract to transfer tokens - await expect( - testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(buyer.address, conduitOne.address, tokenAmount); - - // Seller approves conduit contract to transfer tokens - await expect( - testERC20.connect(seller).approve(conduitOne.address, tokenAmount) - ) - .to.emit(testERC20, "Approval") - .withArgs(seller.address, conduitOne.address, tokenAmount); - - const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; - - const consideration = [ - getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), - getTestItem20(amount.mul(10), amount.mul(10), zone.address), - getTestItem20(amount.mul(20), amount.mul(20), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const badKey = constants.HashZero.slice(0, -1) + "2"; - - const missingConduit = await conduitController.getConduit(badKey); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], badKey, constants.AddressZero, { - value, - }) - ).to.be.revertedWith("InvalidConduit", badKey, missingConduit); - - let orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - await withBalanceChecks([order], 0, [], async () => { - const tx = marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - } - ); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: buyer.address, - fulfillerConduitKey: conduitKeyOne, - }, - ], - null, - null - ); - return receipt; - }); - - orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(true, false, 1, 1) - ); - }); - it("Reverts when 1155 tokens are not approved", async () => { - // Seller mints first nft - const { nftId } = await mint1155(seller); - - // Seller mints second nft - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - seller - ); - - const offer = [ - getTestItem1155(nftId, 0, 0), - getTestItem1155(secondNftId, secondAmount, secondAmount), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("MissingItemAmount"); - }); - it("Reverts when 1155 tokens are not approved", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - seller - ); - - const offer = [ - getTestItem1155(nftId, amount, amount, undefined), - getTestItem1155(secondNftId, secondAmount, secondAmount), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("NOT_AUTHORIZED"); - - const orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - - // Seller approves marketplace contract to transfer NFT - - await set1155ApprovalForAll(seller, marketplaceContract.address, true); - - const executions = await simulateMatchOrders( - [order, mirrorOrder], - fulfillments, - owner, - value - ); - - expect(executions.length).to.equal(5); - - const tx = marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }); - const receipt = await (await tx).wait(); - await checkExpectedEvents( - tx, - receipt, - [ - { - order, - orderHash, - fulfiller: constants.AddressZero, - }, - { - order: mirrorOrder, - orderHash: mirrorOrderHash, - fulfiller: constants.AddressZero, - }, - ], - executions - ); - return receipt; - }); - it("Reverts when token account with no code is supplied", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount, undefined)]; - - const consideration = [ - getTestItem20(amount, amount, seller.address, constants.AddressZero), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; // TODO: look into the revert reason more thoroughly - // Transaction reverted: function returned an unexpected amount of data - }); - it("Reverts when 721 account with no code is supplied", async () => { - const offer = [getTestItem721(0, 1, 1, undefined, buyer.address)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { value } - ) - ).to.be.revertedWith(`NoContract("${buyer.address}")`); - }); - it("Reverts when 1155 account with no code is supplied", async () => { - const amount = toBN(randomBN(2)); - - const offer = [ - getTestItem1155(0, amount, amount, constants.AddressZero), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`NoContract("${constants.AddressZero}")`); - }); - it("Reverts when 1155 account with no code is supplied (via conduit)", async () => { - const amount = toBN(randomBN(2)); - - const offer = [ - getTestItem1155(0, amount, amount, constants.AddressZero), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith(`NoContract("${constants.AddressZero}")`); - }); - it("Reverts when non-token account is supplied as the token", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount, undefined)]; - - const consideration = [ - getTestItem20( - amount, - amount, - seller.address, - marketplaceContract.address - ), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith( - `TokenTransferGenericFailure("${marketplaceContract.address}", "${ - buyer.address - }", "${seller.address}", 0, ${amount.toString()})` - ); - }); - it("Reverts when non-token account is supplied as the token fulfilled via conduit", async () => { - // Seller mints nft - const { nftId, amount } = await mintAndApprove1155( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem1155(nftId, amount, amount, undefined)]; - - const consideration = [ - getTestItem20( - amount, - amount, - seller.address, - marketplaceContract.address - ), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - conduitKeyOne, - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith( - `TokenTransferGenericFailure("${marketplaceContract.address}", "${ - buyer.address - }", "${seller.address}", 0, ${amount.toString()})` - ); - }); - it("Reverts when non-1155 account is supplied as the token", async () => { - const amount = toBN(randomBN(2)); - - const offer = [ - getTestItem1155(0, amount, amount, marketplaceContract.address), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.revertedWith( - `TokenTransferGenericFailure("${marketplaceContract.address}", "${ - seller.address - }", "${buyer.address}", 0, ${amount.toString()})` - ); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder( - order, - [], - toKey(false), - constants.AddressZero, - { - value, - } - ) - ).to.be.reverted; - } - }); - it("Reverts when 1155 token is not approved via conduit", async () => { - // Seller mints first nft - const { nftId, amount } = await mint1155(seller); - - // Seller mints second nft - const { nftId: secondNftId, amount: secondAmount } = await mint1155( - seller - ); - - const offer = [ - getTestItem1155(nftId, amount, amount, testERC1155.address), - getTestItem1155( - secondNftId, - secondAmount, - secondAmount, - testERC1155.address - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("NOT_AUTHORIZED"); - - const orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - }); - it("Reverts when 1155 token with no code is supplied as the token via conduit", async () => { - // Seller mints first nft - const nftId = toBN(randomBN(4)); - const amount = toBN(randomBN(4)); - - // Seller mints second nft - const secondNftId = toBN(randomBN(4)); - const secondAmount = toBN(randomBN(4)); - - const offer = [ - getTestItem1155(nftId, amount, amount, constants.AddressZero), - getTestItem1155( - secondNftId, - secondAmount, - secondAmount, - constants.AddressZero - ), - ]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), zone.address), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, orderHash, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0, // FULL_OPEN - [], - null, - seller, - constants.HashZero, - conduitKeyOne - ); - - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - - const fulfillments = [ - [[[0, 0]], [[1, 0]]], - [[[0, 1]], [[1, 1]]], - [[[1, 0]], [[0, 0]]], - [[[1, 0]], [[0, 1]]], - [[[1, 0]], [[0, 2]]], - ].map(([offerArr, considerationArr]) => - toFulfillment(offerArr, considerationArr) - ); - - await expect( - marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value, - }) - ).to.be.revertedWith("NoContract", constants.AddressZero); - - const orderStatus = await marketplaceContract.getOrderStatus(orderHash); - - expect({ ...orderStatus }).to.deep.equal( - buildOrderStatus(false, false, 0, 0) - ); - }); - it("Reverts when non-payable ether recipient is supplied", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH( - parseEther("1"), - parseEther("1"), - marketplaceContract.address - ), - getItemETH(parseEther("1"), parseEther("1"), owner.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillBasicOrder(basicOrderParameters, { - value, - }) - ).to.be.revertedWith( - `EtherTransferGenericFailure("${ - marketplaceContract.address - }", ${parseEther("1").toString()})` - ); - }); - }); - - describe("Basic Order Calldata", () => { - let calldata, value; - - before(async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - ]; - let order; - ({ order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - )); - - const basicOrderParameters = getBasicOrderParameters( - 0, // EthForERC721 - order - ); - - ({ data: calldata } = - await marketplaceContract.populateTransaction.fulfillBasicOrder( - basicOrderParameters - )); - }); - - it("Reverts if BasicOrderParameters has non-default offset", async () => { - const badData = [calldata.slice(0, 73), "1", calldata.slice(74)].join( - "" - ); - expect(badData.length).to.eq(calldata.length); - - await expect( - buyer.sendTransaction({ - to: marketplaceContract.address, - data: badData, - value, - }) - ).to.be.revertedWith("InvalidBasicOrderParameterEncoding"); - }); - - it("Reverts if additionalRecipients has non-default offset", async () => { - const badData = [ - calldata.slice(0, 1161), - "1", - calldata.slice(1162), - ].join(""); - - await expect( - buyer.sendTransaction({ - to: marketplaceContract.address, - data: badData, - value, - }) - ).to.be.revertedWith("InvalidBasicOrderParameterEncoding"); - }); - - it("Reverts if signature has non-default offset", async () => { - const badData = [ - calldata.slice(0, 1161), - "2", - calldata.slice(1162), - ].join(""); - - await expect( - buyer.sendTransaction({ - to: marketplaceContract.address, - data: badData, - value, - }) - ).to.be.revertedWith("InvalidBasicOrderParameterEncoding"); - }); - }); - - describe("Reentrancy", async () => { - it("Reverts on a reentrant call", async () => { - // Seller mints nft - const nftId = await mintAndApprove721( - seller, - marketplaceContract.address - ); - - const offer = [getTestItem721(nftId)]; - - const consideration = [ - getItemETH(parseEther("10"), parseEther("10"), seller.address), - getItemETH(parseEther("1"), parseEther("1"), reenterer.address), - ]; - - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - // prepare the reentrant call on the reenterer - const callData = marketplaceContract.interface.encodeFunctionData( - "fulfillOrder", - [order, toKey(false)] - ); - const tx = await reenterer.prepare( - marketplaceContract.address, - 0, - callData - ); - await tx.wait(); - - if (!process.env.REFERENCE) { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.revertedWith("NoReentrantCalls"); - } else { - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { - value, - }) - ).to.be.reverted; - } - }); - }); - - describe("ETH offer items", async () => { - let ethAmount; - const tokenAmount = minRandom(100); - let offer; - let consideration; - let seller; - let buyer; - - before(async () => { - ethAmount = parseEther("1"); - seller = await getWalletWithEther(); - buyer = await getWalletWithEther(); - zone = new ethers.Wallet(randomHex(32), provider); - offer = [getItemETH(ethAmount, ethAmount)]; - consideration = [ - getTestItem20(tokenAmount, tokenAmount, seller.address), - ]; - }); - - it("fulfillOrder reverts if any offer item is ETH", async () => { - const { order, value } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillOrder(order, toKey(false), { value }) - ).to.be.revertedWith("InvalidNativeOfferItem"); - }); - - it("fulfillAdvancedOrder reverts if any offer item is ETH", async () => { - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAdvancedOrder(order, [], toKey(false), buyer.address, { - value: ethAmount, - }) - ).to.be.revertedWith("InvalidNativeOfferItem"); - }); - - it("fulfillAvailableOrders reverts if any offer item is ETH", async () => { - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableOrders( - [order], - [[[0, 0]]], - [[[0, 0]]], - toKey(false), - 100, - { value: ethAmount } - ) - ).to.be.revertedWith("InvalidNativeOfferItem"); - }); - - it("fulfillAvailableAdvancedOrders reverts if any offer item is ETH", async () => { - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - - await expect( - marketplaceContract - .connect(buyer) - .fulfillAvailableAdvancedOrders( - [order], - [], - [[[0, 0]]], - [[[0, 0]]], - toKey(false), - buyer.address, - 100, - { value: ethAmount } - ) - ).to.be.revertedWith("InvalidNativeOfferItem"); - }); - - it("matchOrders allows fulfilling with native offer items", async () => { - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - const fulfillments = [ - toFulfillment([[0, 0]], [[1, 0]]), - toFulfillment([[1, 0]], [[0, 0]]), - ]; - - await marketplaceContract - .connect(owner) - .matchOrders([order, mirrorOrder], fulfillments, { - value: ethAmount, - }); - }); - - it("matchAdvancedOrders allows fulfilling with native offer items", async () => { - await mintAndApproveERC20( - buyer, - marketplaceContract.address, - tokenAmount - ); - - const { order } = await createOrder( - seller, - zone, - offer, - consideration, - 0 // FULL_OPEN - ); - const { mirrorOrder } = await createMirrorBuyNowOrder( - buyer, - zone, - order - ); - const fulfillments = [ - toFulfillment([[0, 0]], [[1, 0]]), - toFulfillment([[1, 0]], [[0, 0]]), - ]; - - await marketplaceContract - .connect(owner) - .matchAdvancedOrders([order, mirrorOrder], [], fulfillments, { - value: ethAmount, - }); - }); - }); - }); -}); diff --git a/test/revert.spec.ts b/test/revert.spec.ts new file mode 100644 index 000000000..0b0908376 --- /dev/null +++ b/test/revert.spec.ts @@ -0,0 +1,8486 @@ +import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import hre, { ethers, network } from "hardhat"; + +import { deployContract } from "./utils/contracts"; +import { merkleTree } from "./utils/criteria"; +import { + buildOrderStatus, + buildResolver, + defaultBuyNowMirrorFulfillment, + getBasicOrderParameters, + getItemETH, + randomBN, + randomHex, + toBN, + toFulfillment, + toFulfillmentComponents, + toKey, +} from "./utils/encoding"; +import { faucet, getWalletWithEther } from "./utils/faucet"; +import { fixtureERC20, seaportFixture } from "./utils/fixtures"; +import { + VERSION, + getCustomRevertSelector, + minRandom, + simulateMatchOrders, +} from "./utils/helpers"; + +import type { + ConduitInterface, + ConsiderationInterface, + EIP1271Wallet, + EIP1271Wallet__factory, + Reenterer, + TestBadContractOfferer, + TestERC1155, + TestERC20, + TestERC721, + TestZone, +} from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { ConsiderationItem, Fulfillment, OfferItem } from "./utils/types"; +import type { BigNumber, Wallet } from "ethers"; + +const { parseEther } = ethers.utils; + +describe(`Reverts (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let conduitKeyOne: string; + let conduitOne: ConduitInterface; + let EIP1271WalletFactory: EIP1271Wallet__factory; + let marketplaceContract: ConsiderationInterface; + let stubZone: TestZone; + let testERC1155: TestERC1155; + let testERC20: TestERC20; + let testERC721: TestERC721; + + let checkExpectedEvents: SeaportFixtures["checkExpectedEvents"]; + let createMirrorAcceptOfferOrder: SeaportFixtures["createMirrorAcceptOfferOrder"]; + let createMirrorBuyNowOrder: SeaportFixtures["createMirrorBuyNowOrder"]; + let createOrder: SeaportFixtures["createOrder"]; + let getTestItem1155: SeaportFixtures["getTestItem1155"]; + let getTestItem20: SeaportFixtures["getTestItem20"]; + let getTestItem721: SeaportFixtures["getTestItem721"]; + let getTestItem721WithCriteria: SeaportFixtures["getTestItem721WithCriteria"]; + let mint1155: SeaportFixtures["mint1155"]; + let mint721: SeaportFixtures["mint721"]; + let mintAndApprove1155: SeaportFixtures["mintAndApprove1155"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let mintAndApproveERC20: SeaportFixtures["mintAndApproveERC20"]; + let set1155ApprovalForAll: SeaportFixtures["set1155ApprovalForAll"]; + let set721ApprovalForAll: SeaportFixtures["set721ApprovalForAll"]; + let withBalanceChecks: SeaportFixtures["withBalanceChecks"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + checkExpectedEvents, + conduitKeyOne, + conduitOne, + createMirrorAcceptOfferOrder, + createMirrorBuyNowOrder, + createOrder, + EIP1271WalletFactory, + getTestItem1155, + getTestItem20, + getTestItem721, + getTestItem721WithCriteria, + marketplaceContract, + mint1155, + mint721, + mintAndApprove1155, + mintAndApprove721, + mintAndApproveERC20, + reenterer, + set1155ApprovalForAll, + set721ApprovalForAll, + stubZone, + testERC1155, + testERC20, + testERC721, + withBalanceChecks, + } = await seaportFixture(owner)); + }); + + let seller: Wallet; + let buyer: Wallet; + let zone: Wallet; + let reenterer: Reenterer; + + let sellerContract: EIP1271Wallet; + + async function setupFixture() { + // Setup basic buyer/seller wallets with ETH + const seller = new ethers.Wallet(randomHex(32), provider); + const buyer = new ethers.Wallet(randomHex(32), provider); + const zone = new ethers.Wallet(randomHex(32), provider); + + const sellerContract = await EIP1271WalletFactory.deploy(seller.address); + + for (const wallet of [seller, buyer, zone, sellerContract, reenterer]) { + await faucet(wallet.address, provider); + } + + return { + seller, + buyer, + zone, + sellerContract, + }; + } + + beforeEach(async () => { + ({ seller, buyer, zone, sellerContract } = await loadFixture(setupFixture)); + }); + + describe("Misconfigured orders", async () => { + it("Reverts on bad fraction amounts", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 0; + order.denominator = 10; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "BadFraction"); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 0; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "BadFraction"); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; + order.denominator = 1; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "BadFraction"); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 2; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 2) + ); + }); + it("Reverts on inexact fraction amounts", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 8191; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "InexactFraction"); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 2; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 2) + ); + }); + it("Reverts on partial fill attempt when not supported by order", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 2; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "PartialFillsNotEnabledForOrder" + ); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 1; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Reverts on partially filled order via basic fulfillment", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 2; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 2) + ); + + const basicOrderParameters = getBasicOrderParameters( + 1, // EthForERC1155 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `OrderPartiallyFilled` + ) + .withArgs(orderHash); + }); + it("Reverts on fully filled order", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 1; + order.denominator = 1; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "OrderAlreadyFilled" + ) + .withArgs(orderHash); + }); + it("Reverts on non-zero unused item parameters (identifier set on native, basic, ERC721)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(1000, 1000, seller.address), + getItemETH(10, 10, zone.address), + getItemETH(20, 20, owner.address), + ]; + + consideration[0].identifierOrCriteria = minRandom(1); + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + }); + it("Reverts on non-zero unused item parameters (identifier set on ERC20, basic, ERC721)", async () => { + // Seller mints ERC20 + await mintAndApproveERC20(seller, marketplaceContract.address, 1000); + + // Buyer mints nft + const nftId = await mintAndApprove721(buyer, marketplaceContract.address); + + const offer = [getTestItem20(500, 500)]; + + offer[0].identifierOrCriteria = minRandom(1); + + const consideration = [ + getTestItem721(nftId, 1, 1, seller.address), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order, orderHash } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 4, // ERC721ForERC20 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + }); + it("Reverts on non-zero unused item parameters (identifier set on native, basic, ERC1155)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + consideration[0].identifierOrCriteria = amount; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 1, // EthForERC1155 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + }); + it("Reverts on non-zero unused item parameters (identifier set on ERC20, basic)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + consideration[0].identifierOrCriteria = amount; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 3, // ERC20ForERC1155 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + }); + it("Reverts on non-zero unused item parameters (token set on native, standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + consideration[0].token = seller.address; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + }); + it("Reverts on non-zero unused item parameters (identifier set on native, standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + consideration[0].identifierOrCriteria = amount; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + }); + it("Reverts on non-zero unused item parameters (identifier set on ERC20, standard)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + consideration[0].identifierOrCriteria = amount; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + `UnusedItemParameters` + ); + }); + it("Reverts on inadequate consideration items", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + // Remove a consideration item, but do not reduce + // totalOriginalConsiderationItems as MissingOriginalConsiderationItems + // is being tested for + order.parameters.consideration.pop(); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "MissingOriginalConsiderationItems" + ); + }); + it("Reverts on invalid submitter when required by order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + zone, + value + ); + + expect(executions.length).to.equal(4); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `InvalidRestrictedOrder` + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.reverted; + } + + const tx = marketplaceContract + .connect(zone) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: zone.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: zone.address, + }, + ], + executions + ); + return receipt; + }); + it("Reverts on invalid signatures", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const originalSignature = order.signature; + + // set an invalid V value + order.signature = order.signature.slice(0, -2) + "01"; + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + let expectedRevertReason = + getCustomRevertSelector("BadSignatureV(uint8)") + "1".padStart(64, "0"); + + let tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillBasicOrder(basicOrderParameters, { + value, + }); + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ).to.be.reverted; + + // construct an invalid signature + basicOrderParameters.signature = "0x".padEnd(130, "f") + "1c"; + + expectedRevertReason = getCustomRevertSelector("InvalidSigner()"); + + tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillBasicOrder(basicOrderParameters, { + value, + }); + expect(provider.call(tx)).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidSigner" + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ).to.be.reverted; + + basicOrderParameters.signature = originalSignature; + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Reverts on invalid 1271 signature", async () => { + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves marketplace contract to transfer tokens + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(40, 40, zone.address), + getTestItem20(40, 40, owner.address), + ]; + + const { order } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + zone // wrong signer + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.revertedWith("BAD SIGNER"); + }); + it("Reverts on invalid contract 1271 signature and contract does not supply a revert reason", async () => { + await sellerContract.connect(owner).revertWithMessage(false); + + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves marketplace contract to transfer tokens + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + zone // wrong signer + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.revertedWithCustomError( + marketplaceContract, + "BadContractSignature" + ); + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.reverted; + } + }); + it("Reverts on invalid contract 1271 signature and contract does not return magic value", async () => { + await sellerContract.connect(owner).setValid(false); + + // Seller mints nft to contract + const nftId = await mint721(sellerContract); + + // Seller approves marketplace contract to transfer NFT + await expect( + sellerContract + .connect(seller) + .approveNFT(testERC721.address, marketplaceContract.address) + ) + .to.emit(testERC721, "ApprovalForAll") + .withArgs(sellerContract.address, marketplaceContract.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves marketplace contract to transfer tokens + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + sellerContract.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order } = await createOrder( + sellerContract, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + if (!process.env.REFERENCE) { + const expectedRevertReason = getCustomRevertSelector( + "BadContractSignature()" + ); + + const tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillBasicOrder(basicOrderParameters); + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.reverted; + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters) + ).to.be.reverted; + } + + await sellerContract.connect(owner).setValid(true); + }); + it("Reverts on restricted order where isValidOrder reverts with no data", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2, // FULL_RESTRICTED, + [], + null, + seller, + "0x".padEnd(65, "0") + "2" + ); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `InvalidRestrictedOrder` + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } + + order.extraData = "0x0102030405"; + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `InvalidRestrictedOrder` + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; + } + }); + it("Reverts on restricted order where isValidOrder returns non-magic value", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2, // FULL_RESTRICTED, + [], + null, + seller, + "0x".padEnd(65, "0") + "3" + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `InvalidRestrictedOrder` + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ).to.be.reverted; + } + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `InvalidRestrictedOrder` + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } + + order.extraData = "0x01"; + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + `InvalidRestrictedOrder` + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; + } + }); + it("Reverts on missing offer or consideration components", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + let fulfillments: Fulfillment[] = [ + { + offerComponents: [], + considerationComponents: [], + }, + ]; + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "OfferAndConsiderationRequiredOnFulfillment" + ); + + fulfillments = [ + { + offerComponents: [], + considerationComponents: [ + { + orderIndex: 0, + itemIndex: 0, + }, + ], + }, + ]; + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "OfferAndConsiderationRequiredOnFulfillment" + ); + + fulfillments = [ + { + offerComponents: [ + { + orderIndex: 0, + itemIndex: 0, + }, + ], + considerationComponents: [], + }, + ]; + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "OfferAndConsiderationRequiredOnFulfillment" + ); + + fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("Reverts on mismatched offer and consideration components", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + let fulfillments = [toFulfillment([[0, 0]], [[0, 0]])]; + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "MismatchedFulfillmentOfferAndConsiderationComponents" + ) + .withArgs(0); + + fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("Reverts on mismatched offer and consideration components (branch coverage 1)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [getTestItem721(10, 1, 1, seller.address)]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [toFulfillment([[0, 0]], [[0, 0]])]; + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments) + ).to.be.revertedWithCustomError( + marketplaceContract, + "MismatchedFulfillmentOfferAndConsiderationComponents" + ); + }); + it("Reverts on mismatched offer and consideration components (branch coverage 2)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem721(10, 1, 1, seller.address, owner.address), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [toFulfillment([[0, 0]], [[0, 0]])]; + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments) + ).to.be.revertedWithCustomError( + marketplaceContract, + "MismatchedFulfillmentOfferAndConsiderationComponents" + ); + }); + it("Reverts on mismatched offer components", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [ + [0, 0], + [0, 1], + ], + [[1, 0]], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on mismatched offer components (branch coverage 1)", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [ + [0, 1], + [0, 0], + ], + [[1, 0]], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching offerer (branch coverage 2)", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offer2 = offer.map((o) => ({ ...o })); + + offer2[0].identifierOrCriteria = secondNFTId; + + const { order: order2 } = await createOrder( + owner, + zone, + offer2, + consideration, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [ + [2, 0], + [0, 1], + ], + [[1, 0]], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching conduit key (branch coverage 3)", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offer2 = offer.map((o) => ({ ...o })); + + offer2[0].identifierOrCriteria = secondNFTId; + + const { order: order2 } = await createOrder( + seller, + zone, + offer2, + consideration, + 0, + [], + null, + undefined, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [ + [2, 0], + [0, 1], + ], + [[1, 0]], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching itemType (branch coverage 4)", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offer2 = offer.map((o) => ({ ...o })); + + offer2[0].identifierOrCriteria = secondNFTId; + + offer2[0].itemType = 1; + + const { order: order2 } = await createOrder( + seller, + zone, + offer2, + consideration, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [ + [2, 0], + [0, 1], + ], + [[1, 0]], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching token", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offer2 = offer.map((o) => ({ ...o })); + + offer2[0].identifierOrCriteria = secondNFTId; + + offer2[0].token = testERC1155.address; + + const { order: order2 } = await createOrder( + seller, + zone, + offer2, + consideration, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [ + [2, 0], + [0, 1], + ], + [[1, 0]], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching recipient", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("10"), parseEther("1"), zone.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const consideration2 = consideration.map((o) => ({ ...o })); + + consideration2[0].recipient = owner.address; + + const { order: order2 } = await createOrder( + seller, + zone, + offer, + consideration2, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [ + [[1, 0]], + [ + [2, 0], + [2, 1], + ], + ], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching itemType", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("10"), parseEther("1"), zone.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const consideration2 = consideration.map((o) => ({ ...o })); + + consideration2[0].itemType = 1; + + const { order: order2 } = await createOrder( + seller, + zone, + offer, + consideration2, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [ + [[1, 0]], + [ + [2, 0], + [0, 0], + ], + ], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching token", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("10"), parseEther("1"), zone.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const consideration2 = consideration.map((o) => ({ ...o })); + + consideration2[0].token = testERC1155.address; + + const { order: order2 } = await createOrder( + seller, + zone, + offer, + consideration2, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [ + [[1, 0]], + [ + [2, 0], + [0, 0], + ], + ], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on invalid matching identifier", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("10"), parseEther("1"), zone.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const consideration2 = consideration.map((o) => ({ ...o })); + + consideration2[0].identifierOrCriteria = nftId; + + const { order: order2 } = await createOrder( + seller, + zone, + offer, + consideration2, + 0 + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [ + [[1, 0]], + [ + [2, 0], + [0, 0], + ], + ], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder, order2], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on mismatched consideration components", async () => { + // Seller mints nft + const nftId = await mint721(seller); + + const secondNFTId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: secondNFTId, + startAmount: toBN(1), + endAmount: toBN(1), + }, + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getTestItem20(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [[0, 0]], + [ + [1, 0], + [1, 1], + ], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on fulfillment component with out-of-range order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [ + [[2, 0]], + [ + [1, 0], + [1, 1], + ], + ], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + + it("Reverts on fulfillment component with out-of-range initial order on fulfillAvailableOrders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [ + getTestItem1155(nftId, amount.div(2), amount.div(2)), + getTestItem1155(nftId, amount.div(2), amount.div(2)), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + [ + { orderIndex: 5, itemIndex: 0 }, + { orderIndex: 0, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [order], + offerComponents, + considerationComponents, + toKey(0), + 100, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on unmet consideration items", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "ConsiderationNotMet" + ) + .withArgs(0, 2, parseEther("1")); + }); + it("Reverts on fulfillAvailableAdvancedOrders with empty fulfillment component", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [[]]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "MissingFulfillmentComponentOnAggregation" + ) + .withArgs(0); + }); + it("Reverts on fulfillAvailableAdvancedOrders with out-of-range initial offer order", async () => { + // Seller mints nft + const { nftId, amount } = await mint1155(seller, 2); + + // Seller approves marketplace contract to transfer NFT + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(nftId, amount, amount, undefined), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + [ + { orderIndex: 2, itemIndex: 0 }, + { orderIndex: 0, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on fulfillAvailableAdvancedOrders with out-of-range offer order", async () => { + // Seller mints nft + const { nftId, amount } = await mint1155(seller, 2); + + // Seller approves marketplace contract to transfer NFT + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(nftId, amount, amount, undefined), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on fulfillAvailableAdvancedOrders with mismatched offer components", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId), getTestItem20(1, 1)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 0, itemIndex: 1 }, + ], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on fulfillAvailableAdvancedOrders with out-of-range consideration order", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [[{ orderIndex: 0, itemIndex: 0 }]]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 1 }, + ], + [{ orderIndex: 2, itemIndex: 2 }], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on fulfillAvailableAdvancedOrders with mismatched consideration components", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + { + itemType: 2, // ERC721 + token: testERC721.address, + identifierOrCriteria: nftId, + startAmount: toBN(1), + endAmount: toBN(1), + recipient: zone.address, + }, + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [[{ orderIndex: 0, itemIndex: 0 }]]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 0, itemIndex: 1 }, + ], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + it("Reverts on fulfillAvailableAdvancedOrders no available components", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount.div(2), amount.div(2))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + // first order is expired + const { order: orderOne, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "EXPIRED" + ); + + // second order will be cancelled + const { + order: orderTwo, + orderHash: orderHashTwo, + orderComponents, + } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // can cancel it + await expect( + marketplaceContract.connect(seller).cancel([orderComponents]) + ) + .to.emit(marketplaceContract, "OrderCancelled") + .withArgs(orderHashTwo, seller.address, zone.address); + + // third order will be filled + const { order: orderThree, orderHash: orderHashThree } = + await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // can fill it + await withBalanceChecks([orderThree], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillOrder(orderThree, toKey(0), { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order: orderThree, + orderHash: orderHashThree, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + + const offerComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + ], + ]; + const considerationComponents = [ + [ + { orderIndex: 0, itemIndex: 0 }, + { orderIndex: 1, itemIndex: 0 }, + { orderIndex: 2, itemIndex: 0 }, + ], + [ + { orderIndex: 0, itemIndex: 1 }, + { orderIndex: 1, itemIndex: 1 }, + { orderIndex: 2, itemIndex: 1 }, + ], + [ + { orderIndex: 0, itemIndex: 2 }, + { orderIndex: 1, itemIndex: 2 }, + { orderIndex: 2, itemIndex: 2 }, + ], + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo, orderThree], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.mul(3), + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoSpecifiedOrdersAvailable" + ); + }); + it("Reverts on out-of-range criteria resolvers", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + let criteriaResolvers = [ + buildResolver(3, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "OrderCriteriaResolverOutOfRange" + ) + .withArgs(0); + + criteriaResolvers = [ + buildResolver(0, 0, 5, nftId, proofs[nftId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "OfferCriteriaResolverOutOfRange" + ); + + criteriaResolvers = [ + buildResolver(0, 1, 5, nftId, proofs[nftId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "ConsiderationCriteriaResolverOutOfRange" + ); + + criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + if (process.env.REFERENCE) { + it("Reverts on out-of-range criteria resolver (match)", async () => { + // Seller mints nfts + const nftId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + let criteriaResolvers = [ + buildResolver(3, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder } = await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = [toFulfillment([[1, 0]], [[0, 0]])]; + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "OrderCriteriaResolverOutOfRange" + ) + .withArgs(0); + + criteriaResolvers = [ + buildResolver(0, 0, 5, nftId, proofs[nftId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "OfferCriteriaResolverOutOfRange" + ); + + criteriaResolvers = [ + buildResolver(0, 1, 5, nftId, proofs[nftId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "ConsiderationCriteriaResolverOutOfRange" + ); + }); + } + it("Reverts on unresolved criteria items", async () => { + // Seller and buyer both mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(buyer.address, secondNFTId); + + const tokenIds = [nftId, secondNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + // Buyer approves marketplace contract to transfer NFTs + await set721ApprovalForAll(buyer, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getTestItem721WithCriteria(root, toBN(1), toBN(1), owner.address), + ]; + + let criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "UnresolvedConsiderationCriteria" + ) + .withArgs(0, 0); + + criteriaResolvers = [ + buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "UnresolvedOfferCriteria" + ) + .withArgs(0, 0); + + criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), + ]; + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + if (process.env.REFERENCE) { + it("Reverts on unresolved criteria items (match)", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + + const tokenIds = [nftId, secondNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getTestItem721WithCriteria(root, toBN(1), toBN(1), owner.address), + ]; + + let criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { mirrorOrder } = await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = [toFulfillment([[1, 0]], [[0, 0]])]; + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "UnresolvedConsiderationCriteria" + ) + .withArgs(0, 0); + + criteriaResolvers = [ + buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), + ]; + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "UnresolvedOfferCriteria" + ) + .withArgs(0, 0); + + criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + buildResolver(0, 1, 0, secondNFTId, proofs[secondNFTId.toString()]), + ]; + }); + } + it("Reverts on attempts to resolve criteria for non-criteria item", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { proofs } = merkleTree(tokenIds); + + const offer = [ + getTestItem721(nftId, toBN(1), toBN(1), undefined, testERC721.address), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "CriteriaNotEnabledForItem" + ); + }); + if (process.env.REFERENCE) { + it("Reverts on attempts to resolve criteria for non-criteria item (match)", async () => { + // Seller mints nfts + const nftId = await mint721(seller); + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [ + getTestItem721(root, toBN(1), toBN(1), undefined, testERC721.address), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { mirrorOrder } = await createMirrorAcceptOfferOrder( + buyer, + zone, + order, + criteriaResolvers + ); + + const fulfillments = [toFulfillment([[1, 0]], [[0, 0]])]; + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + criteriaResolvers, + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "CriteriaNotEnabledForItem" + ); + }); + } + it("Reverts on offer amount overflow", async () => { + const { testERC20: testERC20Two } = await fixtureERC20(owner); + // Buyer mints nfts + const nftId = await mintAndApprove721(buyer, marketplaceContract.address); + + await testERC20Two.mint(seller.address, ethers.constants.MaxUint256); + // Seller approves marketplace contract to transfer NFTs + await testERC20Two + .connect(seller) + .approve(marketplaceContract.address, ethers.constants.MaxUint256); + + const offer = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + undefined, + testERC20Two.address + ), + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + undefined, + testERC20Two.address + ), + ]; + + const consideration = [getTestItem721(nftId, 1, 1, seller.address)]; + + const offer2 = [getTestItem721(nftId, 1, 1)]; + const consideration2 = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + buyer.address, + testERC20Two.address + ), + ]; + + const fulfillments = [ + toFulfillment( + [ + [0, 0], + [0, 1], + ], + [[1, 0]] + ), + toFulfillment([[1, 0]], [[0, 0]]), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 1 + ); + + const { order: order2 } = await createOrder( + buyer, + zone, + offer2, + consideration2, + 1 + ); + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, order2], + [], + fulfillments, + ethers.constants.AddressZero + ) + ).to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW); + }); + + it("Reverts on offer amount overflow when another amount is 0", async () => { + const { testERC20: testERC20Two } = await fixtureERC20(owner); + // Buyer mints nfts + const nftId = await mintAndApprove721(buyer, marketplaceContract.address); + + await testERC20Two.mint(seller.address, ethers.constants.MaxUint256); + // Seller approves marketplace contract to transfer NFTs + await testERC20Two + .connect(seller) + .approve(marketplaceContract.address, ethers.constants.MaxUint256); + + const offer = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + undefined, + testERC20Two.address + ), + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + undefined, + testERC20Two.address + ), + getTestItem20(0, 0, undefined, testERC20Two.address), + ]; + + const consideration = [getTestItem721(nftId, 1, 1, seller.address)]; + + const offer2 = [getTestItem721(nftId, 1, 1)]; + const consideration2 = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + buyer.address, + testERC20Two.address + ), + ]; + + const fulfillments = [ + toFulfillment( + [ + [0, 0], + [0, 1], + [0, 2], + ], + [[1, 0]] + ), + toFulfillment([[1, 0]], [[0, 0]]), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 1 + ); + + const { order: order2 } = await createOrder( + buyer, + zone, + offer2, + consideration2, + 1 + ); + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, order2], + [], + fulfillments, + ethers.constants.AddressZero + ) + ).to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW); + + // Reverts on out-of-bounds fulfillment orderIndex + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, order2], + [], + [toFulfillment([[3, 0]], [[0, 0]])], + ethers.constants.AddressZero + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidFulfillmentComponentData" + ); + }); + + it("Reverts on consideration amount overflow", async () => { + const { testERC20: testERC20Two } = await fixtureERC20(owner); + // Buyer mints nfts + const nftId = await mintAndApprove721(buyer, marketplaceContract.address); + + await testERC20Two.mint(seller.address, ethers.constants.MaxUint256); + // Seller approves marketplace contract to transfer NFTs + await testERC20Two + .connect(seller) + .approve(marketplaceContract.address, ethers.constants.MaxUint256); + + const offer = [getTestItem721(nftId, 1, 1)]; + + const consideration = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + seller.address, + testERC20Two.address + ), + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + seller.address, + testERC20Two.address + ), + ]; + + const offer2 = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + undefined, + testERC20Two.address + ), + ]; + const consideration2 = [getTestItem721(nftId, 1, 1, buyer.address)]; + + const fulfillments = [ + toFulfillment( + [[1, 0]], + [ + [0, 0], + [0, 1], + ] + ), + toFulfillment([[0, 0]], [[1, 0]]), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 1 + ); + + const { order: order2 } = await createOrder( + buyer, + zone, + offer2, + consideration2, + 1 + ); + + await expect( + marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, order2], + [], + fulfillments, + ethers.constants.AddressZero + ) + ).to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW); + }); + + it("Reverts on consideration amount overflow when another amount is 0", async () => { + const { testERC20: testERC20Two } = await fixtureERC20(owner); + // Buyer mints nfts + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + await testERC20Two.mint(buyer.address, ethers.constants.MaxUint256); + // Seller approves marketplace contract to transfer NFTs + await testERC20Two + .connect(buyer) + .approve(marketplaceContract.address, ethers.constants.MaxUint256); + + const offer = [getTestItem721(nftId, 1, 1)]; + + const consideration = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + seller.address, + testERC20Two.address + ), + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + seller.address, + testERC20Two.address + ), + getTestItem20(0, 0, seller.address, testERC20Two.address), + ]; + + const offer2 = [ + getTestItem20( + ethers.constants.MaxUint256, + ethers.constants.MaxUint256, + undefined, + testERC20Two.address + ), + ]; + const consideration2 = [getTestItem721(nftId, 1, 1, buyer.address)]; + + const fulfillments = [ + toFulfillment( + [[1, 0]], + [ + [0, 0], + [0, 1], + [0, 2], + ] + ), + toFulfillment([[0, 0]], [[1, 0]]), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 1 + ); + + const { order: order2 } = await createOrder( + buyer, + zone, + offer2, + consideration2, + 1 + ); + + await expect( + marketplaceContract.matchAdvancedOrders( + [order, order2], + [], + fulfillments, + ethers.constants.AddressZero + ) + ).to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW); + }); + + it("Reverts on supplying a criteria proof to a collection-wide criteria item", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(0, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidProof"); + + criteriaResolvers[0].criteriaProof = []; + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + + it("Reverts on supplying a criteria proof to a collection-wide criteria item (aggregate)", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(0, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order: orderOne, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const { order: orderTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + const offerComponents = [ + toFulfillmentComponents([ + [0, 0], + [1, 0], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [0, 2], + [1, 2], + ], + ].map(toFulfillmentComponents); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [orderOne, orderTwo], + criteriaResolvers, + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { + value: value.mul(2), + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidProof"); + }); + + it("Reverts on invalid criteria proof", async () => { + // Seller mints nfts + const nftId = randomBN(); + const secondNFTId = randomBN(); + const thirdNFTId = randomBN(); + + await testERC721.mint(seller.address, nftId); + await testERC721.mint(seller.address, secondNFTId); + await testERC721.mint(seller.address, thirdNFTId); + + const tokenIds = [nftId, secondNFTId, thirdNFTId]; + + // Seller approves marketplace contract to transfer NFTs + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + const { root, proofs } = merkleTree(tokenIds); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + criteriaResolvers + ); + + criteriaResolvers[0].identifier = criteriaResolvers[0].identifier.add(1); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidProof"); + + criteriaResolvers[0].identifier = criteriaResolvers[0].identifier.sub(1); + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + + return receipt; + }); + }); + it("Reverts on attempts to transfer >1 ERC721 in single transfer", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [ + getTestItem721(nftId, toBN(2), toBN(2), undefined, testERC721.address), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidERC721TransferAmount" + ) + .withArgs(2); + }); + it("Reverts on attempts to transfer >1 ERC721 in single transfer (basic)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [ + getTestItem721(nftId, toBN(2), toBN(2), undefined, testERC721.address), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidERC721TransferAmount" + ) + .withArgs(2); + }); + it("Reverts on attempts to transfer >1 ERC721 in single transfer via conduit", async () => { + const nftId = await mintAndApprove721(seller, conduitOne.address, 0); + + const offer = [ + getTestItem721(nftId, toBN(2), toBN(2), undefined, testERC721.address), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidERC721TransferAmount" + ) + .withArgs(2); + }); + }); + + describe("Out of timespan", async () => { + it("Reverts on orders that have not started (standard)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value, startTime, endTime } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "NOT_STARTED" + ); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidTime") + .withArgs(startTime, endTime); + }); + it("Reverts on orders that have expired (standard)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value, startTime, endTime } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "EXPIRED" + ); + + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidTime") + .withArgs(startTime, endTime); + }); + it("Reverts on orders that have not started (basic)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value, startTime, endTime } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "NOT_STARTED" + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidTime") + .withArgs(startTime, endTime); + }); + it("Reverts on orders that have expired (basic)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value, startTime, endTime } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "EXPIRED" + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidTime") + .withArgs(startTime, endTime); + }); + it("Reverts on orders that have not started (match)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value, startTime, endTime } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "NOT_STARTED" + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], defaultBuyNowMirrorFulfillment, { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidTime") + .withArgs(startTime, endTime); + }); + it("Reverts on orders that have expired (match)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value, startTime, endTime } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + "EXPIRED" + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], defaultBuyNowMirrorFulfillment, { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidTime") + .withArgs(startTime, endTime); + }); + }); + + describe("Insufficient amounts and bad items", async () => { + it("Reverts when no ether is supplied (basic)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: toBN(0), + }) + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidMsgValue"); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Reverts when not enough ether is supplied (basic)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: toBN(1), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: value.sub(1), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + }, + ]); + + return receipt; + }); + }); + it("Reverts when not enough ether is supplied as offer item (match)", async () => { + // NOTE: this is a ridiculous scenario, buyer is paying the seller's offer + const offer = [getItemETH(parseEther("10"), parseEther("10"))]; + + const consideration = [ + getItemETH(parseEther("1"), parseEther("1"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + await expect( + marketplaceContract + .connect(buyer) + .matchOrders([order, mirrorOrder], fulfillments, { + value: toBN(1), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + await expect( + marketplaceContract + .connect(buyer) + .matchOrders([order, mirrorOrder], fulfillments, { + value: parseEther("9.999999"), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + await marketplaceContract + .connect(buyer) + .matchOrders([order, mirrorOrder], fulfillments, { + value: parseEther("13"), + }); + }); + it("Reverts when not enough ether is supplied (standard + advanced)", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value: toBN(1), + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value: value.sub(1), + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // fulfill with a tiny bit extra to test for returning eth + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value: value.add(1), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Reverts when not enough ether is supplied (match)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(4); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value: toBN(1), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value: value.sub(1), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InsufficientNativeTokensSupplied" + ); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("Reverts when ether is supplied to a non-payable route (basic)", async () => { + // Seller mints nft + const nftId = randomBN(); + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH( + parseEther("1"), + parseEther("1"), + marketplaceContract.address + ), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20_TO_ERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: 1, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, "InvalidMsgValue") + .withArgs(1); + }); + + it(`Reverts when ether transfer fails (returndata)${ + process.env.REFERENCE ? " — SKIPPED ON REFERENCE" : "" + }`, async () => { + if (process.env.REFERENCE) { + return; + } + + const recipient = await ( + await ethers.getContractFactory("ExcessReturnDataRecipient") + ).deploy(); + const setup = async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + // Buyer approves marketplace contract to transfer tokens + + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), recipient.address), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + return basicOrderParameters; + }; + let basicOrderParameters = await setup(); + const baseGas = await marketplaceContract + .connect(buyer) + .estimateGas.fulfillBasicOrder(basicOrderParameters, { + value: parseEther("12"), + }); + + // TODO: clean *this* up + basicOrderParameters = await setup(); + await recipient.setRevertDataSize(1); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: parseEther("12"), + gasLimit: (hre as any).__SOLIDITY_COVERAGE_RUNNING + ? baseGas.add(35000) + : baseGas.add(1000), + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + }); + + it("Reverts when ether transfer fails (basic)", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Seller approves marketplace contract to transfer NFT + await set721ApprovalForAll(seller, marketplaceContract.address, true); + + // Buyer approves marketplace contract to transfer tokens + + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), conduitOne.address), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value: parseEther("12"), + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ) + .withArgs(conduitOne.address, parseEther("1")); + }); + it("Reverts when tokens are not approved", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; // panic code thrown by underlying 721 + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // Buyer approves marketplace contract to transfer tokens + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Reverts when 1155 token transfer reverts", async () => { + // Seller mints nft + const { nftId, amount } = await mint1155(seller, 10000); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWith("NOT_AUTHORIZED"); + }); + it("Reverts when 1155 token transfer reverts (via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mint1155(seller, 10000); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWith("NOT_AUTHORIZED"); + }); + + // Skip this test when testing the reference contract + if (!process.env.REFERENCE) { + it("Reverts when 1155 token transfer reverts (via conduit, returndata)", async () => { + const recipient = await ( + await ethers.getContractFactory("ExcessReturnDataRecipient") + ).deploy(); + + const setup = async () => { + // seller mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(seller.address, tokenAmount); + + // Seller approves conduit contract to transfer tokens + await expect( + testERC20.connect(seller).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(seller.address, conduitOne.address, tokenAmount); + + // Buyer mints nft + const nftId = randomBN(); + const amount = toBN(randomBN(2)); + await testERC1155.mint(buyer.address, nftId, amount.mul(10000)); + + // Buyer approves conduit contract to transfer NFTs + await expect( + testERC1155 + .connect(buyer) + .setApprovalForAll(conduitOne.address, true) + ) + .to.emit(testERC1155, "ApprovalForAll") + .withArgs(buyer.address, conduitOne.address, true); + + const offer = [getTestItem20(tokenAmount, tokenAmount)]; + + const consideration = [ + getTestItem1155( + nftId, + amount.mul(10), + amount.mul(10), + undefined, + recipient.address + ), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + undefined, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + return { + order, + value, + }; + }; + + const { order: initialOrder, value } = await setup(); + const baseGas = await marketplaceContract + .connect(buyer) + .estimateGas.fulfillAdvancedOrder( + initialOrder, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + } + ); + + // TODO: clean *this* up + const { order } = await setup(); + await recipient.setRevertDataSize(1); + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + gasLimit: baseGas.add(74000), + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidCallToConduit" + ); + }); + } + + it("Reverts when transferred item amount is zero", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves marketplace contract to transfer tokens + + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem1155(nftId, 0, 0, undefined)]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "MissingItemAmount"); + }); + it("Reverts when aggregating zero-amount items", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Buyer mints ERC20 + await testERC20.mint(buyer.address, 1000); + + // Buyer approves marketplace contract to transfer tokens + + await expect( + testERC20.connect(buyer).approve(marketplaceContract.address, 1000) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, 1000); + + const offer = [getTestItem1155(nftId, 0, 0, undefined)]; + + const consideration = [ + getTestItem20(amount.mul(100), amount.mul(100), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order: orderOne } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { order: orderTwo } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [ + toFulfillmentComponents([ + [0, 0], + [1, 0], + ]), + ]; + + const considerationComponents = [ + [ + [0, 0], + [1, 0], + ], + [ + [0, 1], + [1, 1], + ], + [ + [0, 2], + [1, 2], + ], + ].map(toFulfillmentComponents); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [orderOne, orderTwo], + offerComponents, + considerationComponents, + toKey(0), + 100 + ) + ).to.be.revertedWithCustomError(marketplaceContract, "MissingItemAmount"); + }); + it("Reverts when ERC20 tokens return falsey values", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves marketplace contract to transfer tokens + + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // block transfers + await testERC20.blockTransfer(true); + + expect(await testERC20.blocked()).to.be.true; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; // TODO: hardhat can't find error msg on IR pipeline + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + await testERC20.blockTransfer(false); + + expect(await testERC20.blocked()).to.be.false; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + }); + it("Works when ERC20 tokens return falsey values", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves marketplace contract to transfer tokens + + await expect( + testERC20 + .connect(buyer) + .approve(marketplaceContract.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, marketplaceContract.address, tokenAmount); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await testERC20.setNoReturnData(true); + + expect(await testERC20.noReturnData()).to.be.true; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(true, false, 1, 1) + ); + + await testERC20.setNoReturnData(false); + + expect(await testERC20.noReturnData()).to.be.false; + }); + + it("Reverts when ERC20 tokens return falsey values (via conduit)", async () => { + // Seller mints nft + const { nftId, amount } = await mint1155(seller, 10000); + + // Seller approves conduit contract to transfer NFTs + await set1155ApprovalForAll(seller, conduitOne.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves conduit contract to transfer tokens + + await expect( + testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, conduitOne.address, tokenAmount); + + // Seller approves conduit contract to transfer tokens + await expect( + testERC20.connect(seller).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(seller.address, conduitOne.address, tokenAmount); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + // block transfers + await testERC20.blockTransfer(true); + + if (!process.env.REFERENCE) { + const data = await marketplaceContract.interface.encodeFunctionData( + "fulfillAdvancedOrder", + [order, [], conduitKeyOne, ethers.constants.AddressZero] + ); + + const fullTx = await buyer.populateTransaction({ + from: buyer.address, + to: marketplaceContract.address, + value, + data, + gasLimit: 30_000_000, + }); + + const returnedData = await provider.call(fullTx); + + const expectedData = marketplaceContract.interface.encodeErrorResult( + "BadReturnValueFromERC20OnTransfer", + [testERC20.address, buyer.address, seller.address, amount.mul(1000)] + ); + + expect(returnedData).to.equal(expectedData); + + let success = false; + + try { + const tx = await marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + } + ); + + const receipt = await tx.wait(); + success = receipt.status === 1; + } catch (err) {} + + expect(success).to.be.false; + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; + } + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect(orderStatus.isValidated).to.equal(false); + expect(orderStatus.isCancelled).to.equal(false); + expect(orderStatus.totalFilled.toString()).to.equal("0"); + expect(orderStatus.totalSize.toString()).to.equal("0"); + + await testERC20.blockTransfer(false); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: conduitKeyOne, + }, + ], + undefined, + [] + ); + + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect(orderStatus.isValidated).to.equal(true); + expect(orderStatus.isCancelled).to.equal(false); + expect(orderStatus.totalFilled.toString()).to.equal("1"); + expect(orderStatus.totalSize.toString()).to.equal("1"); + }); + + it("Reverts when providing non-existent conduit", async () => { + // Seller mints nft + const { nftId, amount } = await mint1155(seller, 10000); + + // Seller approves conduit contract to transfer NFTs + await set1155ApprovalForAll(seller, conduitOne.address, true); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await testERC20.mint(buyer.address, tokenAmount); + + // Buyer approves conduit contract to transfer tokens + await expect( + testERC20.connect(buyer).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(buyer.address, conduitOne.address, tokenAmount); + + // Seller approves conduit contract to transfer tokens + await expect( + testERC20.connect(seller).approve(conduitOne.address, tokenAmount) + ) + .to.emit(testERC20, "Approval") + .withArgs(seller.address, conduitOne.address, tokenAmount); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getTestItem20(amount.mul(1000), amount.mul(1000), seller.address), + getTestItem20(amount.mul(10), amount.mul(10), zone.address), + getTestItem20(amount.mul(20), amount.mul(20), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const badKey = ethers.constants.HashZero.slice(0, -1) + "2"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + badKey, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError(marketplaceContract, "InvalidConduit"); + + let orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect(orderStatus.isValidated).to.equal(false); + expect(orderStatus.isCancelled).to.equal(false); + expect(orderStatus.totalFilled.toString()).to.equal("0"); + expect(orderStatus.totalSize.toString()).to.equal("0"); + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: conduitKeyOne, + }, + ], + undefined, + undefined + ); + return receipt; + }); + + orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect(orderStatus.isValidated).to.equal(true); + expect(orderStatus.isCancelled).to.equal(false); + expect(orderStatus.totalFilled.toString()).to.equal("1"); + expect(orderStatus.totalSize.toString()).to.equal("1"); + }); + + it("Reverts when 1155 tokens are not approved", async () => { + // Seller mints first nft + const { nftId } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + const offer = [ + getTestItem1155(nftId, 0, 0), + getTestItem1155(secondNftId, secondAmount, secondAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError(marketplaceContract, "MissingItemAmount"); + }); + it("Reverts when 1155 tokens are not approved", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + const offer = [ + getTestItem1155(nftId, amount, amount, undefined), + getTestItem1155(secondNftId, secondAmount, secondAmount), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const { mirrorOrder, mirrorOrderHash } = await createMirrorBuyNowOrder( + buyer, + zone, + order + ); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWith("NOT_AUTHORIZED"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + // Seller approves marketplace contract to transfer NFT + + await set1155ApprovalForAll(seller, marketplaceContract.address, true); + + const executions = await simulateMatchOrders( + marketplaceContract, + [order, mirrorOrder], + fulfillments, + owner, + value + ); + + expect(executions.length).to.equal(5); + + const tx = marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: owner.address, + }, + { + order: mirrorOrder, + orderHash: mirrorOrderHash, + fulfiller: owner.address, + }, + ], + executions + ); + return receipt; + }); + it("Reverts when token account with no code is supplied", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration = [ + getTestItem20( + amount, + amount, + seller.address, + ethers.constants.AddressZero + ), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; // TODO: look into the revert reason more thoroughly + // Transaction reverted: function returned an unexpected amount of data + }); + it("Reverts when 721 account with no code is supplied", async () => { + const offer = [getTestItem721(0, 1, 1, undefined, buyer.address)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ) + .to.be.revertedWithCustomError(marketplaceContract, "NoContract") + .withArgs(buyer.address); + }); + it("Reverts when 1155 account with no code is supplied", async () => { + const amount = toBN(randomBN(2)); + + const offer = [ + getTestItem1155(0, amount, amount, ethers.constants.AddressZero), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError(marketplaceContract, `NoContract`) + .withArgs(ethers.constants.AddressZero); + }); + it("Reverts when 1155 account with no code is supplied (via conduit)", async () => { + const amount = toBN(randomBN(2)); + + const offer = [ + getTestItem1155(0, amount, amount, ethers.constants.AddressZero), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError(marketplaceContract, `NoContract`) + .withArgs(ethers.constants.AddressZero); + }); + it("Reverts when non-token account is supplied as the token", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration = [ + getTestItem20( + amount, + amount, + seller.address, + marketplaceContract.address + ), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "TokenTransferGenericFailure" + ) + .withArgs( + marketplaceContract.address, + buyer.address, + seller.address, + 0, + amount + ); + }); + it("Reverts when non-token account is supplied as the token fulfilled via conduit", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem1155(nftId, amount, amount, undefined)]; + + const consideration = [ + getTestItem20( + amount, + amount, + seller.address, + marketplaceContract.address + ), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + conduitKeyOne, + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "TokenTransferGenericFailure" + ) + .withArgs( + marketplaceContract.address, + buyer.address, + seller.address, + 0, + amount + ); + }); + it("Reverts when non-1155 account is supplied as the token", async () => { + const amount = toBN(randomBN(2)); + + const offer = [ + getTestItem1155(0, amount, amount, marketplaceContract.address), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "TokenTransferGenericFailure" + ) + .withArgs( + marketplaceContract.address, + seller.address, + buyer.address, + 0, + amount + ); + } else { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.reverted; + } + }); + it("Reverts when 1155 token is not approved via conduit", async () => { + // Seller mints first nft + const { nftId, amount } = await mint1155(seller); + + // Seller mints second nft + const { nftId: secondNftId, amount: secondAmount } = await mint1155( + seller + ); + + const offer = [ + getTestItem1155(nftId, amount, amount, testERC1155.address), + getTestItem1155( + secondNftId, + secondAmount, + secondAmount, + testERC1155.address + ), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWith("NOT_AUTHORIZED"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + }); + it("Reverts when 1155 token with no code is supplied as the token via conduit", async () => { + // Seller mints first nft + const nftId = toBN(randomBN(4)); + const amount = toBN(randomBN(4)); + + // Seller mints second nft + const secondNftId = toBN(randomBN(4)); + const secondAmount = toBN(randomBN(4)); + + const offer = [ + getTestItem1155(nftId, amount, amount, ethers.constants.AddressZero), + getTestItem1155( + secondNftId, + secondAmount, + secondAmount, + ethers.constants.AddressZero + ), + ]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0, // FULL_OPEN + [], + null, + seller, + ethers.constants.HashZero, + conduitKeyOne + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = [ + [[[0, 0]], [[1, 0]]], + [[[0, 1]], [[1, 1]]], + [[[1, 0]], [[0, 0]]], + [[[1, 0]], [[0, 1]]], + [[[1, 0]], [[0, 2]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError(marketplaceContract, "NoContract"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + }); + it("Reverts when non-payable ether recipient is supplied", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), conduitOne.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ) + .withArgs(conduitOne.address, parseEther("1")); + }); + it("Reverts when marketplace is an ether recipient for basic orders", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH( + parseEther("1"), + parseEther("1"), + marketplaceContract.address + ), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + const customError = !process.env.REFERENCE + ? "InvalidMsgValue" + : "NativeTokenTransferGenericFailure"; + const args = !process.env.REFERENCE + ? [parseEther("1")] + : [marketplaceContract.address, parseEther("1")]; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { + value, + }) + ) + .to.be.revertedWithCustomError(marketplaceContract, customError) + .withArgs(...args); + }); + }); + + describe("Basic Order Calldata", () => { + let calldata: string | undefined; + let value: BigNumber; + + before(async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + ]; + let order; + ({ order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + )); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + ({ data: calldata } = + await marketplaceContract.populateTransaction.fulfillBasicOrder( + basicOrderParameters + )); + }); + + it("Reverts if BasicOrderParameters has non-default offset", async () => { + calldata = calldata as string; + const badData = [calldata.slice(0, 73), "1", calldata.slice(74)].join(""); + expect(badData.length).to.eq(calldata.length); + + await expect( + buyer.sendTransaction({ + to: marketplaceContract.address, + data: badData, + value, + gasLimit: 100_000, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidBasicOrderParameterEncoding" + ); + }); + + it("Reverts if additionalRecipients has non-default offset", async () => { + calldata = calldata as string; + const badData = [calldata.slice(0, 1161), "1", calldata.slice(1162)].join( + "" + ); + + await expect( + buyer.sendTransaction({ + to: marketplaceContract.address, + data: badData, + value, + gasLimit: 100_000, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidBasicOrderParameterEncoding" + ); + }); + + it("Reverts if signature has non-default offset", async () => { + calldata = calldata as string; + const badData = [calldata.slice(0, 1161), "2", calldata.slice(1162)].join( + "" + ); + + await expect( + buyer.sendTransaction({ + to: marketplaceContract.address, + data: badData, + value, + gasLimit: 100_000, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidBasicOrderParameterEncoding" + ); + }); + }); + + describe("Reentrancy", async () => { + it("Reverts on a reentrant call to fulfillOrder", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + // prepare the reentrant call on the reenterer + const callData = marketplaceContract.interface.encodeFunctionData( + "fulfillOrder", + [order, toKey(0)] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + 0, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to fulfillBasicOrder", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + const callData = marketplaceContract.interface.encodeFunctionData( + "fulfillBasicOrder", + [basicOrderParameters] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder(basicOrderParameters, { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to fulfillBasicOrder_efficient_6GL6yc", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 0, // EthForERC721 + order + ); + + const callData = marketplaceContract.interface.encodeFunctionData( + "fulfillBasicOrder_efficient_6GL6yc", + [basicOrderParameters] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder_efficient_6GL6yc(basicOrderParameters, { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .fulfillBasicOrder_efficient_6GL6yc(basicOrderParameters, { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to fulfillAdvancedOrder", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), reenterer.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + const callData = marketplaceContract.interface.encodeFunctionData( + "fulfillAdvancedOrder", + [order, [], toKey(0), ethers.constants.AddressZero] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to fulfillAvailableOrders", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [toFulfillmentComponents([[0, 0]])]; + + const considerationComponents = [ + [[0, 0]], + [[0, 1]], + [[0, 2]], + [[0, 3]], + ].map(toFulfillmentComponents); + + const callData = marketplaceContract.interface.encodeFunctionData( + "fulfillAvailableOrders", + [[order], offerComponents, considerationComponents, toKey(0), 100] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [order], + offerComponents, + considerationComponents, + toKey(0), + 100, + { value } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [order], + offerComponents, + considerationComponents, + toKey(0), + 100, + { value } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to fulfillAvailableAdvancedOrders", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const offerComponents = [[{ orderIndex: 0, itemIndex: 0 }]]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 0, itemIndex: 1 }], + [{ orderIndex: 0, itemIndex: 2 }], + [{ orderIndex: 0, itemIndex: 3 }], + ]; + + const callData = marketplaceContract.interface.encodeFunctionData( + "fulfillAvailableAdvancedOrders", + [ + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + ] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { value } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 100, + { value } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to matchOrders", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const callData = marketplaceContract.interface.encodeFunctionData( + "matchOrders", + [[order, mirrorOrder], fulfillments] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .matchOrders([order, mirrorOrder], fulfillments, { + value, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to matchAdvancedOrders", async () => { + // Seller mints nft + const { nftId, amount } = await mintAndApprove1155( + seller, + marketplaceContract.address, + 10000 + ); + + const offer = [getTestItem1155(nftId, amount.mul(10), amount.mul(10))]; + + const consideration = [ + getItemETH(amount.mul(1000), amount.mul(1000), seller.address), + getItemETH(amount.mul(10), amount.mul(10), zone.address), + getItemETH(amount.mul(20), amount.mul(20), reenterer.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 1 // PARTIAL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.numerator = 2; // fill two tenths or one fifth + order.denominator = 10; // fill two tenths or one fifth + + const mirrorObject = await createMirrorBuyNowOrder(buyer, zone, order); + + const fulfillments = defaultBuyNowMirrorFulfillment; + + const callData = marketplaceContract.interface.encodeFunctionData( + "matchAdvancedOrders", + [ + [order, mirrorObject.mirrorOrder], + [], + fulfillments, + ethers.constants.AddressZero, + ] + ); + const tx = await reenterer.prepare( + marketplaceContract.address, + value, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(buyer) + .matchAdvancedOrders( + [order, mirrorObject.mirrorOrder], + [], + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(buyer) + .matchAdvancedOrders( + [order, mirrorObject.mirrorOrder], + [], + fulfillments, + ethers.constants.AddressZero, + { + value, + } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to cancel", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, orderComponents, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + const callData = marketplaceContract.interface.encodeFunctionData( + "cancel", + [[orderComponents]] + ); + + const tx = await reenterer.prepare( + marketplaceContract.address, + 0, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(seller) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, which + // reverts with NativeTokenTransferGenericFailure. + await expect( + marketplaceContract + .connect(seller) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to validate", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + const callData = marketplaceContract.interface.encodeFunctionData( + "validate", + [[order]] + ); + + const tx = await reenterer.prepare( + marketplaceContract.address, + 0, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(seller) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, + // which reverts with NativeTokenTransferGenericFailure + await expect( + marketplaceContract + .connect(seller) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + + it("Reverts on a reentrant call to incrementCounter", async () => { + // Seller mints nft + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), zone.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + getItemETH(parseEther("1"), parseEther("1"), reenterer.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + const callData = + marketplaceContract.interface.encodeFunctionData("incrementCounter"); + + const tx = await reenterer.prepare( + marketplaceContract.address, + 0, + callData + ); + await tx.wait(); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract + .connect(seller) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NoReentrantCalls" + ); + } else { + // NoReentrantCalls gets bubbled up in _transferNativeTokens, + // which reverts with NativeTokenTransferGenericFailure + await expect( + marketplaceContract + .connect(seller) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "NativeTokenTransferGenericFailure" + ); + } + }); + }); + + describe("ETH offer items", async () => { + let ethAmount: BigNumber; + const tokenAmount = minRandom(100); + let offer: OfferItem[]; + let consideration: ConsiderationItem[]; + let seller: Wallet; + let buyer: Wallet; + + beforeEach(async () => { + ethAmount = parseEther("1"); + seller = await getWalletWithEther(); + buyer = await getWalletWithEther(); + zone = new ethers.Wallet(randomHex(32), provider); + offer = [getItemETH(ethAmount, ethAmount)]; + consideration = [getTestItem20(tokenAmount, tokenAmount, seller.address)]; + }); + + it("fulfillOrder reverts if any offer item is ETH", async () => { + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { value }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidNativeOfferItem" + ); + }); + + it("fulfillAdvancedOrder reverts if any offer item is ETH", async () => { + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder(order, [], toKey(0), buyer.address, { + value: ethAmount, + }) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidNativeOfferItem" + ); + }); + + it("fulfillAvailableOrders reverts if any offer item is ETH", async () => { + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableOrders( + [order], + [[{ orderIndex: 0, itemIndex: 0 }]], + [[{ orderIndex: 0, itemIndex: 0 }]], + toKey(0), + 100, + { value: ethAmount } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidNativeOfferItem" + ); + }); + + it("fulfillAvailableAdvancedOrders reverts if any offer item is ETH", async () => { + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order], + [], + [[{ orderIndex: 0, itemIndex: 0 }]], + [[{ orderIndex: 0, itemIndex: 0 }]], + toKey(0), + buyer.address, + 100, + { value: ethAmount } + ) + ).to.be.revertedWithCustomError( + marketplaceContract, + "InvalidNativeOfferItem" + ); + }); + + it("matchOrders allows fulfilling with native offer items", async () => { + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + const fulfillments = [ + toFulfillment([[0, 0]], [[1, 0]]), + toFulfillment([[1, 0]], [[0, 0]]), + ]; + + await marketplaceContract + .connect(owner) + .matchOrders([order, mirrorOrder], fulfillments, { + value: ethAmount, + }); + }); + + it("matchAdvancedOrders allows fulfilling with native offer items", async () => { + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + const { mirrorOrder } = await createMirrorBuyNowOrder(buyer, zone, order); + const fulfillments = [ + toFulfillment([[0, 0]], [[1, 0]]), + toFulfillment([[1, 0]], [[0, 0]]), + ]; + + await marketplaceContract + .connect(owner) + .matchAdvancedOrders( + [order, mirrorOrder], + [], + fulfillments, + ethers.constants.AddressZero, + { + value: ethAmount, + } + ); + }); + }); + + describe("Bad contract offerer", async () => { + let seller: Wallet; + let buyer: Wallet; + let offererContract: TestBadContractOfferer; + + beforeEach(async () => { + seller = await getWalletWithEther(); + buyer = await getWalletWithEther(); + zone = new ethers.Wallet(randomHex(32), provider); + + offererContract = await deployContract( + "TestBadContractOfferer", + owner, + marketplaceContract.address, + testERC721.address + ); + }); + + it("Fulfillment reverts if contract offerer is an EOA", async () => { + const offererContract = new ethers.Wallet(randomHex(32), provider); + + // Contract offerer mints nft + const nftId = await mint721(offererContract); + + await set721ApprovalForAll(seller, offererContract.address); + + const offer = [getTestItem721(nftId) as any]; + + const consideration = [ + getItemETH(100, 100, offererContract.address) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Fulfillment does not revert when valid", async () => { + // Contract offerer mints nft + const nftId = await mint721( + offererContract, + 1 // identifier 1: valid + ); + + const offer = [getTestItem721(nftId) as any]; + + const consideration = [ + getItemETH(100, 100, offererContract.address) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await withBalanceChecks([order], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + it("Fulfillment reverts if contract offerer returns no data", async () => { + // Contract offerer mints nft + const nftId = await mint721( + offererContract, + 2 // identifier 2: returns nothing + ); + + const offer = [getTestItem721(nftId) as any]; + + const consideration = [ + getItemETH(100, 100, offererContract.address) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Fulfillment reverts if contract offerer reverts", async () => { + // Contract offerer mints nft + const nftId = await mint721( + offererContract, + 3 // identifier 3: reverts with IntentionalRevert() + ); + + const offer = [getTestItem721(nftId) as any]; + + const consideration = [ + getItemETH(100, 100, offererContract.address) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Fulfillment reverts if contract offerer returns with garbage data", async () => { + // Contract offerer mints nft + const nftId = await mint721( + offererContract, + 4 // identifier 4: reverts with garbage data) + ); + + const offer = [getTestItem721(nftId) as any]; + + const consideration = [ + getItemETH(100, 100, offererContract.address) as any, + ]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + await expect( + marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { value } + ) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidContractOrder" + ) + .withArgs(orderHash); + }); + it("Fulfillment does not revert when valid order included with invalid contract offerer order", async () => { + // Contract offerer mints nft + const nftId10 = await mint721( + offererContract, + 10 // identifier 10: returns garbage data + ); + // Seller mints nft + const nftId100 = await mintAndApprove721( + seller, + marketplaceContract.address, + 100 + ); + + const offer = [getTestItem721(nftId10) as any]; + const offer2 = [getTestItem721(nftId100) as any]; + + const consideration = [ + getItemETH(100, 100, offererContract.address) as any, + ]; + const consideration2 = [getItemETH(100, 100, seller.address) as any]; + + offer[0].identifier = offer[0].identifierOrCriteria; + offer[0].amount = offer[0].endAmount; + + consideration[0].identifier = consideration[0].identifierOrCriteria; + consideration[0].amount = consideration[0].endAmount; + + const { order, value } = await createOrder( + seller, + zone, + offer, + consideration, + 4 // CONTRACT + ); + + const { order: order2, orderHash: orderHash2 } = await createOrder( + seller, + zone, + offer2, + consideration2, + 0 // FULL_OPEN + ); + + const contractOffererNonce = + await marketplaceContract.getContractOffererNonce( + offererContract.address + ); + + const orderHash = + offererContract.address.toLowerCase() + + contractOffererNonce.toHexString().slice(2).padStart(24, "0"); + + const orderStatus = await marketplaceContract.getOrderStatus(orderHash); + + expect({ ...orderStatus }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + const orderStatus2 = await marketplaceContract.getOrderStatus(orderHash2); + + expect({ ...orderStatus2 }).to.deep.equal( + buildOrderStatus(false, false, 0, 0) + ); + + order.parameters.offerer = offererContract.address; + order.numerator = 1; + order.denominator = 1; + order.signature = "0x"; + + const offerComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + ]; + const considerationComponents = [ + [{ orderIndex: 0, itemIndex: 0 }], + [{ orderIndex: 1, itemIndex: 0 }], + ]; + + await withBalanceChecks([order2], 0, [], async () => { + const tx = marketplaceContract + .connect(buyer) + .fulfillAvailableAdvancedOrders( + [order, order2], + [], + offerComponents, + considerationComponents, + toKey(0), + ethers.constants.AddressZero, + 2, + { + value: value.mul(2), + } + ); + const receipt = await (await tx).wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order: order2, + orderHash: orderHash2, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + [] + ); + + return receipt; + }); + }); + }); + + describe(`Changing chainId`, function () { + // Note: Run this test last in this file as it hacks changing the hre + it("Reverts on changed chainId", async () => { + const nftId = await mintAndApprove721( + seller, + marketplaceContract.address + ); + + // Buyer mints ERC20 + const tokenAmount = minRandom(100); + await mintAndApproveERC20( + buyer, + marketplaceContract.address, + tokenAmount + ); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getTestItem20( + tokenAmount.sub(100), + tokenAmount.sub(100), + seller.address + ), + getTestItem20(50, 50, zone.address), + getTestItem20(50, 50, owner.address), + ]; + + const { order } = await createOrder( + seller, + zone, + offer, + consideration, + 0 // FULL_OPEN + ); + + const basicOrderParameters = getBasicOrderParameters( + 2, // ERC20ForERC721 + order + ); + + // Change chainId in-flight to test branch coverage for _deriveDomainSeparator() + // (hacky way, until https://github.com/NomicFoundation/hardhat/issues/3074 is added) + const changeChainId = () => { + const recurse = (obj: any) => { + for (const [key, value] of Object.entries(obj ?? {})) { + if (key === "transactions") continue; + if (key === "chainId") { + obj[key] = typeof value === "bigint" ? BigInt(1) : 1; + } else if (typeof value === "object") { + recurse(obj[key]); + } + } + }; + const hreProvider = hre.network.provider as any; + recurse( + hreProvider._wrapped._wrapped._wrapped?._node?._vm ?? + // When running coverage, there was an additional layer of wrapping + hreProvider._wrapped._wrapped._wrapped._wrapped._node._vm + ); + }; + changeChainId(); + + const expectedRevertReason = getCustomRevertSelector("InvalidSigner()"); + + const tx = await marketplaceContract + .connect(buyer) + .populateTransaction.fulfillBasicOrder(basicOrderParameters); + tx.chainId = 1; + const returnData = await provider.call(tx); + expect(returnData).to.equal(expectedRevertReason); + }); + }); +}); diff --git a/test/transferhelper.spec.ts b/test/transferhelper.spec.ts new file mode 100644 index 000000000..0977f6e7b --- /dev/null +++ b/test/transferhelper.spec.ts @@ -0,0 +1,2548 @@ +import { expect } from "chai"; +import { randomInt } from "crypto"; +import { ethers, network } from "hardhat"; + +import { randomHex } from "./utils/encoding"; +import { faucet } from "./utils/faucet"; +import { + fixtureERC1155, + fixtureERC20, + fixtureERC721, + seaportFixture, +} from "./utils/fixtures"; +import { VERSION } from "./utils/helpers"; + +import type { + ConduitControllerInterface, + ConduitInterface, + EIP1271Wallet, + EIP1271Wallet__factory, + TransferHelper, +} from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { BigNumber, Wallet } from "ethers"; + +describe(`TransferHelper tests (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let conduitController: ConduitControllerInterface; + let EIP1271WalletFactory: EIP1271Wallet__factory; + + let createTransferWithApproval: SeaportFixtures["createTransferWithApproval"]; + let deployNewConduit: SeaportFixtures["deployNewConduit"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + EIP1271WalletFactory, + conduitController, + deployNewConduit, + createTransferWithApproval, + } = await seaportFixture(owner)); + }); + + let sender: Wallet; + let recipient: Wallet; + let zone: Wallet; + + let alice: Wallet; + let bob: Wallet; + let cal: Wallet; + + let senderContract: EIP1271Wallet; + let recipientContract: EIP1271Wallet; + + let tempConduit: ConduitInterface; + let tempConduitKey: string; + let tempTransferHelper: TransferHelper; + + interface Transfer { + itemType: 0 | 1 | 2 | 3 | 4 | 5; + token: string; + from: string; + to: string; + identifier: BigNumber; + amount: BigNumber; + } + + interface TransferHelperItem { + itemType: 0 | 1 | 2 | 3 | 4 | 5; + token: string; + identifier: BigNumber; + amount: BigNumber; + } + + interface TransferWithRecipient { + items: TransferHelperItem[]; + recipient: string; + validateERC721Receiver: boolean; + } + + function createTransferHelperItem(transfer: Transfer): TransferHelperItem { + return { + itemType: transfer.itemType, + token: transfer.token, + identifier: transfer.identifier, + amount: transfer.amount, + }; + } + + function createTransferWithRecipient( + transfers: Transfer[], + recipient: string, + validate: boolean + ): TransferWithRecipient { + const transferHelperItems = []; + for (let i = 0; i < transfers.length; i++) { + transferHelperItems[i] = createTransferHelperItem(transfers[i]); + } + return { + items: transferHelperItems, + recipient, + validateERC721Receiver: validate, + }; + } + + beforeEach(async () => { + // Setup basic buyer/seller wallets with ETH + sender = new ethers.Wallet(randomHex(32), provider); + recipient = new ethers.Wallet(randomHex(32), provider); + zone = new ethers.Wallet(randomHex(32), provider); + + alice = new ethers.Wallet(randomHex(32), provider); + bob = new ethers.Wallet(randomHex(32), provider); + cal = new ethers.Wallet(randomHex(32), provider); + + senderContract = await EIP1271WalletFactory.deploy(sender.address); + recipientContract = await EIP1271WalletFactory.deploy(recipient.address); + + tempConduitKey = owner.address + randomHex(12).slice(2); + tempConduit = await deployNewConduit(owner, tempConduitKey); + + for (const wallet of [ + sender, + recipient, + zone, + senderContract, + recipientContract, + ]) { + await faucet(wallet.address, provider); + } + + // Deploy a new TransferHelper with the tempConduitController address + const transferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + tempTransferHelper = await transferHelperFactory.deploy( + conduitController.address + ); + + await conduitController + .connect(owner) + .updateChannel(tempConduit.address, tempTransferHelper.address, true); + }); + + describe("Single recipient tests", async () => { + it("Executes transfers (many token types) with a conduit", async () => { + // Get 3 Numbers that's value adds to Item Amount and minimum 1. + const itemsToCreate = 10; + const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); + const numERC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); + const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numERC721s); + + const erc20Contracts = []; + const erc20Transfers = []; + + const erc721Contracts = []; + const erc721Transfers = []; + + const erc1155Contracts = []; + const erc1155Transfers = []; + + // Create numERC20s amount of ERC20 objects + for (let i = 0; i < numERC20s; i++) { + // Deploy Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + // Create/Approve X amount of ERC20s + const erc20Transfer = await createTransferWithApproval( + tempERC20Contract, + sender, + 1, + tempConduit.address, + sender.address, + recipient.address + ); + erc20Contracts[i] = tempERC20Contract; + erc20Transfers[i] = erc20Transfer; + } + + // Create numERC721s amount of ERC20 objects + for (let i = 0; i < numERC721s; i++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempConduit.address, + sender.address, + recipient.address + ); + erc721Contracts[i] = tempERC721Contract; + erc721Transfers[i] = erc721Transfer; + } + + // Create numERC1155s amount of ERC1155 objects + for (let i = 0; i < numERC1155s; i++) { + // Deploy Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155( + owner + ); + // Create/Approve numERC1155s amount of ERC1155s + const erc1155Transfer = await createTransferWithApproval( + tempERC1155Contract, + sender, + 3, + tempConduit.address, + sender.address, + recipient.address + ); + erc1155Contracts[i] = tempERC1155Contract; + erc1155Transfers[i] = erc1155Transfer; + } + + const transfers = [ + ...erc20Transfers, + ...erc721Transfers, + ...erc1155Transfers, + ]; + const contracts = [ + ...erc20Contracts, + ...erc721Contracts, + ...erc1155Contracts, + ]; + + const transfersWithRecipients = []; + + transfersWithRecipients[0] = createTransferWithRecipient( + transfers, + recipient.address, + true + ); + + // Send the bulk transfers + await tempTransferHelper + .connect(sender) + .bulkTransfer(transfersWithRecipients, tempConduitKey); + + // Loop through all transfer to do ownership/balance checks + for (let i = 0; i < transfersWithRecipients[0].items.length; i++) { + // Get Itemtype, token, amount, identifier + const { itemType, amount, identifier } = + transfersWithRecipients[0].items[i]; + const token = contracts[i]; + + switch (itemType) { + case 1: // ERC20 + // Check balance + expect( + await (token as typeof erc20Contracts[0]).balanceOf( + sender.address + ) + ).to.equal(0); + expect( + await (token as typeof erc20Contracts[0]).balanceOf( + recipient.address + ) + ).to.equal(amount); + break; + case 2: // ERC721 + case 4: // ERC721_WITH_CRITERIA + expect( + await (token as typeof erc721Contracts[0]).ownerOf(identifier) + ).to.equal(recipient.address); + break; + case 3: // ERC1155 + case 5: // ERC1155_WITH_CRITERIA + // Check balance + expect(await token.balanceOf(sender.address, identifier)).to.equal( + 0 + ); + expect( + await token.balanceOf(recipient.address, identifier) + ).to.equal(amount); + break; + } + } + }); + + it("Cannot execute transfers (many token types) without a conduit", async () => { + // Get 3 Numbers that's value adds to Item Amount and minimum 1. + const itemsToCreate = 10; + const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); + const numERC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); + const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numERC721s); + + const erc20Contracts = []; + const erc20Transfers = []; + + const erc721Contracts = []; + const erc721Transfers = []; + + const erc1155Contracts = []; + const erc1155Transfers = []; + + // Create numERC20s amount of ERC20 objects + for (let i = 0; i < numERC20s; i++) { + // Deploy Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + // Create/Approve X amount of ERC20s + const erc20Transfer = await createTransferWithApproval( + tempERC20Contract, + sender, + 1, + tempTransferHelper.address, + sender.address, + recipient.address + ); + erc20Contracts[i] = tempERC20Contract; + erc20Transfers[i] = erc20Transfer; + } + + // Create numERC721s amount of ERC721 objects + for (let i = 0; i < numERC721s; i++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempTransferHelper.address, + sender.address, + recipient.address + ); + erc721Contracts[i] = tempERC721Contract; + erc721Transfers[i] = erc721Transfer; + } + + // Create numERC1155s amount of ERC1155 objects + for (let i = 0; i < numERC1155s; i++) { + // Deploy Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155( + owner + ); + // Create/Approve numERC1155s amount of ERC1155s + const erc1155Transfer = await createTransferWithApproval( + tempERC1155Contract, + sender, + 3, + tempTransferHelper.address, + sender.address, + recipient.address + ); + erc1155Contracts[i] = tempERC1155Contract; + erc1155Transfers[i] = erc1155Transfer; + } + + const transfers = [ + ...erc20Transfers, + ...erc721Transfers, + ...erc1155Transfers, + ]; + + const transfersWithRecipients = []; + + transfersWithRecipients[0] = createTransferWithRecipient( + transfers, + recipient.address, + true + ); + + // Sending the bulk transfers with no conduit reverts + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer( + transfersWithRecipients, + ethers.utils.formatBytes32String("") + ) + ) + .to.be.revertedWithCustomError(tempTransferHelper, "InvalidConduit") + .withArgs(ethers.constants.HashZero, ethers.constants.AddressZero); + }); + + it("Cannot execute ERC721 transfers to a contract recipient without a conduit", async () => { + // Deploy recipient contract + const erc721RecipientFactory = await ethers.getContractFactory( + "ERC721ReceiverMock" + ); + const erc721Recipient = await erc721RecipientFactory.deploy( + Buffer.from("150b7a02", "hex"), + 0 + ); + + const erc721Contracts = []; + const erc721Transfers = []; + + // Create 5 ERC721 objects + for (let i = 0; i < 5; i++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempTransferHelper.address, + sender.address, + recipient.address + ); + erc721Contracts[i] = tempERC721Contract; + erc721Transfers[i] = erc721Transfer; + } + + const transfersWithRecipients = []; + + transfersWithRecipients[0] = createTransferWithRecipient( + erc721Transfers, + erc721Recipient.address, + true + ); + + // Sending the bulk transfers with no conduit reverts + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer( + transfersWithRecipients, + ethers.utils.formatBytes32String("") + ) + ) + .to.be.revertedWithCustomError(tempTransferHelper, "InvalidConduit") + .withArgs(ethers.constants.HashZero, ethers.constants.AddressZero); + }); + + it("Reverts on native token transfers", async () => { + const ethTransfers = [ + { + items: [ + { + itemType: 0, + token: ethers.constants.AddressZero, + identifier: 0, + amount: 10, + }, + { + itemType: 0, + token: ethers.constants.AddressZero, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(ethTransfers, tempConduitKey) + ).to.be.revertedWithCustomError(tempTransferHelper, "InvalidItemType"); + }); + + it("Reverts on invalid ERC20 identifier via conduit", async () => { + const erc20Transfers = [ + { + items: [ + { + itemType: 1, + token: ethers.constants.AddressZero, + identifier: 5, + amount: 10, + }, + { + itemType: 1, + token: ethers.constants.AddressZero, + identifier: 4, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc20Transfers, tempConduitKey) + ).to.be.revertedWithCustomError( + tempTransferHelper, + "InvalidERC20Identifier" + ); + }); + + it("Reverts on invalid ERC721 transfer amount via conduit", async () => { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + + const erc721Transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 10, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc721Transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "InvalidERC721TransferAmount" + ) + .withArgs(10); + }); + + it("Reverts on invalid ERC721 recipient", async () => { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + + const erc721Transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: tempERC721Contract.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc721Transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ERC721ReceiverErrorRevertBytes" + ) + .withArgs("0x", tempERC721Contract.address, sender.address, 1); + }); + + it("Reverts on invalid function selector", async () => { + const invalidRecipientFactory = await ethers.getContractFactory( + "InvalidERC721Recipient" + ); + const invalidRecipient = await invalidRecipientFactory.deploy(); + + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + + const erc721Transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: invalidRecipient.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc721Transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "InvalidERC721Recipient" + ) + .withArgs(invalidRecipient.address); + }); + + it("Reverts on nonexistent conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + const transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, ethers.utils.formatBytes32String("0xabc")) + ).to.be.reverted; + }); + + it("Reverts on error in ERC721 receiver", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + // Deploy mock ERC721 receiver + const mockERC721ReceiverFactory = await ethers.getContractFactory( + "ERC721ReceiverMock" + ); + const mockERC721Receiver = await mockERC721ReceiverFactory.deploy( + Buffer.from("abcd0000", "hex"), + 1 + ); + + const transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: mockERC721Receiver.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ERC721ReceiverErrorRevertString" + ) + .withArgs( + "ERC721ReceiverMock: reverting", + mockERC721Receiver.address, + sender.address, + 1 + ); + }); + + it("Reverts on error in ERC721 receiver via conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + // Deploy mock ERC721 receiver + const mockERC721ReceiverFactory = await ethers.getContractFactory( + "ERC721ReceiverMock" + ); + const mockERC721Receiver = await mockERC721ReceiverFactory.deploy( + Buffer.from("abcd0000", "hex"), + 1 + ); + + const transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: mockERC721Receiver.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ERC721ReceiverErrorRevertString" + ) + .withArgs( + "ERC721ReceiverMock: reverting", + mockERC721Receiver.address, + sender.address, + 1 + ); + }); + + it("Reverts with custom error in conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + const transfers = [ + { + items: [ + { + itemType: 0, + token: ethers.constants.AddressZero, + identifier: 0, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ).to.be.revertedWithCustomError(tempTransferHelper, "InvalidItemType"); + }); + + it("Reverts with bubbled up string error from call to conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + // Call will revert since ERC721 tokens have not been minted + const transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ConduitErrorRevertString" + ) + .withArgs( + "WRONG_FROM", + tempConduitKey.toLowerCase(), + tempConduit.address + ); + }); + + it("Reverts when no revert string is returned from call to conduit", async () => { + // Deploy ERC1155 Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155(owner); + + await tempERC1155Contract.connect(owner).mint(sender.address, 0, 100); + + const mockConduitControllerFactory = await ethers.getContractFactory( + "ConduitControllerMock" + ); + const mockConduitController = await mockConduitControllerFactory.deploy( + 1 // ConduitMockRevertNoReason + ); + + const mockTransferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + const mockTransferHelper = await mockTransferHelperFactory.deploy( + mockConduitController.address + ); + const mockConduitKey = owner.address + randomHex(12).slice(2); + + // Deploy the mock conduit through the mock conduit controller + await mockConduitController + .connect(owner) + .createConduit(mockConduitKey, owner.address); + + const mockConduitAddress = ( + await mockConduitController.getConduit(mockConduitKey) + )[0]; + + await tempERC1155Contract + .connect(sender) + .setApprovalForAll(mockConduitAddress, true); + + const transfers = [ + { + items: [ + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + mockTransferHelper + .connect(sender) + .bulkTransfer(transfers, mockConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ConduitErrorRevertBytes" + ) + .withArgs("0x", mockConduitKey.toLowerCase(), mockConduitAddress); + }); + + it("Reverts with bubbled up panic error from call to conduit", async () => { + // Deploy mock ERC20 + const mockERC20PanicFactory = await ethers.getContractFactory( + "TestERC20Panic" + ); + const mockERC20Panic = await mockERC20PanicFactory.deploy(); + + const transfers = [ + { + items: [ + { + itemType: 1, + token: mockERC20Panic.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: mockERC20Panic.address, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + const panicError = + "0x4e487b710000000000000000000000000000000000000000000000000000000000000012"; + if (!process.env.REFERENCE) { + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ConduitErrorRevertBytes" + ) + .withArgs( + panicError, + tempConduitKey.toLowerCase(), + tempConduit.address + ); + } else { + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ).to.be.reverted; + } + }); + + it("Reverts with invalid magic value returned by call to conduit", async () => { + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + await tempERC20Contract.connect(owner).mint(sender.address, 100); + + const mockConduitControllerFactory = await ethers.getContractFactory( + "ConduitControllerMock" + ); + const mockConduitController = await mockConduitControllerFactory.deploy( + 2 // ConduitMockInvalidMagic + ); + + const mockTransferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + const mockTransferHelper = await mockTransferHelperFactory.deploy( + mockConduitController.address + ); + const mockConduitKey = owner.address + randomHex(12).slice(2); + + // Deploy the mock conduit through the mock conduit controller + await mockConduitController + .connect(owner) + .createConduit(mockConduitKey, owner.address); + + const mockConduitAddress = ( + await mockConduitController.getConduit(mockConduitKey) + )[0]; + + await tempERC20Contract.connect(sender).approve(mockConduitAddress, 100); + + const transfers = [ + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + mockTransferHelper + .connect(sender) + .bulkTransfer(transfers, mockConduitKey) + ) + .to.be.revertedWithCustomError(mockTransferHelper, "InvalidConduit") + .withArgs(mockConduitKey.toLowerCase(), mockConduitAddress); + }); + + it("Reverts with conduit revert data", async () => { + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + await tempERC20Contract.connect(owner).mint(sender.address, 100); + + const mockConduitControllerFactory = await ethers.getContractFactory( + "ConduitControllerMock" + ); + const mockConduitController = await mockConduitControllerFactory.deploy( + 3 // ConduitMockRevertBytes + ); + + const mockTransferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + const mockTransferHelper = await mockTransferHelperFactory.deploy( + mockConduitController.address + ); + const mockConduitKey = owner.address + randomHex(12).slice(2); + + // Deploy the mock conduit through the mock conduit controller + await mockConduitController + .connect(owner) + .createConduit(mockConduitKey, owner.address); + + const mockConduitAddress = ( + await mockConduitController.getConduit(mockConduitKey) + )[0]; + await tempERC20Contract.connect(sender).approve(mockConduitAddress, 100); + + const transfers = [ + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + ]; + + const customErrorSelector = ethers.utils.id("CustomError()").slice(0, 10); + + await expect( + mockTransferHelper + .connect(sender) + .bulkTransfer(transfers, mockConduitKey) + ) + .to.be.revertedWithCustomError( + mockTransferHelper, + "ConduitErrorRevertBytes" + ) + .withArgs( + customErrorSelector, + mockConduitKey.toLowerCase(), + mockConduitAddress + ); + }); + + it("Reverts when recipient is the null address (with conduit)", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + const transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: ethers.constants.AddressZero, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ).to.be.revertedWithCustomError( + tempTransferHelper, + "RecipientCannotBeZeroAddress" + ); + }); + }); + describe("Multi-recipient tests", async () => { + it("Executes transfers with multiple recipients (many token types) with a conduit", async () => { + const numTransfers = 4; + + // Get 3 Numbers that's value adds to Item Amount and minimum 1. + const itemsToCreate = 10; + const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); + const numERC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); + const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numERC721s); + + const erc20Contracts = []; + const erc20Transfers = []; + + const erc721Contracts = []; + const erc721Transfers = []; + + const erc1155Contracts = []; + const erc1155Transfers = []; + + const recipients = [ + recipient.address, + alice.address, + bob.address, + cal.address, + ]; + + const transfersWithRecipients = []; + const allContracts = []; + + // Create numTransfers amount of TransferHelperItemsWithRecipient + for (let j = 0; j < numTransfers; j++) { + const transferRecipient = recipients[j]; + + // Create numERC20s amount of ERC20 objects + for (let i = 0; i < numERC20s; i++) { + // Deploy Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + // Create/Approve X amount of ERC20s + const erc20Transfer = await createTransferWithApproval( + tempERC20Contract, + sender, + 1, + tempConduit.address, + sender.address, + transferRecipient + ); + erc20Contracts[i] = tempERC20Contract; + erc20Transfers[i] = erc20Transfer; + } + + // Create numERC721s amount of ERC721 objects + for (let i = 0; i < numERC721s; i++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempConduit.address, + sender.address, + transferRecipient + ); + erc721Contracts[i] = tempERC721Contract; + erc721Transfers[i] = erc721Transfer; + } + + // Create numERC1155s amount of ERC1155 objects + for (let i = 0; i < numERC1155s; i++) { + // Deploy Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155( + owner + ); + // Create/Approve numERC1155s amount of ERC1155s + const erc1155Transfer = await createTransferWithApproval( + tempERC1155Contract, + sender, + 3, + tempConduit.address, + sender.address, + transferRecipient + ); + + erc1155Contracts[i] = tempERC1155Contract; + erc1155Transfers[i] = erc1155Transfer; + } + + const transfers = [ + ...erc20Transfers, + ...erc721Transfers, + ...erc1155Transfers, + ]; + + allContracts.push( + ...erc20Contracts, + ...erc721Contracts, + ...erc1155Contracts + ); + + transfersWithRecipients[j] = createTransferWithRecipient( + transfers, + transferRecipient, + true + ); + } + + // Send the bulk transfers + await tempTransferHelper + .connect(sender) + .bulkTransfer(transfersWithRecipients, tempConduitKey); + + let contractsStartingIndex = 0; + // Loop through all transfer to do ownership/balance checks + for (let i = 0; i < transfersWithRecipients.length; i++) { + const items = transfersWithRecipients[i].items; + + for (let j = 0; j < items.length; j++) { + // Get Itemtype, token, amount, identifier + const { itemType, amount, identifier } = items[j]; + const token = allContracts[contractsStartingIndex]; + + switch (itemType) { + case 1: // ERC20 + // Check balance + expect( + await (token as typeof erc20Contracts[0]).balanceOf( + sender.address + ) + ).to.equal(0); + expect( + await (token as typeof erc20Contracts[0]).balanceOf( + transfersWithRecipients[i].recipient + ) + ).to.equal(amount); + break; + case 2: // ERC721 + case 4: // ERC721_WITH_CRITERIA + expect( + await (token as typeof erc721Contracts[0]).ownerOf(identifier) + ).to.equal(transfersWithRecipients[i].recipient); + break; + case 3: // ERC1155 + case 5: // ERC1155_WITH_CRITERIA + // Check balance + expect( + await token.balanceOf(sender.address, identifier) + ).to.equal(0); + expect( + await token.balanceOf( + transfersWithRecipients[i].recipient, + identifier + ) + ).to.equal(amount); + break; + } + contractsStartingIndex++; + } + } + }); + + it("Cannot execute transfers with multiple recipients (many token types) without a conduit", async () => { + const numTransfers = 4; + + // Get 3 Numbers that's value adds to Item Amount and minimum 1. + const itemsToCreate = 10; + const numERC20s = Math.max(1, randomInt(itemsToCreate - 2)); + const numERC721s = Math.max(1, randomInt(itemsToCreate - numERC20s - 1)); + const numERC1155s = Math.max(1, itemsToCreate - numERC20s - numERC721s); + + const erc20Contracts = []; + const erc20Transfers = []; + + const erc721Contracts = []; + const erc721Transfers = []; + + const erc1155Contracts = []; + const erc1155Transfers = []; + + const recipients = [ + recipient.address, + alice.address, + bob.address, + cal.address, + ]; + + const transfersWithRecipientsNoConduit = []; + const allContracts = []; + + // Create numTransfers amount of TransferHelperItemsWithRecipient + for (let j = 0; j < numTransfers; j++) { + const transferRecipient = recipients[j]; + + // Create numERC20s amount of ERC20 objects + for (let i = 0; i < numERC20s; i++) { + // Deploy Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + // Create/Approve X amount of ERC20s + const erc20Transfer = await createTransferWithApproval( + tempERC20Contract, + sender, + 1, + tempTransferHelper.address, + sender.address, + transferRecipient + ); + erc20Contracts[i] = tempERC20Contract; + erc20Transfers[i] = erc20Transfer; + } + + // Create numERC721s amount of ERC721 objects + for (let i = 0; i < numERC721s; i++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempTransferHelper.address, + sender.address, + transferRecipient + ); + erc721Contracts[i] = tempERC721Contract; + erc721Transfers[i] = erc721Transfer; + } + + // Create numERC1155s amount of ERC1155 objects + for (let i = 0; i < numERC1155s; i++) { + // Deploy Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155( + owner + ); + // Create/Approve numERC1155s amount of ERC1155s + const erc1155Transfer = await createTransferWithApproval( + tempERC1155Contract, + sender, + 3, + tempTransferHelper.address, + sender.address, + transferRecipient + ); + + erc1155Contracts[i] = tempERC1155Contract; + erc1155Transfers[i] = erc1155Transfer; + } + + const transfers = [ + ...erc20Transfers, + ...erc721Transfers, + ...erc1155Transfers, + ]; + + allContracts.push( + ...erc20Contracts, + ...erc721Contracts, + ...erc1155Contracts + ); + + transfersWithRecipientsNoConduit[j] = createTransferWithRecipient( + transfers, + transferRecipient, + true + ); + } + + // Sending the bulk transfers with no conduit reverts + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer( + transfersWithRecipientsNoConduit, + ethers.utils.formatBytes32String("") + ) + ) + .to.be.revertedWithCustomError(tempTransferHelper, "InvalidConduit") + .withArgs(ethers.constants.HashZero, ethers.constants.AddressZero); + }); + + it("Cannot execute ERC721 transfers to multiple contract recipients without a conduit", async () => { + // Deploy recipient contract + const erc721RecipientFactory = await ethers.getContractFactory( + "ERC721ReceiverMock" + ); + const erc721RecipientOne = await erc721RecipientFactory.deploy( + Buffer.from("150b7a02", "hex"), + 0 + ); + + const erc721RecipientTwo = await erc721RecipientFactory.deploy( + Buffer.from("150b7a02", "hex"), + 0 + ); + + const erc721RecipientThree = await erc721RecipientFactory.deploy( + Buffer.from("150b7a02", "hex"), + 0 + ); + + const erc721RecipientFour = await erc721RecipientFactory.deploy( + Buffer.from("150b7a02", "hex"), + 0 + ); + + const erc721RecipientFive = await erc721RecipientFactory.deploy( + Buffer.from("150b7a02", "hex"), + 0 + ); + + const erc721Recipients = [ + erc721RecipientOne, + erc721RecipientTwo, + erc721RecipientThree, + erc721RecipientFour, + erc721RecipientFive, + ]; + + const numTransfers = 5; + const transfersWithRecipients = []; + + const allContracts = []; + + for (let i = 0; i < numTransfers; i++) { + const erc721Items = []; + const erc721Contracts = []; + + // Create 5 ERC721 items + for (let j = 0; j < 5; j++) { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempTransferHelper.address, + sender.address, + erc721Recipients[j].address + ); + + erc721Contracts[j] = tempERC721Contract; + erc721Items[j] = erc721Transfer; + } + transfersWithRecipients[i] = createTransferWithRecipient( + erc721Items, + erc721Recipients[i].address, + true + ); + + allContracts.push(...erc721Contracts); + } + + // Sending the bulk transfers with no conduit reverts + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer( + transfersWithRecipients, + ethers.utils.formatBytes32String("") + ) + ) + .to.be.revertedWithCustomError(tempTransferHelper, "InvalidConduit") + .withArgs(ethers.constants.HashZero, ethers.constants.AddressZero); + }); + + it("Reverts on native token transfers", async () => { + const ethTransferHelperItems = [ + { + items: [ + { + itemType: 0, + token: ethers.constants.AddressZero, + identifier: 0, + amount: 10, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 0, + token: ethers.constants.AddressZero, + identifier: 0, + amount: 20, + }, + ], + recipient: bob.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(ethTransferHelperItems, tempConduitKey) + ).to.be.revertedWithCustomError(tempTransferHelper, "InvalidItemType"); + }); + + it("Reverts on invalid ERC20 identifier", async () => { + const erc20TransferHelperItems = [ + { + items: [ + { + itemType: 1, + token: ethers.constants.AddressZero, + identifier: 5, + amount: 10, + }, + ], + recipient: alice.address, + validateERC721Receiver: false, + }, + { + items: [ + { + itemType: 1, + token: ethers.constants.AddressZero, + identifier: 4, + amount: 20, + }, + ], + recipient: bob.address, + validateERC721Receiver: false, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc20TransferHelperItems, tempConduitKey) + ).to.be.revertedWithCustomError( + tempTransferHelper, + "InvalidERC20Identifier" + ); + }); + + it("Reverts on invalid ERC721 transfer amount", async () => { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + + const erc721TransferHelperItems = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 10, + }, + ], + recipient: alice.address, + validateERC721Receiver: false, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 20, + }, + ], + recipient: bob.address, + validateERC721Receiver: false, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc721TransferHelperItems, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "InvalidERC721TransferAmount" + ) + .withArgs(10); + }); + + it("Successful ERC721 receiver contract", async () => { + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + + // Will accept ERC721 transfers + const mockERC721ReceiverFactory = await ethers.getContractFactory( + "ERC721ReceiverMock" + ); + const erc721Receiver = await mockERC721ReceiverFactory.deploy( + "0x150b7a02", // Magic value + 0 + ); + + // Create/Approve numERC721s amount of ERC721s + const erc721Transfer = await createTransferWithApproval( + tempERC721Contract, + sender, + 2, + tempConduit.address, + sender.address, + erc721Receiver.address + ); + + const erc721TransferHelperItems = [ + { + items: [erc721Transfer], + recipient: erc721Receiver.address, + validateERC721Receiver: true, + }, + ]; + + await tempTransferHelper + .connect(sender) + .bulkTransfer(erc721TransferHelperItems, tempConduitKey); + }); + + it("Reverts on invalid ERC721 recipient", async () => { + // Deploy Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + const { testERC721: tempERC721ContractTwo } = await fixtureERC721(owner); + + const erc721TransferHelperItems = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + ], + recipient: tempERC721Contract.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 2, + token: tempERC721ContractTwo.address, + identifier: 2, + amount: 1, + }, + ], + recipient: tempERC721Contract.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc721TransferHelperItems, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ERC721ReceiverErrorRevertBytes" + ) + .withArgs("0x", tempERC721Contract.address, sender.address, 1); + }); + + it("Reverts on invalid function selector", async () => { + const invalidRecipientFactory = await ethers.getContractFactory( + "InvalidERC721Recipient" + ); + const invalidRecipient = await invalidRecipientFactory.deploy(); + const invalidRecipientTwo = await invalidRecipientFactory.deploy(); + + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + + const erc721TransferHelperItems = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + ], + recipient: invalidRecipient.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: invalidRecipientTwo.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(erc721TransferHelperItems, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "InvalidERC721Recipient" + ) + .withArgs(invalidRecipient.address); + }); + + it("Reverts on nonexistent conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + const transferHelperItems = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + ], + recipient: alice.address, + validateERC721Receiver: false, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: bob.address, + validateERC721Receiver: false, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: cal.address, + validateERC721Receiver: false, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: alice.address, + validateERC721Receiver: false, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer( + transferHelperItems, + ethers.utils.formatBytes32String("0xabc") + ) + ).to.be.reverted; + }); + + it("Reverts on error in ERC721 receiver", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + // Deploy mock ERC721 receiver + const mockERC721ReceiverFactory = await ethers.getContractFactory( + "ERC721ReceiverMock" + ); + const mockERC721ReceiverOne = await mockERC721ReceiverFactory.deploy( + Buffer.from("abcd0000", "hex"), + 1 + ); + const mockERC721ReceiverTwo = await mockERC721ReceiverFactory.deploy( + Buffer.from("abcd6969", "hex"), + 1 + ); + const mockERC721ReceiverThree = await mockERC721ReceiverFactory.deploy( + Buffer.from("42069abc", "hex"), + 1 + ); + const mockERC721ReceiverFour = await mockERC721ReceiverFactory.deploy( + Buffer.from("abc42069", "hex"), + 1 + ); + + const transferHelperItems = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + ], + recipient: mockERC721ReceiverOne.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: mockERC721ReceiverTwo.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: mockERC721ReceiverThree.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: mockERC721ReceiverFour.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transferHelperItems, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ERC721ReceiverErrorRevertString" + ) + .withArgs( + "ERC721ReceiverMock: reverting", + mockERC721ReceiverOne.address, + sender.address, + 1 + ); + }); + + it("Reverts with custom error in conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + const transferHelperItems = [ + // Invalid item type + { + items: [ + { + itemType: 0, + token: ethers.constants.AddressZero, + identifier: 1, + amount: 1, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: bob.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: cal.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transferHelperItems, tempConduitKey) + ).to.be.revertedWithCustomError(tempTransferHelper, "InvalidItemType"); + }); + + it("Reverts with bubbled up string error from call to conduit", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + // Call will revert since ERC721 tokens have not been minted + const transferHelperItems = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + ], + recipient: bob.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: cal.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transferHelperItems, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ConduitErrorRevertString" + ) + .withArgs( + "WRONG_FROM", + tempConduitKey.toLowerCase(), + tempConduit.address + ); + }); + + it("Reverts when no revert string is returned from call to conduit", async () => { + // Deploy ERC1155 Contract + const { testERC1155: tempERC1155Contract } = await fixtureERC1155(owner); + + await tempERC1155Contract.connect(owner).mint(sender.address, 0, 100); + + const mockConduitControllerFactory = await ethers.getContractFactory( + "ConduitControllerMock" + ); + const mockConduitController = await mockConduitControllerFactory.deploy( + 1 // ConduitMockRevertNoReason + ); + + const mockTransferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + const mockTransferHelper = await mockTransferHelperFactory.deploy( + mockConduitController.address + ); + const mockConduitKey = owner.address + randomHex(12).slice(2); + + // Deploy the mock conduit through the mock conduit controller + await mockConduitController + .connect(owner) + .createConduit(mockConduitKey, owner.address); + + const mockConduitAddress = ( + await mockConduitController.getConduit(mockConduitKey) + )[0]; + + await tempERC1155Contract + .connect(sender) + .setApprovalForAll(mockConduitAddress, true); + + const transfers = [ + { + items: [ + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: recipient.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 3, + token: tempERC1155Contract.address, + identifier: 0, + amount: 10, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + mockTransferHelper + .connect(sender) + .bulkTransfer(transfers, mockConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ConduitErrorRevertBytes" + ) + .withArgs("0x", mockConduitKey.toLowerCase(), mockConduitAddress); + }); + + it("Reverts with bubbled up panic error from call to conduit", async () => { + // Deploy mock ERC20 + const mockERC20PanicFactory = await ethers.getContractFactory( + "TestERC20Panic" + ); + const mockERC20Panic = await mockERC20PanicFactory.deploy(); + + const transfers = [ + { + items: [ + { + itemType: 1, + token: mockERC20Panic.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: mockERC20Panic.address, + identifier: 0, + amount: 20, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: mockERC20Panic.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: mockERC20Panic.address, + identifier: 0, + amount: 20, + }, + ], + recipient: bob.address, + validateERC721Receiver: true, + }, + ]; + + const panicError = + "0x4e487b710000000000000000000000000000000000000000000000000000000000000012"; + + if (!process.env.REFERENCE) { + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ) + .to.be.revertedWithCustomError( + tempTransferHelper, + "ConduitErrorRevertBytes" + ) + .withArgs( + panicError, + tempConduitKey.toLowerCase(), + tempConduit.address + ); + } else { + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ).to.be.reverted; + } + }); + + it("Reverts with invalid magic value returned by call to conduit", async () => { + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + await tempERC20Contract.connect(owner).mint(sender.address, 100); + + const mockConduitControllerFactory = await ethers.getContractFactory( + "ConduitControllerMock" + ); + const mockConduitController = await mockConduitControllerFactory.deploy( + 2 // ConduitMockInvalidMagic + ); + + const mockTransferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + const mockTransferHelper = await mockTransferHelperFactory.deploy( + mockConduitController.address + ); + const mockConduitKey = owner.address + randomHex(12).slice(2); + + // Deploy the mock conduit through the mock conduit controller + await mockConduitController + .connect(owner) + .createConduit(mockConduitKey, owner.address); + + const mockConduitAddress = ( + await mockConduitController.getConduit(mockConduitKey) + )[0]; + + await tempERC20Contract.connect(sender).approve(mockConduitAddress, 100); + + const transfers = [ + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: bob.address, + validateERC721Receiver: true, + }, + ]; + + await expect( + mockTransferHelper + .connect(sender) + .bulkTransfer(transfers, mockConduitKey) + ) + .to.be.revertedWithCustomError(mockTransferHelper, "InvalidConduit") + .withArgs(mockConduitKey.toLowerCase(), mockConduitAddress); + }); + + it("Reverts with conduit revert data", async () => { + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + await tempERC20Contract.connect(owner).mint(sender.address, 100); + + const mockConduitControllerFactory = await ethers.getContractFactory( + "ConduitControllerMock" + ); + const mockConduitController = await mockConduitControllerFactory.deploy( + 3 // ConduitMockRevertBytes + ); + + const mockTransferHelperFactory = await ethers.getContractFactory( + "TransferHelper" + ); + const mockTransferHelper = await mockTransferHelperFactory.deploy( + mockConduitController.address + ); + const mockConduitKey = owner.address + randomHex(12).slice(2); + + // Deploy the mock conduit through the mock conduit controller + await mockConduitController + .connect(owner) + .createConduit(mockConduitKey, owner.address); + + const mockConduitAddress = ( + await mockConduitController.getConduit(mockConduitKey) + )[0]; + await tempERC20Contract.connect(sender).approve(mockConduitAddress, 100); + + const transfers = [ + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: alice.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: bob.address, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: cal.address, + validateERC721Receiver: true, + }, + ]; + + const customErrorSelector = ethers.utils.id("CustomError()").slice(0, 10); + + await expect( + mockTransferHelper + .connect(sender) + .bulkTransfer(transfers, mockConduitKey) + ) + .to.be.revertedWithCustomError( + mockTransferHelper, + "ConduitErrorRevertBytes" + ) + .withArgs( + customErrorSelector, + mockConduitKey.toLowerCase(), + mockConduitAddress + ); + }); + + it("Reverts when recipient is the null address (with conduit)", async () => { + // Deploy ERC721 Contract + const { testERC721: tempERC721Contract } = await fixtureERC721(owner); + // Deploy ERC20 Contract + const { testERC20: tempERC20Contract } = await fixtureERC20(owner); + + const transfers = [ + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: ethers.constants.AddressZero, + validateERC721Receiver: true, + }, + { + items: [ + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 1, + amount: 1, + }, + { + itemType: 2, + token: tempERC721Contract.address, + identifier: 2, + amount: 1, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 10, + }, + { + itemType: 1, + token: tempERC20Contract.address, + identifier: 0, + amount: 20, + }, + ], + recipient: ethers.constants.AddressZero, + validateERC721Receiver: true, + }, + ]; + await expect( + tempTransferHelper + .connect(sender) + .bulkTransfer(transfers, tempConduitKey) + ).to.be.revertedWithCustomError( + tempTransferHelper, + "RecipientCannotBeZeroAddress" + ); + }); + }); +}); diff --git a/test/typehashdirectory.spec.ts b/test/typehashdirectory.spec.ts new file mode 100644 index 000000000..f6b9d4451 --- /dev/null +++ b/test/typehashdirectory.spec.ts @@ -0,0 +1,23 @@ +import { expect } from "chai"; +import { hexConcat } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { deployContract } from "./utils/contracts"; +import { getBulkOrderTypeHashes } from "./utils/eip712/bulk-orders"; + +// Reference contracts use storage for type hashes, not +// a lookup contract. +if (!process.env.REFERENCE) { + describe("TypehashDirectory", () => { + let address: string; + before(async () => { + address = (await deployContract("TypehashDirectory")).address; + }); + + it("Code is equal to concatenated type hashes for heights 1-24", async () => { + const code = await ethers.provider.getCode(address); + const typeHashes = getBulkOrderTypeHashes(24); + expect(code).to.eq(hexConcat(["0xfe", ...typeHashes])); + }); + }); +} diff --git a/test/utils/contracts.ts b/test/utils/contracts.ts index 8eefbc74e..73753cdb1 100644 --- a/test/utils/contracts.ts +++ b/test/utils/contracts.ts @@ -1,15 +1,18 @@ import { ethers } from "hardhat"; -import { Contract } from "ethers"; -import { JsonRpcSigner } from "@ethersproject/providers"; -import * as dotenv from "dotenv"; -dotenv.config(); +import type { JsonRpcSigner } from "@ethersproject/providers"; +import type { Contract, Wallet } from "ethers"; + +import "dotenv/config"; export const deployContract = async ( name: string, - signer: JsonRpcSigner, + signer?: JsonRpcSigner | Wallet, ...args: any[] ): Promise => { + if (!signer) { + signer = await ethers.provider.getSigner(0); + } const references = new Map([ ["Consideration", "ReferenceConsideration"], ["Conduit", "ReferenceConduit"], @@ -18,7 +21,7 @@ export const deployContract = async ( const nameWithReference = process.env.REFERENCE && references.has(name) - ? references.get(name) || name + ? references.get(name) ?? name : name; const f = await ethers.getContractFactory(nameWithReference, signer); diff --git a/test/utils/criteria.js b/test/utils/criteria.ts similarity index 57% rename from test/utils/criteria.js rename to test/utils/criteria.ts index 56149f6d5..b34dc536b 100644 --- a/test/utils/criteria.js +++ b/test/utils/criteria.ts @@ -1,7 +1,10 @@ -const { ethers } = require("ethers"); -const { bufferToHex, keccak256 } = require("ethereumjs-util"); +import { ethers } from "ethers"; -const merkleTree = (tokenIds) => { +const { keccak256 } = ethers.utils; + +type BufferElementPositionIndex = { [key: string]: number }; + +export const merkleTree = (tokenIds: ethers.BigNumber[]) => { const elements = tokenIds .map((tokenId) => Buffer.from(tokenId.toHexString().slice(2).padStart(64, "0"), "hex") @@ -11,19 +14,22 @@ const merkleTree = (tokenIds) => { return idx === 0 || !arr[idx - 1].equals(el); }); - const bufferElementPositionIndex = elements.reduce((memo, el, index) => { - memo[bufferToHex(el)] = index; - return memo; - }, {}); + const bufferElementPositionIndex = elements.reduce( + (memo: BufferElementPositionIndex, el, index) => { + memo["0x" + el.toString("hex")] = index; + return memo; + }, + {} + ); // Create layers const layers = getLayers(elements); - const root = bufferToHex(layers[layers.length - 1][0]); + const root = "0x" + layers[layers.length - 1][0].toString("hex"); const proofs = Object.fromEntries( elements.map((el) => [ - ethers.BigNumber.from("0x" + el.toString("hex")).toString(), + ethers.BigNumber.from(el).toString(), getHexProof(el, bufferElementPositionIndex, layers), ]) ); @@ -39,13 +45,13 @@ const merkleTree = (tokenIds) => { }; }; -const getLayers = (elements) => { +const getLayers = (elements: Buffer[]) => { if (elements.length === 0) { throw new Error("empty tree"); } const layers = []; - layers.push(elements.map((el) => keccak256(el))); + layers.push(elements.map((el) => Buffer.from(keccak256(el).slice(2), "hex"))); // Get next layer until we reach the root while (layers[layers.length - 1].length > 1) { @@ -55,8 +61,8 @@ const getLayers = (elements) => { return layers; }; -const getNextLayer = (elements) => { - return elements.reduce((layer, el, idx, arr) => { +const getNextLayer = (elements: Buffer[]) => { + return elements.reduce((layer: Buffer[], el, idx, arr) => { if (idx % 2 === 0) { // Hash the current element with its pair element layer.push(combinedHash(el, arr[idx + 1])); @@ -66,7 +72,7 @@ const getNextLayer = (elements) => { }, []); }; -const combinedHash = (first, second) => { +const combinedHash = (first: Buffer, second: Buffer) => { if (!first) { return second; } @@ -74,17 +80,24 @@ const combinedHash = (first, second) => { return first; } - return keccak256(Buffer.concat([first, second].sort(Buffer.compare))); + return Buffer.from( + keccak256(Buffer.concat([first, second].sort(Buffer.compare))).slice(2), + "hex" + ); }; -const getHexProof = (el, bufferElementPositionIndex, layers) => { - let idx = bufferElementPositionIndex[bufferToHex(el)]; +const getHexProof = ( + el: Buffer, + bufferElementPositionIndex: BufferElementPositionIndex, + layers: Buffer[][] +) => { + let idx = bufferElementPositionIndex["0x" + el.toString("hex")]; if (typeof idx !== "number") { throw new Error("Element does not exist in Merkle tree"); } - const proofBuffer = layers.reduce((proof, layer) => { + const proofBuffer = layers.reduce((proof: Buffer[], layer) => { const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1; const pairElement = pairIdx < layer.length ? layer[pairIdx] : null; @@ -99,7 +112,3 @@ const getHexProof = (el, bufferElementPositionIndex, layers) => { return proofBuffer.map((el) => "0x" + el.toString("hex")); }; - -module.exports = Object.freeze({ - merkleTree, -}); diff --git a/test/utils/eip712/Eip712MerkleTree.ts b/test/utils/eip712/Eip712MerkleTree.ts new file mode 100644 index 000000000..bc1e59954 --- /dev/null +++ b/test/utils/eip712/Eip712MerkleTree.ts @@ -0,0 +1,130 @@ +import { _TypedDataEncoder as TypedDataEncoder } from "@ethersproject/hash"; +import { expect } from "chai"; +import { + defaultAbiCoder, + hexConcat, + keccak256, + toUtf8Bytes, +} from "ethers/lib/utils"; +import { MerkleTree } from "merkletreejs"; + +import { DefaultGetter } from "./defaults"; +import { + bufferKeccak, + bufferToHex, + chunk, + fillArray, + getRoot, + hexToBuffer, +} from "./utils"; + +import type { OrderComponents } from "../types"; +import type { EIP712TypeDefinitions } from "./defaults"; + +type BulkOrderElements = + | [OrderComponents, OrderComponents] + | [BulkOrderElements, BulkOrderElements]; + +const getTree = (leaves: string[], defaultLeafHash: string) => + new MerkleTree(leaves.map(hexToBuffer), bufferKeccak, { + complete: true, + sort: false, + hashLeaves: false, + fillDefaultHash: hexToBuffer(defaultLeafHash), + }); + +const encodeProof = ( + key: number, + proof: string[], + signature = `0x${"ff".repeat(64)}` +) => { + return hexConcat([ + signature, + `0x${key.toString(16).padStart(6, "0")}`, + defaultAbiCoder.encode([`uint256[${proof.length}]`], [proof]), + ]); +}; + +export class Eip712MerkleTree = any> { + tree: MerkleTree; + private leafHasher: (value: any) => string; + defaultNode: BaseType; + defaultLeaf: string; + encoder: TypedDataEncoder; + + get completedSize() { + return Math.pow(2, this.depth); + } + + /** Returns the array of elements in the tree, padded to the complete size with empty items. */ + getCompleteElements() { + const elements = this.elements; + return fillArray([...elements], this.completedSize, this.defaultNode); + } + + /** Returns the array of leaf nodes in the tree, padded to the complete size with default hashes. */ + getCompleteLeaves() { + const leaves = this.elements.map(this.leafHasher); + return fillArray([...leaves], this.completedSize, this.defaultLeaf); + } + + get root() { + return this.tree.getHexRoot(); + } + + getProof(i: number) { + const leaves = this.getCompleteLeaves(); + const leaf = leaves[i]; + const proof = this.tree.getHexProof(leaf, i); + const root = this.tree.getHexRoot(); + return { leaf, proof, root }; + } + + getEncodedProofAndSignature(i: number, signature: string) { + const { proof } = this.getProof(i); + return encodeProof(i, proof, signature); + } + + getDataToSign(): BulkOrderElements { + let layer = this.getCompleteElements() as any; + while (layer.length > 2) { + layer = chunk(layer, 2); + } + return layer; + } + + add(element: BaseType) { + this.elements.push(element); + } + + getBulkOrderHash() { + const structHash = this.encoder.hashStruct("BulkOrder", { + tree: this.getDataToSign(), + }); + const leaves = this.getCompleteLeaves().map(hexToBuffer); + const rootHash = bufferToHex(getRoot(leaves, false)); + const typeHash = keccak256(toUtf8Bytes(this.encoder._types.BulkOrder)); + const bulkOrderHash = keccak256(hexConcat([typeHash, rootHash])); + + expect(bulkOrderHash, "derived bulk order hash should match").to.equal( + structHash + ); + + return structHash; + } + + constructor( + public types: EIP712TypeDefinitions, + public rootType: string, + public leafType: string, + public elements: BaseType[], + public depth: number + ) { + const encoder = TypedDataEncoder.from(types); + this.encoder = encoder; + this.leafHasher = (leaf: BaseType) => encoder.hashStruct(leafType, leaf); + this.defaultNode = DefaultGetter.from(types, leafType); + this.defaultLeaf = this.leafHasher(this.defaultNode); + this.tree = getTree(this.getCompleteLeaves(), this.defaultLeaf); + } +} diff --git a/test/utils/eip712/bulk-orders.ts b/test/utils/eip712/bulk-orders.ts new file mode 100644 index 000000000..20ee894e1 --- /dev/null +++ b/test/utils/eip712/bulk-orders.ts @@ -0,0 +1,62 @@ +import { _TypedDataEncoder, keccak256, toUtf8Bytes } from "ethers/lib/utils"; + +import { Eip712MerkleTree } from "./Eip712MerkleTree"; +import { DefaultGetter } from "./defaults"; +import { fillArray } from "./utils"; + +import type { OrderComponents } from "../types"; +import type { EIP712TypeDefinitions } from "./defaults"; + +const { bulkOrderType } = require("../../../eip-712-types/bulkOrder.js"); + +function getBulkOrderTypes(height: number): EIP712TypeDefinitions { + const types = { ...bulkOrderType }; + types.BulkOrder = [ + { name: "tree", type: `OrderComponents${`[2]`.repeat(height)}` }, + ]; + return types; +} + +export function getBulkOrderTreeHeight(length: number): number { + return Math.max(Math.ceil(Math.log2(length)), 1); +} + +export function getBulkOrderTree( + orderComponents: OrderComponents[], + startIndex = 0, + height = getBulkOrderTreeHeight(orderComponents.length + startIndex) +) { + const types = getBulkOrderTypes(height); + const defaultNode = DefaultGetter.from(types, "OrderComponents"); + let elements = [...orderComponents]; + + if (startIndex > 0) { + elements = [ + ...fillArray([] as OrderComponents[], startIndex, defaultNode), + ...orderComponents, + ]; + } + const tree = new Eip712MerkleTree( + types, + "BulkOrder", + "OrderComponents", + elements, + height + ); + return tree; +} + +export function getBulkOrderTypeHash(height: number): string { + const types = getBulkOrderTypes(height); + const encoder = _TypedDataEncoder.from(types); + const typeString = toUtf8Bytes(encoder._types.BulkOrder); + return keccak256(typeString); +} + +export function getBulkOrderTypeHashes(maxHeight: number): string[] { + const typeHashes: string[] = []; + for (let i = 0; i < maxHeight; i++) { + typeHashes.push(getBulkOrderTypeHash(i + 1)); + } + return typeHashes; +} diff --git a/test/utils/eip712/code-gen.ts b/test/utils/eip712/code-gen.ts new file mode 100644 index 000000000..12e89ac98 --- /dev/null +++ b/test/utils/eip712/code-gen.ts @@ -0,0 +1,46 @@ +import { writeFileSync } from "fs"; +import path from "path"; + +import { toHex } from "../encoding"; + +const readCode = (index: number, length: number) => { + const [shiftKey, letMaybe, prevNode, sibling] = + index > 0 + ? [ + `shr(${index}, key)`, + ``, + `keccak256(0, TwoWords)`, + `add(proof, ${toHex(index * 32)})`, + ] + : [`key`, `let `, `leaf`, `proof`]; + + const getPtr = `${letMaybe}scratch := shl(5, and(${shiftKey}, 1))`; + const writePrevNode = `mstore(scratch, ${prevNode})`; + const code = [ + getPtr, + writePrevNode, + `mstore(xor(scratch, OneWord), calldataload(${sibling}))`, + ]; + if (index + 1 === length) { + code.push(`root := keccak256(0, TwoWords)`); + } + return code; +}; + +const depth = 7; + +const allCode = [ + `\tfunction _computeMerkleProofDepth${depth}(Eip712MerkleProof proofPtr, uint256 leaf) pure returns (bytes32 root) {`, + `\t\tassembly{`, + `\t\tlet key := shr(248, proofPtr)`, + `\t\tlet proof := add(proofPtr, 1)`, +]; +for (let i = 0; i < depth; i++) { + if (i === depth - 1) allCode.push("\t\t\t// Compute root hash"); + else allCode.push(`\t\t\t// Compute level ${i + 1}`); + allCode.push(...readCode(i, depth).map((ln) => "\t\t\t" + ln)); + if (i !== depth - 1) allCode.push(""); +} +allCode.push("\t\t}", "\t}"); + +writeFileSync(path.join(__dirname, "gen.sol"), allCode.join("\n")); diff --git a/test/utils/eip712/defaults.ts b/test/utils/eip712/defaults.ts new file mode 100644 index 000000000..1e24a4191 --- /dev/null +++ b/test/utils/eip712/defaults.ts @@ -0,0 +1,113 @@ +/* eslint-disable no-dupe-class-members */ +/* eslint-disable no-unused-vars */ +import { Logger } from "@ethersproject/logger"; +import { hexZeroPad } from "ethers/lib/utils"; + +import type { TypedDataField } from "@ethersproject/abstract-signer"; + +const logger = new Logger("defaults"); + +const baseDefaults: Record = { + integer: 0, + address: hexZeroPad("0x", 20), + bool: false, + bytes: "0x", + string: "", +}; + +const isNullish = (value: any): boolean => { + if (value === undefined) return false; + + return ( + value !== undefined && + value !== null && + ((["string", "number"].includes(typeof value) && + BigInt(value) === BigInt(0)) || + (Array.isArray(value) && value.every(isNullish)) || + (typeof value === "object" && Object.values(value).every(isNullish)) || + (typeof value === "boolean" && value === false)) + ); +}; + +function getDefaultForBaseType(type: string): any { + // bytesXX + const [, width] = type.match(/^bytes(\d+)$/) ?? []; + if (width) return hexZeroPad("0x", parseInt(width)); + + if (type.match(/^(u?)int(\d*)$/)) type = "integer"; + + return baseDefaults[type]; +} + +export type EIP712TypeDefinitions = Record; + +type DefaultMap = { + [K in keyof T]: any; +}; + +export class DefaultGetter { + defaultValues: DefaultMap = {} as DefaultMap; + + constructor(protected types: Types) { + for (const name in types) { + const defaultValue = this.getDefaultValue(name); + this.defaultValues[name] = defaultValue; + if (!isNullish(defaultValue)) { + logger.throwError( + `Got non-empty value for type ${name} in default generator: ${defaultValue}` + ); + } + } + } + + static from( + types: Types + ): DefaultMap; + + static from( + types: Types, + type: keyof Types + ): any; + + static from( + types: Types, + type?: keyof Types + ): DefaultMap { + const { defaultValues } = new DefaultGetter(types); + if (type) return defaultValues[type]; + return defaultValues; + } + + getDefaultValue(type: string): any { + if (this.defaultValues[type]) return this.defaultValues[type]; + // Basic type (address, bool, uint256, etc) + const basic = getDefaultForBaseType(type); + if (basic !== undefined) return basic; + + // Array + const match = type.match(/^(.*)(\x5b(\d*)\x5d)$/); + if (match) { + const subtype = match[1]; + const length = parseInt(match[3]); + if (length > 0) { + const baseValue = this.getDefaultValue(subtype); + return Array(length).fill(baseValue); + } + return []; + } + + // Struct + const fields = this.types[type]; + if (fields) { + return fields.reduce( + (obj, { name, type }) => ({ + ...obj, + [name]: this.getDefaultValue(type), + }), + {} + ); + } + + return logger.throwArgumentError(`unknown type: ${type}`, "type", type); + } +} diff --git a/test/utils/eip712/utils.ts b/test/utils/eip712/utils.ts new file mode 100644 index 000000000..4226dcac0 --- /dev/null +++ b/test/utils/eip712/utils.ts @@ -0,0 +1,54 @@ +import { hexConcat, hexlify, keccak256 } from "ethers/lib/utils"; + +import type { BytesLike } from "ethers"; + +export const makeArray = (len: number, getValue: (i: number) => T) => + Array(len) + .fill(0) + .map((_, i) => getValue(i)); + +export const chunk = (array: T[], size: number) => { + return makeArray(Math.ceil(array.length / size), (i) => + array.slice(i * size, (i + 1) * size) + ); +}; + +export const bufferToHex = (buf: Buffer) => hexlify(buf); + +export const hexToBuffer = (value: string) => + Buffer.from(value.slice(2), "hex"); + +export const bufferKeccak = (value: BytesLike) => hexToBuffer(keccak256(value)); + +export const hashConcat = (arr: BytesLike[]) => bufferKeccak(hexConcat(arr)); + +export const fillArray = (arr: T[], length: number, value: T) => { + if (length > arr.length) arr.push(...Array(length - arr.length).fill(value)); + return arr; +}; + +export const getRoot = (elements: (Buffer | string)[], hashLeaves = true) => { + if (elements.length === 0) throw new Error("empty tree"); + + const leaves = elements.map((e) => { + const leaf = Buffer.isBuffer(e) ? e : hexToBuffer(e); + return hashLeaves ? bufferKeccak(leaf) : leaf; + }); + + const layers: Buffer[][] = [leaves]; + + // Get next layer until we reach the root + while (layers[layers.length - 1].length > 1) { + layers.push(getNextLayer(layers[layers.length - 1])); + } + + return layers[layers.length - 1][0]; +}; + +export const getNextLayer = (elements: Buffer[]) => { + return chunk(elements, 2).map(hashConcat); + // return elements.reduce((layer: Buffer[], el, idx, arr) => { + // if (idx % 2 === 0) layer.push(hashConcat(el, arr[idx + 1])); + // return layer; + // }, []); +}; diff --git a/test/utils/encoding.ts b/test/utils/encoding.ts index 8bfa67c5a..0cd2d9bea 100644 --- a/test/utils/encoding.ts +++ b/test/utils/encoding.ts @@ -1,18 +1,19 @@ +import { expect } from "chai"; import { randomBytes as nodeRandomBytes } from "crypto"; -import { utils, BigNumber, constants, ContractTransaction } from "ethers"; +import { BigNumber, constants, utils } from "ethers"; import { getAddress, keccak256, toUtf8Bytes } from "ethers/lib/utils"; -import { + +import type { BasicOrderParameters, - BigNumberish, ConsiderationItem, CriteriaResolver, + Fulfillment, FulfillmentComponent, OfferItem, Order, OrderComponents, } from "./types"; - -export { BigNumberish }; +import type { BigNumberish, ContractTransaction } from "ethers"; const SeededRNG = require("./seeded-rng"); @@ -26,8 +27,6 @@ if (GAS_REPORT_MODE) { randomBytes = (n: number) => nodeRandomBytes(n).toString("hex"); } -// const randomBytes - export const randomHex = (bytes = 32) => `0x${randomBytes(bytes)}`; export const random128 = () => toBN(randomHex(16)); @@ -40,8 +39,8 @@ export const toHex = (n: BigNumberish, numBytes: number = 0) => { : typeof n === "string" ? hexRegex.test(n) ? n.replace(/0x/, "") - : (+n).toString(16) - : (+n).toString(16); + : Number(n).toString(16) + : Number(n).toString(16); return `0x${asHexString.padStart(numBytes * 2, "0")}`; }; @@ -70,9 +69,7 @@ export const convertSignatureToEIP2098 = (signature: string) => { return signature; } - if (signature.length !== 132) { - throw Error("invalid signature length (must be 64 or 65 bytes)"); - } + expect(signature.length, "signature must be 64 or 65 bytes").to.eq(132); return utils.splitSignature(signature).compact; }; @@ -80,8 +77,8 @@ export const convertSignatureToEIP2098 = (signature: string) => { export const getBasicOrderParameters = ( basicOrderRouteType: number, order: Order, - fulfillerConduitKey = false, - tips = [] + fulfillerConduitKey: string | boolean = false, + tips: { amount: BigNumber; recipient: string }[] = [] ): BasicOrderParameters => ({ offerer: order.parameters.offerer, zone: order.parameters.zone, @@ -102,7 +99,9 @@ export const getBasicOrderParameters = ( ), signature: order.signature, offererConduitKey: order.parameters.conduitKey, - fulfillerConduitKey: toKey(fulfillerConduitKey), + fulfillerConduitKey: toKey( + typeof fulfillerConduitKey === "string" ? fulfillerConduitKey : 0 + ), additionalRecipients: [ ...order.parameters.consideration .slice(1) @@ -189,10 +188,7 @@ export const toFulfillmentComponents = ( export const toFulfillment = ( offerArr: number[][], considerationsArr: number[][] -): { - offerComponents: FulfillmentComponent[]; - considerationComponents: FulfillmentComponent[]; -} => ({ +): Fulfillment => ({ offerComponents: toFulfillmentComponents(offerArr), considerationComponents: toFulfillmentComponents(considerationsArr), }); @@ -322,8 +318,8 @@ export const getBasicOrderExecutions = ( amount: offerItem.endAmount, recipient: fulfiller, }, - offerer: offerer, - conduitKey: conduitKey, + offerer, + conduitKey, }, { item: { diff --git a/test/utils/events.ts b/test/utils/events.ts new file mode 100644 index 000000000..d25b43277 --- /dev/null +++ b/test/utils/events.ts @@ -0,0 +1,49 @@ +import type { Contract, ContractTransaction } from "ethers"; + +type DecodedTransactionEvent = { + eventName: string; + data: { [key: string | number]: string | number | boolean }; +}; + +type EventDecoder = { + eventName: string; + contract: Contract; +}; + +export async function decodeEvents( + tx: ContractTransaction, + eventDecoders: EventDecoder[] +): Promise { + const receipt = await tx.wait(); + const events = receipt.events; + if (events == null) { + return []; + } + + const decodedEvents = events + .map((event) => { + for (const decoder of eventDecoders) { + // Attempt to decode each event as decoder.eventName. + // If the event is not successfully decoded (e.g. if the + // event is not an event with name decoder.eventName), + // the catch will be hit. + try { + const result = decoder.contract.interface.decodeEventLog( + decoder.eventName, + event.data, + event.topics + ); + return { + eventName: decoder.eventName, + data: result, + } as DecodedTransactionEvent; + } catch {} + } + // Event was not decoded by any decoder so return null. + return null; + }) + // Filter out all nulls so that at the end we are left with + // only successfully decoded events. + .filter(Boolean); + return decodedEvents as DecodedTransactionEvent[]; +} diff --git a/test/utils/faucet.ts b/test/utils/faucet.ts new file mode 100644 index 000000000..0b6e91f15 --- /dev/null +++ b/test/utils/faucet.ts @@ -0,0 +1,18 @@ +import { parseEther } from "@ethersproject/units"; +import { ethers } from "hardhat"; + +import { randomHex } from "./encoding"; + +import type { JsonRpcProvider } from "@ethersproject/providers"; + +const TEN_THOUSAND_ETH = parseEther("10000").toHexString().replace("0x0", "0x"); + +export const faucet = async (address: string, provider: JsonRpcProvider) => { + await provider.send("hardhat_setBalance", [address, TEN_THOUSAND_ETH]); +}; + +export const getWalletWithEther = async () => { + const wallet = new ethers.Wallet(randomHex(32), ethers.provider); + await faucet(wallet.address, ethers.provider); + return wallet; +}; diff --git a/test/utils/fixtures/conduit.ts b/test/utils/fixtures/conduit.ts index 2c4d3a42e..841af7837 100644 --- a/test/utils/fixtures/conduit.ts +++ b/test/utils/fixtures/conduit.ts @@ -1,15 +1,17 @@ -/* eslint-disable camelcase */ import { expect } from "chai"; -import { constants, Wallet } from "ethers"; +import { constants } from "ethers"; import { getCreate2Address, keccak256 } from "ethers/lib/utils"; import hre, { ethers } from "hardhat"; -import { + +import { deployContract } from "../contracts"; +import { randomHex } from "../encoding"; + +import type { ConduitControllerInterface, + Conduit__factory, ImmutableCreate2FactoryInterface, } from "../../../typechain-types"; -import { deployContract } from "../contracts"; -import { randomHex } from "../encoding"; -import { whileImpersonating } from "../impersonate"; +import type { Wallet } from "ethers"; const deployConstants = require("../../../constants/constants"); @@ -18,10 +20,12 @@ export const conduitFixture = async ( owner: Wallet ) => { let conduitController: ConduitControllerInterface; - let conduitImplementation: any; + let conduitImplementation: Conduit__factory; if (process.env.REFERENCE) { - conduitImplementation = await ethers.getContractFactory("ReferenceConduit"); - conduitController = await deployContract("ConduitController", owner as any); + conduitImplementation = (await ethers.getContractFactory( + "ReferenceConduit" + )) as Conduit__factory; + conduitController = await deployContract("ConduitController", owner); } else { conduitImplementation = await ethers.getContractFactory("Conduit"); @@ -82,23 +86,29 @@ export const conduitFixture = async ( const deployNewConduit = async (owner: Wallet, conduitKey?: string) => { // Create a conduit key with a random salt const assignedConduitKey = - conduitKey || owner.address + randomHex(12).slice(2); + conduitKey ?? owner.address + randomHex(12).slice(2); const { conduit: tempConduitAddress } = await conduitController.getConduit( assignedConduitKey ); - await whileImpersonating(owner.address, ethers.provider, async () => { + if (!process.env.REFERENCE) { await expect( conduitController .connect(owner) .createConduit(assignedConduitKey, constants.AddressZero) - ).to.be.revertedWith("InvalidInitialOwner"); + ).to.be.revertedWithCustomError(conduitController, "InvalidInitialOwner"); + } else { + await expect( + conduitController + .connect(owner) + .createConduit(assignedConduitKey, constants.AddressZero) + ).to.be.reverted; + } - await conduitController - .connect(owner) - .createConduit(assignedConduitKey, owner.address); - }); + await conduitController + .connect(owner) + .createConduit(assignedConduitKey, owner.address); const tempConduit = conduitImplementation.attach(tempConduitAddress); return tempConduit; diff --git a/test/utils/fixtures/create2.ts b/test/utils/fixtures/create2.ts index 95c0d0e49..f8908bf04 100644 --- a/test/utils/fixtures/create2.ts +++ b/test/utils/fixtures/create2.ts @@ -1,8 +1,10 @@ import { expect } from "chai"; -import { Wallet } from "ethers"; import hre, { ethers } from "hardhat"; -import { ImmutableCreate2FactoryInterface } from "../../../typechain-types"; -import { faucet } from "../impersonate"; + +import { faucet } from "../faucet"; + +import type { ImmutableCreate2FactoryInterface } from "../../../typechain-types"; +import type { Wallet } from "ethers"; const deployConstants = require("../../../constants/constants"); diff --git a/test/utils/fixtures/index.ts b/test/utils/fixtures/index.ts index 0dad7240c..8c0691303 100644 --- a/test/utils/fixtures/index.ts +++ b/test/utils/fixtures/index.ts @@ -1,22 +1,30 @@ -/* eslint-disable no-unused-expressions */ import { expect } from "chai"; -import { - BigNumber, - constants, - Contract, - ContractReceipt, - ContractTransaction, - Wallet, -} from "ethers"; +import { Contract /* , constants */ } from "ethers"; import { ethers } from "hardhat"; + import { deployContract } from "../contracts"; import { toBN } from "../encoding"; -import { AdvancedOrder, CriteriaResolver } from "../types"; + import { conduitFixture } from "./conduit"; import { create2FactoryFixture } from "./create2"; import { marketplaceFixture } from "./marketplace"; import { tokensFixture } from "./tokens"; +import type { Reenterer } from "../../../typechain-types"; +import type { + AdvancedOrder, + ConsiderationItem, + CriteriaResolver, + OfferItem, +} from "../types"; +import type { + BigNumber, + BigNumberish, + ContractReceipt, + ContractTransaction, + Wallet, +} from "ethers"; + export { conduitFixture } from "./conduit"; export { fixtureERC20, @@ -29,7 +37,7 @@ const { provider } = ethers; export const seaportFixture = async (owner: Wallet) => { const EIP1271WalletFactory = await ethers.getContractFactory("EIP1271Wallet"); - const reenterer = await deployContract("Reenterer", owner as any); + const reenterer = await deployContract("Reenterer", owner); const { chainId } = await provider.getNetwork(); const create2Factory = await create2FactoryFixture(owner); const { @@ -67,8 +75,12 @@ export const seaportFixture = async (owner: Wallet) => { marketplaceContract, directMarketplaceContract, stubZone, + postExecutionZone, + invalidContractOfferer, + invalidContractOffererRatifyOrder, domainData, signOrder, + signBulkOrder, createOrder, createMirrorBuyNowOrder, createMirrorAcceptOfferOrder, @@ -369,9 +381,17 @@ export const seaportFixture = async (owner: Wallet) => { }; const checkTransferEvent = async ( - tx: any, - item: any, - { offerer, conduitKey, target }: any + tx: ContractTransaction | Promise, + item: (OfferItem | ConsiderationItem) & { + identifier?: string; + amount?: BigNumberish; + recipient?: string; + }, + { + offerer, + conduitKey, + target, + }: { offerer: string; conduitKey: string; target: string } ) => { const { itemType, @@ -381,7 +401,7 @@ export const seaportFixture = async (owner: Wallet) => { amount, recipient, } = item; - const identifier = id1 || id2; + const identifier = id1 ?? id2; const sender = getTransferSender(offerer, conduitKey); if ([1, 2, 5].includes(itemType)) { const contract = new Contract( @@ -402,7 +422,7 @@ export const seaportFixture = async (owner: Wallet) => { }; const checkExpectedEvents = async ( - tx: Promise, + tx: Promise | ContractTransaction, receipt: ContractReceipt, orderGroups: Array<{ order: AdvancedOrder; @@ -452,6 +472,18 @@ export const seaportFixture = async (owner: Wallet) => { } } + const txMethod = (await tx).data.slice(0, 10); + const matchMethods = [ + marketplaceContract.interface.getSighash("matchOrders"), + marketplaceContract.interface.getSighash("matchAdvancedOrders"), + ]; + const isMatchMethod = matchMethods.includes(txMethod); + if (isMatchMethod) { + await expect(Promise.resolve(tx)) + .to.emit(marketplaceContract, "OrdersMatched") + .withArgs(orderGroups.map((o) => o.orderHash)); + } + for (let { order, orderHash, @@ -468,8 +500,9 @@ export const seaportFixture = async (owner: Wallet) => { const elapsed = toBN(timestamp).sub(order.parameters.startTime as any); const remaining = duration.sub(elapsed); - const marketplaceContractEvents = (receipt.events as any[]) + const marketplaceContractOrderFulfilledEvents = (receipt.events as any[]) .filter((x) => x.address === marketplaceContract.address) + .filter((x) => x.event === "OrderFulfilled") .map((x) => ({ eventName: x.event, eventSignature: x.eventSignature, @@ -493,9 +526,9 @@ export const seaportFixture = async (owner: Wallet) => { })) .filter((x) => x.orderHash === orderHash); - expect(marketplaceContractEvents.length).to.equal(1); + expect(marketplaceContractOrderFulfilledEvents.length).to.equal(1); - const event = marketplaceContractEvents[0]; + const event = marketplaceContractOrderFulfilledEvents[0]; expect(event.eventName).to.equal("OrderFulfilled"); expect(event.eventSignature).to.equal( @@ -509,10 +542,33 @@ export const seaportFixture = async (owner: Wallet) => { expect(event.zone).to.equal(order.parameters.zone); expect(event.recipient).to.equal(recipient); + const txMethod = (await tx).data.slice(0, 10); + const matchMethods = [ + marketplaceContract.interface.getSighash("matchOrders"), + marketplaceContract.interface.getSighash("matchAdvancedOrders"), + ]; + if (txMethod in matchMethods) { + const marketplaceContractOrdersMatchedEvents = (receipt.events as any[]) + .filter((x) => x.address === marketplaceContract.address) + .filter((x) => x.event === "OrdersMatched") + .map((x) => ({ + eventName: x.event, + eventSignature: x.eventSignature, + orderHashes: x.args.orderHashes, + })); + expect(marketplaceContractOrdersMatchedEvents.length).to.equal(1); + expect( + marketplaceContractOrdersMatchedEvents[0].orderHashes.length + ).to.equal(orderGroups.length); + expect( + marketplaceContractOrdersMatchedEvents[0].orderHashes + ).to.include(event.orderHash); + } + const { offerer, conduitKey, consideration, offer } = order.parameters; const compareEventItems = async ( item: any, - orderItem: any, + orderItem: OfferItem | ConsiderationItem, isConsiderationItem: boolean ) => { expect(item.itemType).to.equal( @@ -585,7 +641,7 @@ export const seaportFixture = async (owner: Wallet) => { { ...item, amount }, { offerer: receipt.from, - conduitKey: fulfillerConduitKey, + conduitKey: fulfillerConduitKey!, target: receipt.to, } ); @@ -630,33 +686,35 @@ export const seaportFixture = async (owner: Wallet) => { if (offer.itemType === 1) { // ERC20 // search for transfer - const transferLogs = (tokenEvents || []) + const transferLogs = (tokenEvents ?? []) .map((x) => testERC20.interface.parseLog(x)) .filter( (x) => x.signature === "Transfer(address,address,uint256)" && - x.args.from === event.offerer && + x.args.from === event.offerer /* && + // TODO: work out better way to check recipient with new matchOrder logic (recipient !== constants.AddressZero ? x.args.to === recipient - : true) + : true) */ ); expect(transferLogs.length).to.be.above(0); - for (const transferLog of transferLogs) { - // TODO: check each transferred amount - } + // TODO: check each transferred amount + // for (const transferLog of transferLogs) { + // } } else if (offer.itemType === 2) { // ERC721 // search for transfer - const transferLogs = (tokenEvents || []) + const transferLogs = (tokenEvents ?? []) .map((x) => testERC721.interface.parseLog(x)) .filter( (x) => x.signature === "Transfer(address,address,uint256)" && - x.args.from === event.offerer && + x.args.from === event.offerer /* && + // TODO: work out better way to check recipient with new matchOrder logic (recipient !== constants.AddressZero ? x.args.to === recipient - : true) + : true) */ ); expect(transferLogs.length).to.equal(1); @@ -666,25 +724,27 @@ export const seaportFixture = async (owner: Wallet) => { ); } else if (offer.itemType === 3) { // search for transfer - const transferLogs = (tokenEvents || []) + const transferLogs = (tokenEvents ?? []) .map((x) => testERC1155.interface.parseLog(x)) .filter( (x) => (x.signature === "TransferSingle(address,address,address,uint256,uint256)" && - x.args.from === event.offerer && + x.args.from === event.offerer) /* && + // TODO: work out better way to check recipient with new matchOrder logic (fulfiller !== constants.AddressZero ? x.args.to === fulfiller - : true)) || + : true) */ || (x.signature === "TransferBatch(address,address,address,uint256[],uint256[])" && - x.args.from === event.offerer && + x.args.from === event.offerer) /* && + // TODO: work out better way to check recipient with new matchOrder logic (fulfiller !== constants.AddressZero ? x.args.to === fulfiller - : true)) + : true) */ ); - expect(transferLogs.length > 0).to.be.true; + expect(transferLogs.length).to.be.above(0); let found = false; for (const transferLog of transferLogs) { @@ -701,6 +761,7 @@ export const seaportFixture = async (owner: Wallet) => { } } + // eslint-disable-next-line no-unused-expressions expect(found).to.be.true; } } @@ -722,7 +783,7 @@ export const seaportFixture = async (owner: Wallet) => { if (consideration.itemType === 1) { // ERC20 // search for transfer - const transferLogs = (tokenEvents || []) + const transferLogs = (tokenEvents ?? []) .map((x) => testERC20.interface.parseLog(x)) .filter( (x) => @@ -731,14 +792,13 @@ export const seaportFixture = async (owner: Wallet) => { ); expect(transferLogs.length).to.be.above(0); - for (const transferLog of transferLogs) { - // TODO: check each transferred amount - } + // TODO: check each transferred amount + // for (const transferLog of transferLogs) { + // } } else if (consideration.itemType === 2) { // ERC721 // search for transfer - - const transferLogs = (tokenEvents || []) + const transferLogs = (tokenEvents ?? []) .map((x) => testERC721.interface.parseLog(x)) .filter( (x) => @@ -753,7 +813,7 @@ export const seaportFixture = async (owner: Wallet) => { ); } else if (consideration.itemType === 3) { // search for transfer - const transferLogs = (tokenEvents || []) + const transferLogs = (tokenEvents ?? []) .map((x) => testERC1155.interface.parseLog(x)) .filter( (x) => @@ -765,7 +825,7 @@ export const seaportFixture = async (owner: Wallet) => { x.args.to === consideration.recipient) ); - expect(transferLogs.length > 0).to.be.true; + expect(transferLogs.length).to.be.above(0); let found = false; for (const transferLog of transferLogs) { @@ -783,6 +843,7 @@ export const seaportFixture = async (owner: Wallet) => { } } + // eslint-disable-next-line no-unused-expressions expect(found).to.be.true; } } @@ -821,8 +882,12 @@ export const seaportFixture = async (owner: Wallet) => { marketplaceContract, directMarketplaceContract, stubZone, + postExecutionZone, + invalidContractOfferer, + invalidContractOffererRatifyOrder, domainData, signOrder, + signBulkOrder, createOrder, createMirrorBuyNowOrder, createMirrorAcceptOfferOrder, diff --git a/test/utils/fixtures/marketplace.ts b/test/utils/fixtures/marketplace.ts index fa1527b73..bea064ab9 100644 --- a/test/utils/fixtures/marketplace.ts +++ b/test/utils/fixtures/marketplace.ts @@ -1,33 +1,40 @@ import { expect } from "chai"; -import { constants, Wallet } from "ethers"; +import { constants } from "ethers"; import { keccak256, recoverAddress } from "ethers/lib/utils"; import hre, { ethers } from "hardhat"; -import { - ConduitInterface, - ConduitControllerInterface, - ImmutableCreate2FactoryInterface, - ConsiderationInterface, - TestZone, -} from "../../../typechain-types"; + import { deployContract } from "../contracts"; +import { getBulkOrderTree } from "../eip712/bulk-orders"; import { calculateOrderHash, convertSignatureToEIP2098, randomHex, toBN, } from "../encoding"; -import { +import { VERSION } from "../helpers"; + +import type { + ConduitControllerInterface, + ConduitInterface, + ConsiderationInterface, + ImmutableCreate2FactoryInterface, + TestInvalidContractOfferer, + TestInvalidContractOffererRatifyOrder, + TestPostExecution, + TestZone, +} from "../../../typechain-types"; +import type { AdvancedOrder, ConsiderationItem, CriteriaResolver, OfferItem, OrderComponents, } from "../types"; +import type { Contract, Wallet } from "ethers"; -const { orderType } = require("../../../eip-712-types/order"); const deployConstants = require("../../../constants/constants"); - -const VERSION = !process.env.REFERENCE ? "1.1" : "rc.1.1"; +// const { bulkOrderType } = require("../../../eip-712-types/bulkOrder"); +const { orderType } = require("../../../eip-712-types/order"); export const marketplaceFixture = async ( create2Factory: ImmutableCreate2FactoryInterface, @@ -41,11 +48,12 @@ export const marketplaceFixture = async ( process.env.REFERENCE ? "ReferenceConsideration" : "Seaport" ); - const directMarketplaceContract = await deployContract( - process.env.REFERENCE ? "ReferenceConsideration" : "Consideration", - owner as any, - conduitController.address - ); + const directMarketplaceContract = + await deployContract( + process.env.REFERENCE ? "ReferenceConsideration" : "Consideration", + owner, + conduitController.address + ); const marketplaceContractAddress = await create2Factory.findCreate2Address( deployConstants.MARKETPLACE_CONTRACT_CREATION_SALT, @@ -78,20 +86,36 @@ export const marketplaceFixture = async ( .connect(owner) .updateChannel(conduitOne.address, marketplaceContract.address, true); - const stubZone: TestZone = await deployContract("TestZone", owner as any); + const stubZone = await deployContract("TestZone", owner); + const postExecutionZone = await deployContract( + "TestPostExecution", + owner + ); + + const invalidContractOfferer = + await deployContract( + "TestInvalidContractOfferer", + owner, + marketplaceContractAddress + ); + + const invalidContractOffererRatifyOrder = + await deployContract( + "TestInvalidContractOffererRatifyOrder", + owner, + marketplaceContractAddress + ); // Required for EIP712 signing const domainData = { name: process.env.REFERENCE ? "Consideration" : "Seaport", version: VERSION, - chainId: chainId, + chainId, verifyingContract: marketplaceContract.address, }; const getAndVerifyOrderHash = async (orderComponents: OrderComponents) => { - const orderHash = await marketplaceContract.getOrderHash( - orderComponents as any - ); + const orderHash = await marketplaceContract.getOrderHash(orderComponents); const derivedOrderHash = calculateOrderHash(orderComponents); expect(orderHash).to.equal(derivedOrderHash); return orderHash; @@ -100,7 +124,7 @@ export const marketplaceFixture = async ( // Returns signature const signOrder = async ( orderComponents: OrderComponents, - signer: Wallet + signer: Wallet | Contract ) => { const signature = await signer._signTypedData( domainData, @@ -121,9 +145,65 @@ export const marketplaceFixture = async ( return signature; }; + const signBulkOrder = async ( + orderComponents: OrderComponents[], + signer: Wallet | Contract, + startIndex = 0, + height?: number, + extraCheap?: boolean + ) => { + const tree = getBulkOrderTree(orderComponents, startIndex, height); + const bulkOrderType = tree.types; + const chunks = tree.getDataToSign(); + let signature = await signer._signTypedData(domainData, bulkOrderType, { + tree: chunks, + }); + + if (extraCheap) { + signature = convertSignatureToEIP2098(signature); + } + + const proofAndSignature = tree.getEncodedProofAndSignature( + startIndex, + signature + ); + + const orderHash = tree.getBulkOrderHash(); + + const { domainSeparator } = await marketplaceContract.information(); + const digest = keccak256( + `0x1901${domainSeparator.slice(2)}${orderHash.slice(2)}` + ); + const recoveredAddress = recoverAddress(digest, signature); + + expect(recoveredAddress).to.equal(signer.address); + + // Verify each individual order + for (const components of orderComponents) { + const individualOrderHash = await getAndVerifyOrderHash(components); + const digest = keccak256( + `0x1901${domainSeparator.slice(2)}${individualOrderHash.slice(2)}` + ); + const individualOrderSignature = await signer._signTypedData( + domainData, + orderType, + components + ); + const recoveredAddress = recoverAddress(digest, individualOrderSignature); + expect(recoveredAddress).to.equal(signer.address); + } + + return proofAndSignature; + }; + const createOrder = async ( - offerer: Wallet, - zone: Wallet | undefined | string = undefined, + offerer: Wallet | Contract, + zone: + | TestZone + | TestPostExecution + | Wallet + | undefined + | string = undefined, offer: OfferItem[], consideration: ConsiderationItem[], orderType: number, @@ -132,7 +212,10 @@ export const marketplaceFixture = async ( signer?: Wallet, zoneHash = constants.HashZero, conduitKey = constants.HashZero, - extraCheap = false + extraCheap = false, + useBulkSignature = false, + bulkSignatureIndex?: number, + bulkSignatureHeight?: number ) => { const counter = await marketplaceContract.getCounter(offerer.address); @@ -145,7 +228,7 @@ export const marketplaceFixture = async ( const orderParameters = { offerer: offerer.address, zone: !extraCheap - ? (zone as Wallet).address || (zone as string) + ? (zone as Wallet).address ?? zone : constants.AddressZero, offer, consideration, @@ -177,7 +260,7 @@ export const marketplaceFixture = async ( totalSize, }; - const flatSig = await signOrder(orderComponents, signer || offerer); + const flatSig = await signOrder(orderComponents, signer ?? offerer); const order = { parameters: orderParameters, @@ -187,6 +270,28 @@ export const marketplaceFixture = async ( extraData: "0x", // only used for advanced orders }; + if (useBulkSignature) { + order.signature = await signBulkOrder( + [orderComponents], + signer ?? offerer, + bulkSignatureIndex, + bulkSignatureHeight, + extraCheap + ); + + // Verify bulk signature length + expect( + order.signature.slice(2).length / 2, + "bulk signature length should be valid (98 < length < 837)" + ) + .to.be.gt(98) + .and.lt(837); + expect( + (order.signature.slice(2).length / 2 - 67) % 32, + "bulk signature length should be valid ((length - 67) % 32 < 2)" + ).to.be.lt(2); + } + // How much ether (at most) needs to be supplied when fulfilling the order const value = offer .map((x) => @@ -215,6 +320,8 @@ export const marketplaceFixture = async ( value, orderStatus, orderComponents, + startTime, + endTime, }; }; @@ -433,9 +540,9 @@ export const marketplaceFixture = async ( counter, }; - const flatSig = await signOrder(orderComponents as any, offerer); + const flatSig = await signOrder(orderComponents, offerer); - const mirrorOrderHash = await getAndVerifyOrderHash(orderComponents as any); + const mirrorOrderHash = await getAndVerifyOrderHash(orderComponents); const mirrorOrder = { parameters: orderParameters, @@ -467,8 +574,12 @@ export const marketplaceFixture = async ( marketplaceContract, directMarketplaceContract, stubZone, + postExecutionZone, + invalidContractOfferer, + invalidContractOffererRatifyOrder, domainData, signOrder, + signBulkOrder, createOrder, createMirrorBuyNowOrder, createMirrorAcceptOfferOrder, diff --git a/test/utils/fixtures/tokens.ts b/test/utils/fixtures/tokens.ts index 9fddd4372..84da3a942 100644 --- a/test/utils/fixtures/tokens.ts +++ b/test/utils/fixtures/tokens.ts @@ -1,20 +1,23 @@ -/* eslint-disable camelcase */ -import { JsonRpcSigner } from "@ethersproject/providers"; import { expect } from "chai"; -import { BigNumber, constants, Wallet } from "ethers"; import { ethers } from "hardhat"; -import { TestERC1155, TestERC20, TestERC721 } from "../../../typechain-types"; + import { deployContract } from "../contracts"; import { - randomBN, - toBN, - BigNumberish, getOfferOrConsiderationItem, random128, + randomBN, + toBN, } from "../encoding"; -import { whileImpersonating } from "../impersonate"; -export const fixtureERC20 = async (signer: JsonRpcSigner) => { +import type { + TestERC1155, + TestERC20, + TestERC721, +} from "../../../typechain-types"; +import type { JsonRpcSigner } from "@ethersproject/providers"; +import type { BigNumber, BigNumberish, Contract, Wallet } from "ethers"; + +export const fixtureERC20 = async (signer: JsonRpcSigner | Wallet) => { const testERC20: TestERC20 = await deployContract("TestERC20", signer); const mintAndApproveERC20 = async ( @@ -47,7 +50,7 @@ export const fixtureERC20 = async (signer: JsonRpcSigner) => { }; }; -export const fixtureERC721 = async (signer: JsonRpcSigner) => { +export const fixtureERC721 = async (signer: JsonRpcSigner | Wallet) => { const testERC721: TestERC721 = await deployContract("TestERC721", signer); const set721ApprovalForAll = ( @@ -61,13 +64,13 @@ export const fixtureERC721 = async (signer: JsonRpcSigner) => { .withArgs(signer.address, spender, approved); }; - const mint721 = async (signer: Wallet, id?: BigNumberish) => { + const mint721 = async (signer: Wallet | Contract, id?: BigNumberish) => { const nftId = id ? toBN(id) : randomBN(); await testERC721.mint(signer.address, nftId); return nftId; }; - const mint721s = async (signer: Wallet, count: number) => { + const mint721s = async (signer: Wallet | Contract, count: number) => { const arr = []; for (let i = 0; i < count; i++) arr.push(await mint721(signer)); return arr; @@ -124,7 +127,7 @@ export const fixtureERC721 = async (signer: JsonRpcSigner) => { }; }; -export const fixtureERC1155 = async (signer: JsonRpcSigner) => { +export const fixtureERC1155 = async (signer: JsonRpcSigner | Wallet) => { const testERC1155: TestERC1155 = await deployContract("TestERC1155", signer); const set1155ApprovalForAll = ( @@ -212,14 +215,14 @@ export const fixtureERC1155 = async (signer: JsonRpcSigner) => { const minRandom = (min: number) => randomBN(10).add(min); -export const tokensFixture = async (signer: JsonRpcSigner) => { +export const tokensFixture = async (signer: JsonRpcSigner | Wallet) => { const erc20 = await fixtureERC20(signer); const erc721 = await fixtureERC721(signer); const erc1155 = await fixtureERC1155(signer); const { testERC1155: testERC1155Two } = await fixtureERC1155(signer); const tokenByType = [ { - address: constants.AddressZero, + address: ethers.constants.AddressZero, } as any, // ETH erc20.testERC20, erc721.testERC721, @@ -245,19 +248,13 @@ export const tokensFixture = async (signer: JsonRpcSigner) => { await (contract as TestERC20).mint(receiver.address, amount); // Receiver approves contract to transfer tokens - await whileImpersonating( - receiver.address, - ethers.provider, - async () => { - await expect( - (contract as TestERC20) - .connect(receiver) - .approve(approvalAddress, amount) - ) - .to.emit(contract, "Approval") - .withArgs(receiver.address, approvalAddress, amount); - } - ); + await expect( + (contract as TestERC20) + .connect(receiver) + .approve(approvalAddress, amount) + ) + .to.emit(contract, "Approval") + .withArgs(receiver.address, approvalAddress, amount); break; case 2: // ERC721 case 4: // ERC721_WITH_CRITERIA diff --git a/test/utils/helpers.ts b/test/utils/helpers.ts new file mode 100644 index 000000000..8cd0ac946 --- /dev/null +++ b/test/utils/helpers.ts @@ -0,0 +1,53 @@ +import { ethers } from "ethers"; + +import { randomBN } from "./encoding"; + +import type { + AdvancedOrder, + CriteriaResolver, + Fulfillment, + Order, +} from "./types"; + +export const VERSION = `1.2${process.env.REFERENCE ? "-reference" : ""}`; + +export const minRandom = (min: ethers.BigNumberish) => randomBN(10).add(min); + +export const getCustomRevertSelector = (customErrorString: string) => + ethers.utils + .keccak256(ethers.utils.toUtf8Bytes(customErrorString)) + .slice(0, 10); + +export const simulateMatchOrders = async ( + marketplaceContract: ethers.Contract, + orders: Order[], + fulfillments: Fulfillment[], + caller: ethers.Wallet, + value: ethers.BigNumberish +) => + marketplaceContract + .connect(caller) + .callStatic.matchOrders(orders, fulfillments, { + value, + }); + +export const simulateAdvancedMatchOrders = async ( + marketplaceContract: ethers.Contract, + orders: AdvancedOrder[], + criteriaResolvers: CriteriaResolver[], + fulfillments: Fulfillment[], + caller: ethers.Wallet, + value: ethers.BigNumberish, + recipient: string = ethers.constants.AddressZero +) => + marketplaceContract + .connect(caller) + .callStatic.matchAdvancedOrders( + orders, + criteriaResolvers, + fulfillments, + recipient, + { + value, + } + ); diff --git a/test/utils/impersonate.ts b/test/utils/impersonate.ts deleted file mode 100644 index bd1ccb283..000000000 --- a/test/utils/impersonate.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { JsonRpcProvider } from "@ethersproject/providers"; -import { parseEther } from "@ethersproject/units"; -import { ethers } from "hardhat"; -import { randomHex } from "./encoding"; - -const TEN_THOUSAND_ETH = parseEther("10000").toHexString().replace("0x0", "0x"); - -export const impersonate = async ( - address: string, - provider: JsonRpcProvider -) => { - await provider.send("hardhat_impersonateAccount", [address]); - await faucet(address, provider); -}; - -export const faucet = async (address: string, provider: JsonRpcProvider) => { - await provider.send("hardhat_setBalance", [address, TEN_THOUSAND_ETH]); -}; - -export const getWalletWithEther = async () => { - const wallet = new ethers.Wallet(randomHex(32), ethers.provider); - await faucet(wallet.address, ethers.provider); - return wallet; -}; - -export const stopImpersonation = async ( - address: string, - provider: JsonRpcProvider -) => { - await provider.send("hardhat_stopImpersonatingAccount", [address]); -}; - -export const whileImpersonating = async ( - address: string, - provider: JsonRpcProvider, - fn: () => T -) => { - await impersonate(address, provider); - const result = await fn(); - await stopImpersonation(address, provider); - return result; -}; diff --git a/test/utils/reports/comment-table.ts b/test/utils/reports/comment-table.ts new file mode 100644 index 000000000..003d9804f --- /dev/null +++ b/test/utils/reports/comment-table.ts @@ -0,0 +1,73 @@ +import chalk from "chalk"; + +const err = chalk.bold.red; +const warn = chalk.hex("#FFA500"); +const info = chalk.blue; +const success = chalk.green; + +export function diffPctString( + newValue: number, + oldValue: number, + warnOnIncrease?: boolean, + diffOnly?: boolean +): string { + if ([newValue, oldValue].every(isNaN)) { + return warn("null"); + } + const diff = newValue - oldValue; + + if (diff === 0) return info(newValue.toString()); + const pct = +((100 * diff) / oldValue).toFixed(2); + const prefix = pct > 0 ? "+" : ""; + const color = diff > 0 ? (warnOnIncrease ? warn : err) : success; + const value = diffOnly ? diff : newValue; + return `${value} (${color(`${prefix}${pct}%`)})`; +} +// eslint-disable-next-line no-control-regex +const stripANSI = (str: string) => str.replace(/\u001b\[.*?m/g, ""); + +export function getColumnSizesAndAlignments( + rows: string[][], + padding = 0 +): Array<[number, boolean]> { + const sizesAndAlignments: Array<[number, boolean]> = []; + const numColumns = rows[0].length; + for (let i = 0; i < numColumns; i++) { + const entries = rows.map((row) => stripANSI(row[i])); + const maxSize = Math.max(...entries.map((e) => e.length)); + const alignLeft = entries.slice(1).some((e) => !!e.match(/[a-zA-Z]/g)); + sizesAndAlignments.push([maxSize + padding, alignLeft]); + } + return sizesAndAlignments; +} + +const padColumn = ( + col: string, + size: number, + padWith: string, + alignLeft: boolean +) => { + const padSize = Math.max(0, size - stripANSI(col).length); + const padding = padWith.repeat(padSize); + if (alignLeft) return `${col}${padding}`; + return `${padding}${col}`; +}; + +export const toCommentTable = (rows: string[][]): string[] => { + const sizesAndAlignments = getColumnSizesAndAlignments(rows); + rows.forEach((row) => { + row.forEach((col, c) => { + const [size, alignLeft] = sizesAndAlignments[c]; + row[c] = padColumn(col, size, " ", alignLeft); + }); + }); + + const completeRows = rows.map((row) => `| ${row.join(" | ")} |`); + const rowSeparator = `==${sizesAndAlignments + .map(([size]) => "=".repeat(size)) + .join("===")}==`; + completeRows.splice(1, 0, rowSeparator); + completeRows.unshift(rowSeparator); + completeRows.push(rowSeparator); + return completeRows; +}; diff --git a/test/utils/reports/report_parser.ts b/test/utils/reports/report_parser.ts new file mode 100644 index 000000000..a64b6f194 --- /dev/null +++ b/test/utils/reports/report_parser.ts @@ -0,0 +1,74 @@ +import fs from "fs"; +import path from "path"; + +import { diffPctString, toCommentTable } from "./comment-table"; + +type GasReport = { + contract: string; + method: string; + min: number; + max: number; + avg: number; + calls: number; +}; + +function parseReport(text: string): GasReport[] { + const lines = text + .split("\n") + .slice(6) + .filter((ln) => ln.indexOf("·") !== 0); + const rows = lines + .map((ln) => ln.replace(/\|/g, "").replace(/\s/g, "").split("·")) + .filter((row) => row.length === 7) + .map(([contract, method, min, max, avg, calls]) => ({ + contract, + method, + min: +min, + max: +max, + avg: +avg, + calls: +calls, + })); + return rows; +} + +function parseReportFile(fileName: string, write?: boolean) { + const text = fs.readFileSync(path.join(__dirname, fileName), "utf8"); + const report = parseReport(text); + if (write) { + fs.writeFileSync( + path.join(__dirname, fileName.replace(".md", ".json")), + JSON.stringify(report, null, 2) + ); + } + return report; +} + +export function compareReports(report1: GasReport[], report2: GasReport[]) { + const rows: string[][] = []; + rows.push([`contract`, `method`, `min`, `max`, `avg`]); + report1.forEach((r1, i) => { + if (r1.contract !== "Seaport") return; + const r2 = report2[i]; + if (r1.contract !== r2.contract || r1.method !== r2.method) { + throw new Error("contract and method for comparison do not match"); + } + rows.push([ + r1.contract, + r1.method, + diffPctString(r2.min, r1.min, false, true), + diffPctString(r2.max, r1.max, false, true), + diffPctString(r2.avg, r1.avg, false, true), + ]); + }); + console.log(toCommentTable(rows).join("\n")); +} + +export function compareReportFiles( + name1: string, + name2: string, + write?: boolean +) { + const report1 = parseReportFile(name1, write); + const report2 = parseReportFile(name2, write); + compareReports(report1, report2); +} diff --git a/test/utils/seeded-rng.js b/test/utils/seeded-rng.js index 927790cfc..a0ea23f6e 100644 --- a/test/utils/seeded-rng.js +++ b/test/utils/seeded-rng.js @@ -51,7 +51,7 @@ The Windows scripting host version: https://www.GRC.com/otg/wsh-uheprng.js ---------------------------------------------------------------------------- Qualifying MWC multipliers are: 187884, 686118, 898134, 1104375, 1250205, - 1460910 and 1768863. (We use the largest one that's < 2^21) + 1460910 and 1768863. (Use the largest one that's < 2^21) ============================================================================ */ "use strict"; const stringify = JSON.stringify; @@ -120,7 +120,7 @@ const uheprng = function (seed) { }; // this EXPORTED function is the default function returned by this library. - // The values returned are integers in the range from 0 to range-1. We first + // The values returned are integers in the range from 0 to range-1. First // obtain two 32-bit fractions (from rawprng) to synthesize a single high // resolution 53-bit prng (0 to <1), then we multiply this by the caller's // "range" param and take the "floor" to return a equally probable integer. diff --git a/test/utils/sign.ts b/test/utils/sign.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/utils/types.ts b/test/utils/types.ts index 898286ae5..ed0737dae 100644 --- a/test/utils/types.ts +++ b/test/utils/types.ts @@ -1,6 +1,4 @@ -import { BigNumber } from "ethers"; - -export type BigNumberish = string | BigNumber | number | boolean; +import type { BigNumber } from "ethers"; export type AdditionalRecipient = { amount: BigNumber; @@ -12,6 +10,11 @@ export type FulfillmentComponent = { itemIndex: number; }; +export type Fulfillment = { + offerComponents: FulfillmentComponent[]; + considerationComponents: FulfillmentComponent[]; +}; + export type CriteriaResolver = { orderIndex: number; side: 0 | 1; @@ -90,3 +93,7 @@ export type AdvancedOrder = { signature: string; extraData: string; }; + +export type BulkOrder = { + tree: Array>>>>>>; +}; diff --git a/test/zone.spec.ts b/test/zone.spec.ts new file mode 100644 index 000000000..dddd1b648 --- /dev/null +++ b/test/zone.spec.ts @@ -0,0 +1,1004 @@ +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers, network } from "hardhat"; + +import { merkleTree } from "./utils/criteria"; +import { + buildResolver, + getItemETH, + randomHex, + toAddress, + toBN, + toFulfillment, + toKey, +} from "./utils/encoding"; +import { decodeEvents } from "./utils/events"; +import { faucet } from "./utils/faucet"; +import { seaportFixture } from "./utils/fixtures"; +import { VERSION } from "./utils/helpers"; + +import type { + ConsiderationInterface, + TestERC721, + TestZone, +} from "../typechain-types"; +import type { SeaportFixtures } from "./utils/fixtures"; +import type { Contract, Wallet } from "ethers"; + +const { parseEther } = ethers.utils; + +describe(`Zone - PausableZone (Seaport v${VERSION})`, function () { + const { provider } = ethers; + const owner = new ethers.Wallet(randomHex(32), provider); + + let marketplaceContract: ConsiderationInterface; + let stubZone: TestZone; + let testERC721: TestERC721; + + let checkExpectedEvents: SeaportFixtures["checkExpectedEvents"]; + let createOrder: SeaportFixtures["createOrder"]; + let getTestItem721: SeaportFixtures["getTestItem721"]; + let getTestItem721WithCriteria: SeaportFixtures["getTestItem721WithCriteria"]; + let mintAndApprove721: SeaportFixtures["mintAndApprove721"]; + let withBalanceChecks: SeaportFixtures["withBalanceChecks"]; + + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + }); + }); + + before(async () => { + await faucet(owner.address, provider); + + ({ + checkExpectedEvents, + createOrder, + getTestItem721, + getTestItem721WithCriteria, + marketplaceContract, + mintAndApprove721, + stubZone, + testERC721, + withBalanceChecks, + } = await seaportFixture(owner)); + }); + + let buyer: Wallet; + let seller: Wallet; + + async function setupFixture() { + // Setup basic buyer/seller wallets with ETH + const seller = new ethers.Wallet(randomHex(32), provider); + const buyer = new ethers.Wallet(randomHex(32), provider); + + for (const wallet of [seller, buyer]) { + await faucet(wallet.address, provider); + } + + return { seller, buyer }; + } + + beforeEach(async () => { + ({ seller, buyer } = await loadFixture(setupFixture)); + }); + + /** Create zone and get zone address */ + async function createZone(pausableZoneController: Contract, salt?: string) { + const tx = await pausableZoneController.createZone(salt ?? randomHex()); + + const zoneContract = await ethers.getContractFactory("PausableZone", owner); + + const events = await decodeEvents(tx, [ + { eventName: "ZoneCreated", contract: pausableZoneController }, + { eventName: "Unpaused", contract: zoneContract as any }, + ]); + expect(events.length).to.be.equal(2); + + const [unpauseEvent, zoneCreatedEvent] = events; + expect(unpauseEvent.eventName).to.equal("Unpaused"); + expect(zoneCreatedEvent.eventName).to.equal("ZoneCreated"); + + return zoneCreatedEvent.data.zone as string; + } + + it("Fulfills an order with a pausable zone", async () => { + const pausableDeployer = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const deployer = await pausableDeployer.deploy(owner.address); + + const zoneAddr = await createZone(deployer); + + // create basic order using pausable as zone + // execute basic 721 <=> ETH order + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zoneAddr, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = await marketplaceContract + .connect(buyer) + .fulfillOrder(order, toKey(0), { + value, + }); + + const receipt = await tx.wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + return receipt; + }); + }); + + it("Fulfills an advanced order with criteria with a pausable zone", async () => { + const pausableZoneController = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZone = await pausableZoneController.deploy(owner.address); + + const zoneAddr = await createZone(pausableZone); + + // create basic order using pausable as zone + // execute basic 721 <=> ETH order + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const { root, proofs } = merkleTree([nftId]); + + const offer = [getTestItem721WithCriteria(root, toBN(1), toBN(1))]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const criteriaResolvers = [ + buildResolver(0, 0, 0, nftId, proofs[nftId.toString()]), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zoneAddr, + offer, + consideration, + 2, // FULL_RESTRICTED + criteriaResolvers + ); + + await withBalanceChecks([order], 0, criteriaResolvers, async () => { + const tx = await marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + criteriaResolvers, + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + + const receipt = await tx.wait(); + await checkExpectedEvents( + tx, + receipt, + [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ], + undefined, + criteriaResolvers + ); + return receipt; + }); + }); + + it("Fulfills a PARTIAL_RESTRICTED order with the caller being the offerer", async () => { + const pausableZoneController = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZone = await pausableZoneController.deploy(owner.address); + + const zoneAddr = await createZone(pausableZone); + + // create basic order using pausable as zone + // execute basic 721 <=> ETH order + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { order, orderHash, value } = await createOrder( + seller, + zoneAddr, + offer, + consideration, + 3 // PARTIAL_RESTRICTED + ); + + await withBalanceChecks([order], 0, undefined, async () => { + const tx = await marketplaceContract + .connect(buyer) + .fulfillAdvancedOrder( + order, + [], + toKey(0), + ethers.constants.AddressZero, + { + value, + } + ); + + const receipt = await tx.wait(); + await checkExpectedEvents(tx, receipt, [ + { + order, + orderHash, + fulfiller: buyer.address, + fulfillerConduitKey: toKey(0), + }, + ]); + return receipt; + }); + }); + + it("Fulfills an order with executeMatchOrders", async () => { + // Create Pausable Zone Controller + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + // Deploy Pausable Zone + const zoneAddr = await createZone(pausableZoneController); + + // Mint NFTs for use in orders + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + const secondNFTId = await mintAndApprove721( + buyer, + marketplaceContract.address + ); + const thirdNFTId = await mintAndApprove721( + owner, + marketplaceContract.address + ); + + // Define orders + const offerOne = [ + getTestItem721(nftId, toBN(1), toBN(1), undefined, testERC721.address), + ]; + const considerationOne = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + seller.address, + testERC721.address + ), + ]; + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zoneAddr, + offerOne, + considerationOne, + 2 + ); + + const offerTwo = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + const considerationTwo = [ + getTestItem721( + thirdNFTId, + toBN(1), + toBN(1), + buyer.address, + testERC721.address + ), + ]; + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + buyer, + zoneAddr, + offerTwo, + considerationTwo, + 2 + ); + + const offerThree = [ + getTestItem721( + thirdNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + const considerationThree = [ + getTestItem721( + nftId, + toBN(1), + toBN(1), + owner.address, + testERC721.address + ), + ]; + const { order: orderThree, orderHash: orderHashThree } = await createOrder( + owner, + zoneAddr, + offerThree, + considerationThree, + 2 + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[2, 0]]], + [[[2, 0]], [[1, 0]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + pausableZoneController + .connect(buyer) + .callStatic.executeMatchOrders( + zoneAddr, + marketplaceContract.address, + [orderOne, orderTwo, orderThree], + fulfillments, + { value: 0 } + ) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + // Ensure that the number of executions from matching orders with zone + // is equal to the number of fulfillments + const executions = await pausableZoneController + .connect(owner) + .callStatic.executeMatchOrders( + zoneAddr, + marketplaceContract.address, + [orderOne, orderTwo, orderThree], + fulfillments, + { value: 0 } + ); + expect(executions.length).to.equal(fulfillments.length); + + // Perform the match orders with zone + const tx = await pausableZoneController + .connect(owner) + .executeMatchOrders( + zoneAddr, + marketplaceContract.address, + [orderOne, orderTwo, orderThree], + fulfillments + ); + + // Decode all events and get the order hashes + const orderFulfilledEvents = await decodeEvents(tx, [ + { eventName: "OrderFulfilled", contract: marketplaceContract }, + ]); + expect(orderFulfilledEvents.length).to.equal(fulfillments.length); + + // Check that the actual order hashes match those from the events, in order + const actualOrderHashes = [orderHashOne, orderHashTwo, orderHashThree]; + orderFulfilledEvents.forEach((orderFulfilledEvent, i) => + expect(orderFulfilledEvent.data.orderHash).to.be.equal( + actualOrderHashes[i] + ) + ); + }); + + it("Fulfills an order with executeMatchAdvancedOrders", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + // Deploy pausable zone + const zoneAddr = await createZone(pausableZoneController); + + // Mint NFTs for use in orders + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + const secondNFTId = await mintAndApprove721( + buyer, + marketplaceContract.address + ); + const thirdNFTId = await mintAndApprove721( + owner, + marketplaceContract.address + ); + + // Define orders + const offerOne = [ + getTestItem721(nftId, toBN(1), toBN(1), undefined, testERC721.address), + ]; + const considerationOne = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + seller.address, + testERC721.address + ), + ]; + const { order: orderOne, orderHash: orderHashOne } = await createOrder( + seller, + zoneAddr, + offerOne, + considerationOne, + 2 + ); + + const offerTwo = [ + getTestItem721( + secondNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + const considerationTwo = [ + getTestItem721( + thirdNFTId, + toBN(1), + toBN(1), + buyer.address, + testERC721.address + ), + ]; + const { order: orderTwo, orderHash: orderHashTwo } = await createOrder( + buyer, + zoneAddr, + offerTwo, + considerationTwo, + 2 + ); + + const offerThree = [ + getTestItem721( + thirdNFTId, + toBN(1), + toBN(1), + undefined, + testERC721.address + ), + ]; + const considerationThree = [ + getTestItem721( + nftId, + toBN(1), + toBN(1), + owner.address, + testERC721.address + ), + ]; + const { order: orderThree, orderHash: orderHashThree } = await createOrder( + owner, + zoneAddr, + offerThree, + considerationThree, + 2 + ); + + const fulfillments = [ + [[[1, 0]], [[0, 0]]], + [[[0, 0]], [[2, 0]]], + [[[2, 0]], [[1, 0]]], + ].map(([offerArr, considerationArr]) => + toFulfillment(offerArr, considerationArr) + ); + + await expect( + pausableZoneController + .connect(buyer) + .executeMatchAdvancedOrders( + zoneAddr, + marketplaceContract.address, + [orderOne, orderTwo, orderThree], + [], + fulfillments, + { value: 0 } + ) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + // Ensure that the number of executions from matching advanced orders with zone + // is equal to the number of fulfillments + const executions = await pausableZoneController + .connect(owner) + .callStatic.executeMatchAdvancedOrders( + zoneAddr, + marketplaceContract.address, + [orderOne, orderTwo, orderThree], + [], + fulfillments, + { value: 0 } + ); + expect(executions.length).to.equal(fulfillments.length); + + // Perform the match advanced orders with zone + const tx = await pausableZoneController + .connect(owner) + .executeMatchAdvancedOrders( + zoneAddr, + marketplaceContract.address, + [orderOne, orderTwo, orderThree], + [], + fulfillments + ); + + // Decode all events and get the order hashes + const orderFulfilledEvents = await decodeEvents(tx, [ + { eventName: "OrderFulfilled", contract: marketplaceContract }, + ]); + expect(orderFulfilledEvents.length).to.equal(fulfillments.length); + + // Check that the actual order hashes match those from the events, in order + const actualOrderHashes = [orderHashOne, orderHashTwo, orderHashThree]; + orderFulfilledEvents.forEach((orderFulfilledEvent, i) => + expect(orderFulfilledEvent.data.orderHash).to.be.equal( + actualOrderHashes[i] + ) + ); + }); + + it("Only the deployer owner can create a zone", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + // deploy pausable zone from non-deployer owner + const salt = randomHex(); + await expect( + pausableZoneController.connect(seller).createZone(salt) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + // deploy pausable zone from owner + await createZone(pausableZoneController); + }); + + it("Assign pauser and self destruct the zone", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + const zoneAddr = await createZone(pausableZoneController); + + // Attach to Pausable Zone + const zoneContract = await ethers.getContractFactory("PausableZone", owner); + + // Attach to zone + const zone = await zoneContract.attach(zoneAddr); + + // Try to nuke the zone through the deployer before being assigned pauser + await expect(pausableZoneController.connect(buyer).pause(zoneAddr)).to.be + .reverted; + + // Try to nuke the zone directly before being assigned pauser + await expect(zone.connect(buyer).pause(zoneAddr)).to.be.reverted; + + await expect( + pausableZoneController.connect(buyer).assignPauser(seller.address) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + await expect( + pausableZoneController.connect(owner).assignPauser(toAddress(0)) + ).to.be.revertedWithCustomError( + pausableZoneController, + "PauserCanNotBeSetAsZero" + ); + + // owner assigns the pauser of the zone + await pausableZoneController.connect(owner).assignPauser(buyer.address); + + // Check pauser owner + expect(await pausableZoneController.pauser()).to.equal(buyer.address); + + // Now as pauser, nuke the zone + const tx = await pausableZoneController.connect(buyer).pause(zoneAddr); + + // Check paused event was emitted + const pauseEvents = await decodeEvents(tx, [ + { eventName: "Paused", contract: zoneContract as any }, + ]); + expect(pauseEvents.length).to.equal(1); + }); + + it("Revert on deploying a zone with the same salt", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + const salt = randomHex(); + + // Create zone with salt + await pausableZoneController.createZone(salt); + + // Create zone with same salt + await expect( + pausableZoneController.createZone(salt) + ).to.be.revertedWithCustomError( + pausableZoneController, + "ZoneAlreadyExists" + ); + }); + + it("Revert on an order with a pausable zone if zone has been self destructed", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + const zoneAddr = await createZone(pausableZoneController); + + // create basic order using pausable as zone + // execute basic 721 <=> ETH order + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + // eslint-disable-next-line + const { order, orderHash, value } = await createOrder( + seller, + zoneAddr, + offer, + consideration, + 2 + ); + + // owner nukes the zone + pausableZoneController.pause(zoneAddr); + + if (!process.env.REFERENCE) { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ) + .to.be.revertedWithCustomError( + marketplaceContract, + "InvalidRestrictedOrder" + ) + .withArgs(orderHash); + } else { + await expect( + marketplaceContract.connect(buyer).fulfillOrder(order, toKey(0), { + value, + }) + ).to.be.reverted; + } + }); + + it("Reverts if non-owner tries to self destruct the zone", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + const zoneAddr = await createZone(pausableZoneController); + + // non owner tries to use pausable deployer to nuke the zone, reverts + await expect(pausableZoneController.connect(buyer).pause(zoneAddr)).to.be + .reverted; + }); + + it("Zone can cancel restricted orders", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + // deploy PausableZone + const zoneAddr = await createZone(pausableZoneController); + + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { orderComponents } = await createOrder( + seller, + zoneAddr, + offer, + consideration, + 2 // FULL_RESTRICTED, zone can execute or cancel + ); + + await expect( + pausableZoneController + .connect(buyer) + .cancelOrders(zoneAddr, marketplaceContract.address, [orderComponents]) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + await pausableZoneController.cancelOrders( + zoneAddr, + marketplaceContract.address, + [orderComponents] + ); + }); + + it("Operator of zone can cancel restricted orders", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + // deploy PausableZone + const zoneAddr = await createZone(pausableZoneController); + + // Attach to PausableZone zone + const zoneContract = await ethers.getContractFactory("PausableZone", owner); + + // Attach to zone + const zone = await zoneContract.attach(zoneAddr); + + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { orderComponents } = await createOrder( + seller, + zoneAddr, + offer, + consideration, + 2 // FULL_RESTRICTED, zone can execute or cancel + ); + + // Non operator address should not be allowed to operate the zone + await expect( + zone + .connect(seller) + .cancelOrders(marketplaceContract.address, [orderComponents]) + ).to.be.reverted; + + // Approve operator + await pausableZoneController + .connect(owner) + .assignOperator(zoneAddr, seller.address); + + // Now allowed to operate the zone + await zone + .connect(seller) + .cancelOrders(marketplaceContract.address, [orderComponents]); + + // Cannot assign operator to zero address + await expect( + pausableZoneController + .connect(owner) + .assignOperator(zoneAddr, toAddress(0)) + ).to.be.revertedWithCustomError( + pausableZoneController, + "PauserCanNotBeSetAsZero" + ); + }); + + it("Reverts trying to assign operator as non-deployer", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + // deploy PausableZone + const zoneAddr = await createZone(pausableZoneController); + + // Attach to pausable zone + const zoneContract = await ethers.getContractFactory("PausableZone", owner); + + // Attach to zone + const zone = await zoneContract.attach(zoneAddr); + + // Try to approve operator without permission + await expect( + pausableZoneController + .connect(seller) + .assignOperator(zoneAddr, seller.address) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + // Try to approve operator directly without permission + await expect(zone.connect(seller).assignOperator(seller.address)).to.be + .reverted; + }); + + it("Reverts if non-Zone tries to cancel restricted orders", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + await createZone(pausableZoneController); + + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { orderComponents } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + await expect(marketplaceContract.connect(buyer).cancel([orderComponents])) + .to.be.reverted; + }); + + it("Reverts if non-owner tries to use the zone to cancel restricted orders", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + const zoneAddr = await createZone(pausableZoneController); + + const nftId = await mintAndApprove721(seller, marketplaceContract.address); + + const offer = [getTestItem721(nftId)]; + + const consideration = [ + getItemETH(parseEther("10"), parseEther("10"), seller.address), + getItemETH(parseEther("1"), parseEther("1"), owner.address), + ]; + + const { orderComponents } = await createOrder( + seller, + stubZone, + offer, + consideration, + 2 // FULL_RESTRICTED + ); + + // buyer calls zone owner to cancel an order through the zone + await expect( + pausableZoneController + .connect(buyer) + .cancelOrders(zoneAddr, marketplaceContract.address, [orderComponents]) + ).to.be.reverted; + }); + + it("Lets the Zone Deployer owner transfer ownership via a two-stage process", async () => { + const pausableZoneControllerFactory = await ethers.getContractFactory( + "PausableZoneController", + owner + ); + const pausableZoneController = await pausableZoneControllerFactory.deploy( + owner.address + ); + + await createZone(pausableZoneController); + + await expect( + pausableZoneController.connect(buyer).transferOwnership(buyer.address) + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + await expect( + pausableZoneController.connect(owner).transferOwnership(toAddress(0)) + ).to.be.revertedWithCustomError( + pausableZoneController, + "OwnerCanNotBeSetAsZero" + ); + + await expect( + pausableZoneController.connect(seller).cancelOwnershipTransfer() + ).to.be.revertedWithCustomError(pausableZoneController, "CallerIsNotOwner"); + + await expect( + pausableZoneController.connect(buyer).acceptOwnership() + ).to.be.revertedWithCustomError( + pausableZoneController, + "CallerIsNotPotentialOwner" + ); + + // just get any random address as the next potential owner. + await pausableZoneController + .connect(owner) + .transferOwnership(buyer.address); + + // Check potential owner + expect(await pausableZoneController.potentialOwner()).to.equal( + buyer.address + ); + + await pausableZoneController.connect(owner).cancelOwnershipTransfer(); + await pausableZoneController + .connect(owner) + .transferOwnership(buyer.address); + await pausableZoneController.connect(buyer).acceptOwnership(); + + expect(await pausableZoneController.owner()).to.equal(buyer.address); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 831e4358e..4a8b9a26c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,8 +5,9 @@ "strict": true, "esModuleInterop": true, "outDir": "dist", - "declaration": true + "declaration": true, + "resolveJsonModule": true }, - "include": ["./scripts", "./test", "./typechain-types"], + "include": ["./scripts", "./test", "./typechain-types", "./eip-712-types", "./*.config.ts", "./docs/prepare-docs.js"], "files": ["./hardhat.config.ts"] } diff --git a/yarn.lock b/yarn.lock index c8722159b..b72a4c3d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,578 +3,430 @@ "@babel/code-frame@^7.0.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.18.6": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== - -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== - dependencies: - "@cspotcode/source-map-consumer" "0.8.0" - -"@ensdomains/ens@^0.4.4": - version "0.4.5" - resolved "https://registry.npmjs.org/@ensdomains/ens/-/ens-0.4.5.tgz" - integrity sha512-JSvpj1iNMFjK6K+uVl4unqMoa9rf5jopb8cya5UGBWz23Nw8hSNT7efgUx4BTlAPAgpNlEioUfeTyQ6J9ZvTVw== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - bluebird "^3.5.2" - eth-ens-namehash "^2.0.8" - solc "^0.4.20" - testrpc "0.0.1" - web3-utils "^1.0.0-beta.31" + "@jridgewell/trace-mapping" "0.3.9" -"@ensdomains/resolver@^0.2.4": - version "0.2.4" - resolved "https://registry.npmjs.org/@ensdomains/resolver/-/resolver-0.2.4.tgz" - integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== - -"@eslint/eslintrc@^1.2.2": - version "1.2.2" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz" - integrity sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg== +"@eslint/eslintrc@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" + integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.1" - globals "^13.9.0" + espree "^9.4.0" + globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" - minimatch "^3.0.4" + minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@ethereum-waffle/chai@^3.4.4": - version "3.4.4" - resolved "https://registry.npmjs.org/@ethereum-waffle/chai/-/chai-3.4.4.tgz" - integrity sha512-/K8czydBtXXkcM9X6q29EqEkc5dN3oYenyH2a9hF7rGAApAJUpH8QBtojxOY/xQ2up5W332jqgxwp0yPiYug1g== - dependencies: - "@ethereum-waffle/provider" "^3.4.4" - ethers "^5.5.2" - -"@ethereum-waffle/compiler@^3.4.4": - version "3.4.4" - resolved "https://registry.npmjs.org/@ethereum-waffle/compiler/-/compiler-3.4.4.tgz" - integrity sha512-RUK3axJ8IkD5xpWjWoJgyHclOeEzDLQFga6gKpeGxiS/zBu+HB0W2FvsrrLalTFIaPw/CGYACRBSIxqiCqwqTQ== - dependencies: - "@resolver-engine/imports" "^0.3.3" - "@resolver-engine/imports-fs" "^0.3.3" - "@typechain/ethers-v5" "^2.0.0" - "@types/mkdirp" "^0.5.2" - "@types/node-fetch" "^2.5.5" - ethers "^5.0.1" - mkdirp "^0.5.1" - node-fetch "^2.6.1" - solc "^0.6.3" - ts-generator "^0.1.1" - typechain "^3.0.0" - -"@ethereum-waffle/ens@^3.4.4": - version "3.4.4" - resolved "https://registry.npmjs.org/@ethereum-waffle/ens/-/ens-3.4.4.tgz" - integrity sha512-0m4NdwWxliy3heBYva1Wr4WbJKLnwXizmy5FfSSr5PMbjI7SIGCdCB59U7/ZzY773/hY3bLnzLwvG5mggVjJWg== - dependencies: - "@ensdomains/ens" "^0.4.4" - "@ensdomains/resolver" "^0.2.4" - ethers "^5.5.2" - -"@ethereum-waffle/mock-contract@^3.4.4": - version "3.4.4" - resolved "https://registry.npmjs.org/@ethereum-waffle/mock-contract/-/mock-contract-3.4.4.tgz" - integrity sha512-Mp0iB2YNWYGUV+VMl5tjPsaXKbKo8MDH9wSJ702l9EBjdxFf/vBvnMBAC1Fub1lLtmD0JHtp1pq+mWzg/xlLnA== - dependencies: - "@ethersproject/abi" "^5.5.0" - ethers "^5.5.2" - -"@ethereum-waffle/provider@^3.4.4": - version "3.4.4" - resolved "https://registry.npmjs.org/@ethereum-waffle/provider/-/provider-3.4.4.tgz" - integrity sha512-GK8oKJAM8+PKy2nK08yDgl4A80mFuI8zBkE0C9GqTRYQqvuxIyXoLmJ5NZU9lIwyWVv5/KsoA11BgAv2jXE82g== - dependencies: - "@ethereum-waffle/ens" "^3.4.4" - ethers "^5.5.2" - ganache-core "^2.13.2" - patch-package "^6.2.2" - postinstall-postinstall "^2.1.0" - -"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.0", "@ethereumjs/block@^3.6.2": - version "3.6.2" - resolved "https://registry.npmjs.org/@ethereumjs/block/-/block-3.6.2.tgz" - integrity sha512-mOqYWwMlAZpYUEOEqt7EfMFuVL2eyLqWWIzcf4odn6QgXY8jBI2NhVuJncrMCKeMZrsJAe7/auaRRB6YcdH+Qw== - dependencies: - "@ethereumjs/common" "^2.6.3" - "@ethereumjs/tx" "^3.5.1" - ethereumjs-util "^7.1.4" - merkle-patricia-tree "^4.2.4" - -"@ethereumjs/blockchain@^5.5.0", "@ethereumjs/blockchain@^5.5.2": - version "5.5.2" - resolved "https://registry.npmjs.org/@ethereumjs/blockchain/-/blockchain-5.5.2.tgz" - integrity sha512-Jz26iJmmsQtngerW6r5BDFaew/f2mObLrRZo3rskLOx1lmtMZ8+TX/vJexmivrnWgmAsTdNWhlKUYY4thPhPig== - dependencies: - "@ethereumjs/block" "^3.6.2" - "@ethereumjs/common" "^2.6.3" - "@ethereumjs/ethash" "^1.1.0" - debug "^4.3.3" - ethereumjs-util "^7.1.4" - level-mem "^5.0.1" - lru-cache "^5.1.1" - semaphore-async-await "^1.5.1" - -"@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.6.0", "@ethereumjs/common@^2.6.3", "@ethereumjs/common@^2.6.4": - version "2.6.4" - resolved "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.4.tgz" - integrity sha512-RDJh/R/EAr+B7ZRg5LfJ0BIpf/1LydFgYdvZEuTraojCbVypO2sQ+QnpP5u2wJf9DASyooKqu8O4FJEWUV6NXw== - dependencies: - crc-32 "^1.2.0" - ethereumjs-util "^7.1.4" - -"@ethereumjs/ethash@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@ethereumjs/ethash/-/ethash-1.1.0.tgz" - integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== - dependencies: - "@ethereumjs/block" "^3.5.0" - "@types/levelup" "^4.3.0" - buffer-xor "^2.0.1" - ethereumjs-util "^7.1.1" - miller-rabin "^4.0.0" - -"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.4.0", "@ethereumjs/tx@^3.5.1": - version "3.5.1" - resolved "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.1.tgz" - integrity sha512-xzDrTiu4sqZXUcaBxJ4n4W5FrppwxLxZB4ZDGVLtxSQR4lVuOnFR6RcUHdg1mpUhAPVrmnzLJpxaeXnPxIyhWA== - dependencies: - "@ethereumjs/common" "^2.6.3" - ethereumjs-util "^7.1.4" - -"@ethereumjs/vm@^5.6.0": - version "5.9.0" - resolved "https://registry.npmjs.org/@ethereumjs/vm/-/vm-5.9.0.tgz" - integrity sha512-0IRsj4IuF8lFDWVVLc4mFOImaSX8VWF8CGm3mXHG/LLlQ/Tryy/kKXMw/bU9D+Zw03CdteW+wCGqNFS6+mPjpg== - dependencies: - "@ethereumjs/block" "^3.6.2" - "@ethereumjs/blockchain" "^5.5.2" - "@ethereumjs/common" "^2.6.4" - "@ethereumjs/tx" "^3.5.1" - async-eventemitter "^0.2.4" - core-js-pure "^3.0.1" - debug "^4.3.3" - ethereumjs-util "^7.1.4" - functional-red-black-tree "^1.0.1" - mcl-wasm "^0.7.1" - merkle-patricia-tree "^4.2.4" - rustbn.js "~0.2.0" - -"@ethersproject/abi@5.0.0-beta.153": - version "5.0.0-beta.153" - resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz" - integrity sha512-aXweZ1Z7vMNzJdLpR1CZUAIgnwjrZeUSvN9syCwlBaEBUFJmFY+HHnfuTI5vIhVs/mRkfJVrbEyl51JZQqyjAg== - dependencies: - "@ethersproject/address" ">=5.0.0-beta.128" - "@ethersproject/bignumber" ">=5.0.0-beta.130" - "@ethersproject/bytes" ">=5.0.0-beta.129" - "@ethersproject/constants" ">=5.0.0-beta.128" - "@ethersproject/hash" ">=5.0.0-beta.128" - "@ethersproject/keccak256" ">=5.0.0-beta.127" - "@ethersproject/logger" ">=5.0.0-beta.129" - "@ethersproject/properties" ">=5.0.0-beta.131" - "@ethersproject/strings" ">=5.0.0-beta.130" - -"@ethersproject/abi@5.0.7": - version "5.0.7" - resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz" - integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== - dependencies: - "@ethersproject/address" "^5.0.4" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/hash" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/strings" "^5.0.4" - -"@ethersproject/abi@5.6.1", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.0": - version "5.6.1" - resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.1.tgz" - integrity sha512-0cqssYh6FXjlwKWBmLm3+zH2BNARoS5u/hxbz+LpQmcDB3w0W553h2btWui1/uZp2GBM/SI3KniTuMcYyHpA5w== - dependencies: - "@ethersproject/address" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/hash" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - -"@ethersproject/abstract-provider@5.6.0", "@ethersproject/abstract-provider@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz" - integrity sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw== - dependencies: - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" - "@ethersproject/web" "^5.6.0" - -"@ethersproject/abstract-signer@5.6.0", "@ethersproject/abstract-signer@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.6.0.tgz" - integrity sha512-WOqnG0NJKtI8n0wWZPReHtaLkDByPL67tn4nBaDAhmVq8sjHTPbCdz4DRhVu/cfTOvfy9w3iq5QZ7BX7zw56BQ== - dependencies: - "@ethersproject/abstract-provider" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - -"@ethersproject/address@5.6.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.0.tgz" - integrity sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ== - dependencies: - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/rlp" "^5.6.0" - -"@ethersproject/base64@5.6.0", "@ethersproject/base64@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.6.0.tgz" - integrity sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw== - dependencies: - "@ethersproject/bytes" "^5.6.0" - -"@ethersproject/basex@5.6.0", "@ethersproject/basex@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.6.0.tgz" - integrity sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ== - dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - -"@ethersproject/bignumber@5.6.0", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.6.0.tgz" - integrity sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA== - dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - bn.js "^4.11.9" - -"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.6.0": - version "5.6.1" - resolved "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.6.1.tgz" - integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== - dependencies: - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/constants@5.6.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.6.0.tgz" - integrity sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA== - dependencies: - "@ethersproject/bignumber" "^5.6.0" - -"@ethersproject/contracts@5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.6.0.tgz" - integrity sha512-74Ge7iqTDom0NX+mux8KbRUeJgu1eHZ3iv6utv++sLJG80FVuU9HnHeKVPfjd9s3woFhaFoQGf3B3iH/FrQmgw== - dependencies: - "@ethersproject/abi" "^5.6.0" - "@ethersproject/abstract-provider" "^5.6.0" - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/address" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" - -"@ethersproject/hash@5.6.0", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.6.0.tgz" - integrity sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA== - dependencies: - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/address" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - -"@ethersproject/hdnode@5.6.0", "@ethersproject/hdnode@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.6.0.tgz" - integrity sha512-61g3Jp3nwDqJcL/p4nugSyLrpl/+ChXIOtCEM8UDmWeB3JCAt5FoLdOMXQc3WWkc0oM2C0aAn6GFqqMcS/mHTw== - dependencies: - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/basex" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/pbkdf2" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/sha2" "^5.6.0" - "@ethersproject/signing-key" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" - "@ethersproject/wordlists" "^5.6.0" - -"@ethersproject/json-wallets@5.6.0", "@ethersproject/json-wallets@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz" - integrity sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ== - dependencies: - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/address" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/hdnode" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/pbkdf2" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" + integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" + integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.6.0", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.6.0.tgz" - integrity sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w== +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== dependencies: - "@ethersproject/bytes" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.6.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.6.0.tgz" - integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg== - -"@ethersproject/networks@5.6.2", "@ethersproject/networks@^5.6.0": - version "5.6.2" - resolved "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.2.tgz" - integrity sha512-9uEzaJY7j5wpYGTojGp8U89mSsgQLc40PCMJLMCnFXTs7nhBveZ0t7dbqWUNrepWTszDbFkYD6WlL8DKx5huHA== - dependencies: - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/pbkdf2@5.6.0", "@ethersproject/pbkdf2@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz" - integrity sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ== - dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/sha2" "^5.6.0" - -"@ethersproject/properties@5.6.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.6.0.tgz" - integrity sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg== - dependencies: - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/providers@5.6.4": - version "5.6.4" - resolved "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.4.tgz" - integrity sha512-WAdknnaZ52hpHV3qPiJmKx401BLpup47h36Axxgre9zT+doa/4GC/Ne48ICPxTm0BqndpToHjpLP1ZnaxyE+vw== - dependencies: - "@ethersproject/abstract-provider" "^5.6.0" - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/address" "^5.6.0" - "@ethersproject/basex" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/hash" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.0" - "@ethersproject/rlp" "^5.6.0" - "@ethersproject/sha2" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" - "@ethersproject/web" "^5.6.0" +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" + integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" bech32 "1.1.4" ws "7.4.6" -"@ethersproject/random@5.6.0", "@ethersproject/random@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/random/-/random-5.6.0.tgz" - integrity sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw== +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.6.0", "@ethersproject/rlp@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.6.0.tgz" - integrity sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g== +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/sha2@5.6.0", "@ethersproject/sha2@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.6.0.tgz" - integrity sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA== +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.6.0", "@ethersproject/signing-key@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.6.0.tgz" - integrity sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA== +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - bn.js "^4.11.9" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/solidity@5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.6.0.tgz" - integrity sha512-YwF52vTNd50kjDzqKaoNNbC/r9kMDPq3YzDWmsjFTRBcIF1y4JCQJ8gB30wsTfHbaxgxelI5BfxQSxD/PbJOww== - dependencies: - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/sha2" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - -"@ethersproject/strings@5.6.0", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.6.0.tgz" - integrity sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg== - dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/transactions@5.6.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.6.0.tgz" - integrity sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg== - dependencies: - "@ethersproject/address" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/rlp" "^5.6.0" - "@ethersproject/signing-key" "^5.6.0" - -"@ethersproject/units@5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/units/-/units-5.6.0.tgz" - integrity sha512-tig9x0Qmh8qbo1w8/6tmtyrm/QQRviBh389EQ+d8fP4wDsBrJBf08oZfoiz1/uenKK9M78yAP4PoR7SsVoTjsw== - dependencies: - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/wallet@5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.6.0.tgz" - integrity sha512-qMlSdOSTyp0MBeE+r7SUhr1jjDlC1zAXB8VD84hCnpijPQiSNbxr6GdiLXxpUs8UKzkDiNYYC5DRI3MZr+n+tg== - dependencies: - "@ethersproject/abstract-provider" "^5.6.0" - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/address" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/hash" "^5.6.0" - "@ethersproject/hdnode" "^5.6.0" - "@ethersproject/json-wallets" "^5.6.0" - "@ethersproject/keccak256" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.0" - "@ethersproject/signing-key" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" - "@ethersproject/wordlists" "^5.6.0" - -"@ethersproject/web@5.6.0", "@ethersproject/web@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.6.0.tgz" - integrity sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg== - dependencies: - "@ethersproject/base64" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - -"@ethersproject/wordlists@5.6.0", "@ethersproject/wordlists@^5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.6.0.tgz" - integrity sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q== - dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/hash" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@ethersproject/solidity@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/units@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/wallet@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" + integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/json-wallets" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" + integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@humanwhocodes/config-array@^0.11.6": + version "0.11.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" + integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" - resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" - resolved "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== dependencies: ethereumjs-abi "^0.6.8" @@ -583,19 +435,24 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" -"@noble/hashes@1.0.0", "@noble/hashes@~1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz" - integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== +"@noble/hashes@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" + integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== -"@noble/secp256k1@1.5.5", "@noble/secp256k1@~1.5.2": - version "1.5.5" - resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.5.5.tgz" - integrity sha512-sZ1W6gQzYnu45wPrWx8D3kwI2/U29VYTx9OjbDAd7jwRItJ0cSTMPRL/C8AWZFn9kWFLQGqEXVEE86w4Z8LpIQ== +"@noble/hashes@~1.1.1": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.4.tgz#2611ebf5764c1bf754da7c7794de4fb30512336d" + integrity sha512-+PYsVPrTSqtVjatKt2A/Proukn2Yrz61OBThOCKErc5w2/r1Fh37vbDv0Eah7pyNltrmacjwTvdw3JoR+WE4TA== + +"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" + integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -603,97 +460,285 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomiclabs/hardhat-ethers@^2.0.4": - version "2.0.5" - resolved "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz" - integrity sha512-A2gZAGB6kUvLx+kzM92HKuUF33F1FSe90L0TmkXkT2Hh0OKRpvWZURUSU2nghD2yC4DzfEZ3DftfeHGvZ2JTUw== +"@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" -"@nomiclabs/hardhat-waffle@^2.0.1": - version "2.0.3" - resolved "https://registry.npmjs.org/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.3.tgz" - integrity sha512-049PHSnI1CZq6+XTbrMbMv5NaL7cednTfPenx02k3cEh8wBMLa6ys++dBETJa6JjfwgA9nBhhHQ173LJv6k2Pg== +"@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/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: - "@types/sinon-chai" "^3.2.3" - "@types/web3" "1.0.19" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + crc-32 "^1.2.0" -"@rari-capital/solmate@^6.2.0": - version "6.2.0" - resolved "https://registry.npmjs.org/@rari-capital/solmate/-/solmate-6.2.0.tgz" - integrity sha512-g94F+Ra9ixyJyNgvnOIufNjUz488uEG0nxIEEtJ7+g+tA1XGUupRB2kB5b+VO7WYO26RNOVD2fW6xE4e14iWpg== +"@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" -"@resolver-engine/core@^0.3.3": - version "0.3.3" - resolved "https://registry.npmjs.org/@resolver-engine/core/-/core-0.3.3.tgz" - integrity sha512-eB8nEbKDJJBi5p5SrvrvILn4a0h42bKtbCTri3ZxCGt6UvoQyp7HnGOfki944bUjBSHKK3RvgfViHn+kqdXtnQ== +"@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: - debug "^3.1.0" - is-url "^1.2.4" - request "^2.85.0" + "@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" -"@resolver-engine/fs@^0.3.3": - version "0.3.3" - resolved "https://registry.npmjs.org/@resolver-engine/fs/-/fs-0.3.3.tgz" - integrity sha512-wQ9RhPUcny02Wm0IuJwYMyAG8fXVeKdmhm8xizNByD4ryZlx6PP6kRen+t/haF43cMfmaV7T3Cx6ChOdHEhFUQ== +"@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/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: - "@resolver-engine/core" "^0.3.3" - debug "^3.1.0" + "@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" -"@resolver-engine/imports-fs@^0.3.3": - version "0.3.3" - resolved "https://registry.npmjs.org/@resolver-engine/imports-fs/-/imports-fs-0.3.3.tgz" - integrity sha512-7Pjg/ZAZtxpeyCFlZR5zqYkz+Wdo84ugB5LApwriT8XFeQoLwGUj4tZFFvvCuxaNCcqZzCYbonJgmGObYBzyCA== +"@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: - "@resolver-engine/fs" "^0.3.3" - "@resolver-engine/imports" "^0.3.3" - debug "^3.1.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + ethereum-cryptography "0.1.3" + readable-stream "^3.6.0" -"@resolver-engine/imports@^0.3.3": - version "0.3.3" - resolved "https://registry.npmjs.org/@resolver-engine/imports/-/imports-0.3.3.tgz" - integrity sha512-anHpS4wN4sRMwsAbMXhMfOD/y4a4Oo0Cw/5+rue7hSwGWsDOQaAU1ClK1OxjUC35/peazxEl8JaSRRS+Xb8t3Q== +"@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: - "@resolver-engine/core" "^0.3.3" - debug "^3.1.0" - hosted-git-info "^2.6.0" - path-browserify "^1.0.0" - url "^0.11.0" + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + ethereum-cryptography "0.1.3" -"@scure/base@~1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@scure/base/-/base-1.0.0.tgz" - integrity sha512-gIVaYhUsy+9s58m/ETjSJVKHhKTBMmcRb9cEV5/5dwvfDlfORjKrFsDeDHWRrm6RjcPvCLZFwGJjAjLj1gg4HA== +"@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== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "^4.0.0-beta.2" + ethereum-cryptography "0.1.3" -"@scure/bip32@1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.0.1.tgz" - integrity sha512-AU88KKTpQ+YpTLoicZ/qhFhRRIo96/tlb+8YmDDHR9yiKVjSsFZiefJO4wjS2PMTkz5/oIcw84uAq/8pleQURA== +"@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" + 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.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.5.tgz#75d08bd9bd84a3cb7950be6bd27171a294516b01" + integrity sha512-+W5C/+5FHI2xBajUN9THSNc1UP6FUsA7LeLmfnaC9VMi/50/DEjjxd8OmizEXgV1Bjck7my4NVQLL1Ti39FkpA== dependencies: - "@noble/hashes" "~1.0.0" - "@noble/secp256k1" "~1.5.2" - "@scure/base" "~1.0.0" + "@ethersproject/abi" "^5.1.2" + "@types/chai-as-promised" "^7.1.3" + chai-as-promised "^7.1.1" + chalk "^2.4.2" + deep-eql "^4.0.1" + ordinal "^1.0.3" -"@scure/bip39@1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.0.0.tgz" - integrity sha512-HrtcikLbd58PWOkl02k9V6nXWQyoa7A0+Ek9VF7z17DDk9XZAFUcIdqfh0jJXLypmizc5/8P6OxoUeKliiWv4w== +"@nomicfoundation/hardhat-network-helpers@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.7.tgz#9103be2b359899a8b7996f54df12a1b7977367e3" + integrity sha512-X+3mNvn8B7BY5hpIaLO+TrfzWq12bpux+ajGGdmdcfC78NXmYmOZkAtiz1QZx1YIZGMS1LaXzPXyBExxKFpCaw== + dependencies: + ethereumjs-util "^7.1.4" + +"@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" + integrity sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.0.tgz#1225f7da647ae1ad25a87125664704ecc0af6ccc" + integrity sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA== + +"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.0.tgz#dbc052dcdfd50ae50fd5ae1788b69b4e0fa40040" + integrity sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.0.tgz#e6b2eea633995b557e74e881d2a43eab4760903d" + integrity sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.0.tgz#af81107f5afa794f19988a368647727806e18dc4" + integrity sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.0.tgz#6877e1da1a06a9f08446070ab6e0a5347109f868" + integrity sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.0.tgz#bb6cd83a0c259eccef4183796b6329a66cf7ebd9" + integrity sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg== + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.0.tgz#9d4bca1cc9a1333fde985675083b0b7d165f6076" + integrity sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw== + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.0.tgz#0db5bfc6aa952bea4098d8d2c8947b4e5c4337ee" + integrity sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.0.tgz#2e0f39a2924dcd77db6b419828595e984fabcb33" + integrity sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz#e5ddc43ad5c0aab96e5054520d8e16212e125f50" + integrity sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.0" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.0" + "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.0" + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.0" + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.0" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.0" + +"@nomiclabs/hardhat-ethers@^2.0.6": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.1.tgz#8057b43566a0e41abeb8142064a3c0d3f23dca86" + integrity sha512-RHWYwnxryWR8hzRmU4Jm/q4gzvXpetUOJ4OPlwH2YARcDB+j79+yAYCwO0lN1SUOb4++oOTJEe6AWLEc42LIvg== + +"@nomiclabs/hardhat-etherscan@^3.1.0": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.3.tgz#c9dbaa4174edfa075a464a0e9142bc8710a2c4e2" + integrity sha512-UeNO97j0lwOHqX7mrH6SfQQBdXq1Ng6eFr7uJKuQOrq2UVTWGD70lE5QO4fAFVPz9ao+xlNpMyIqSR7+OaDR+Q== dependencies: - "@noble/hashes" "~1.0.0" - "@scure/base" "~1.0.0" + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" + debug "^4.1.1" + fs-extra "^7.0.1" + lodash "^4.17.11" + semver "^6.3.0" + table "^6.8.0" + undici "^5.4.0" + +"@rari-capital/solmate@^6.2.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@rari-capital/solmate/-/solmate-6.4.0.tgz#c6ee4110c8075f14b415e420b13bd8bdbbc93d9e" + integrity sha512-BXWIHHbG5Zbgrxi0qVYe0Zs+bfx+XgOciVUACjuIApV0KzC0kY8XdO1higusIei/ZKCC+GUKdcdQZflxYPUTKQ== + +"@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b" + integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q== + dependencies: + "@noble/hashes" "~1.1.1" + "@noble/secp256k1" "~1.6.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" + integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== + dependencies: + "@noble/hashes" "~1.1.1" + "@scure/base" "~1.1.0" "@sentry/core@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== dependencies: "@sentry/hub" "5.30.0" @@ -704,7 +749,7 @@ "@sentry/hub@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== dependencies: "@sentry/types" "5.30.0" @@ -713,7 +758,7 @@ "@sentry/minimal@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== dependencies: "@sentry/hub" "5.30.0" @@ -722,7 +767,7 @@ "@sentry/node@^5.18.1": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== dependencies: "@sentry/core" "5.30.0" @@ -737,7 +782,7 @@ "@sentry/tracing@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== dependencies: "@sentry/hub" "5.30.0" @@ -748,501 +793,356 @@ "@sentry/types@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== "@sentry/utils@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== dependencies: "@sentry/types" "5.30.0" tslib "^1.9.3" -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^5.2.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.3.0.tgz#0ec9264cf54a527671d990eb874e030b55b70dcc" + integrity sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": - version "0.14.1" - resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz" - integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== +"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1", "@solidity-parser/parser@^0.14.5": + version "0.14.5" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" + integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== dependencies: antlr4ts "^0.5.0-alpha.4" -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@truffle/error@^0.1.0": - version "0.1.0" - resolved "https://registry.npmjs.org/@truffle/error/-/error-0.1.0.tgz" - integrity sha512-RbUfp5VreNhsa2Q4YbBjz18rOQI909pG32bghl1hulO7IpvcqTS+C3Ge5cNbiWQ1WGzy1wIeKLW0tmQtHFB7qg== - -"@truffle/interface-adapter@^0.5.16": - version "0.5.16" - resolved "https://registry.npmjs.org/@truffle/interface-adapter/-/interface-adapter-0.5.16.tgz" - integrity sha512-4L8/TtFSe9eW4KWeXAvi3RrD0rImbLeYB4axPLOCAitUEDCTB/iJjZ1cMkC85LbO9mwz5/AjP0i37YO10rging== - dependencies: - bn.js "^5.1.3" - ethers "^4.0.32" - web3 "1.5.3" - -"@truffle/provider@^0.2.24": - version "0.2.54" - resolved "https://registry.npmjs.org/@truffle/provider/-/provider-0.2.54.tgz" - integrity sha512-BW2bb6p7dAipUCHlRDMSswFqessXkIb8tHVRVkm6KAENIor0F4UCCPlxIzrM/ShRQ1O16jZ+0cxLMwiRWTWdLg== +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== dependencies: - "@truffle/error" "^0.1.0" - "@truffle/interface-adapter" "^0.5.16" - web3 "1.5.3" + defer-to-connect "^2.0.1" "@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== "@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@typechain/ethers-v5@^10.0.0": - version "10.0.0" - resolved "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.0.0.tgz" - integrity sha512-Kot7fwAqnH96ZbI8xrRgj5Kpv9yCEdjo7mxRqrH7bYpEgijT5MmuOo8IVsdhOu7Uog4ONg7k/d5UdbAtTKUgsA== + version "10.1.1" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.1.1.tgz#fdb527d8854129cea5f139d76c6c6e1c9bb040ec" + integrity sha512-o6nffJBxwmeX1ZiZpdnP/tqGd/7M7iYvQC88ZXaFFoyAGh7eYncynzVjOJV0XmaKzAc6puqyqZrnva+gJbk4sw== dependencies: lodash "^4.17.15" ts-essentials "^7.0.1" -"@typechain/ethers-v5@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-2.0.0.tgz" - integrity sha512-0xdCkyGOzdqh4h5JSf+zoWx85IusEjDcPIwNEHP8mrWSnCae4rvrqB+/gtpdNfX7zjlFlZiMeePn2r63EI3Lrw== - dependencies: - ethers "^5.0.2" - "@typechain/hardhat@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.0.0.tgz" - integrity sha512-AnhwODKHxx3+st5uc1j2NQh79Lv2OuvDQe4dKn8ZxhqYsAsTPnHTLBeI8KPZ+mfdE7v13D2QYssRTIkkGhK35A== + version "6.1.4" + resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.4.tgz#da930bf17bdae5e0996b86d37992c6c58b8a49c8" + integrity sha512-S8k5d1Rjc+plwKpkorlifmh72M7Ki0XNUOVVLtdbcA/vLaEkuqZSJFdddpBgS5QxiJP+6CbRa/yO6EVTE2+fMQ== dependencies: fs-extra "^9.1.0" - lodash "^4.17.15" -"@types/abstract-leveldown@*": - version "7.2.0" - resolved "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz" - integrity sha512-q5veSX6zjUy/DlDhR4Y4cU0k2Ar+DT2LUraP00T19WLmTO6Se1djepCCaqU6nQrwcJ5Hyo/CWqxTzrrFg8eqbQ== +"@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/bn.js@*", "@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== dependencies: "@types/node" "*" -"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": - version "4.11.6" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== +"@types/bn.js@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" +"@types/chai-as-promised@^7.1.3": + version "7.1.5" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255" + integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ== + dependencies: + "@types/chai" "*" + "@types/chai@*", "@types/chai@^4.3.0": - version "4.3.1" - resolved "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz" - integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ== + version "4.3.4" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" + integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== "@types/concat-stream@^1.6.0": version "1.6.1" - resolved "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74" integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA== dependencies: "@types/node" "*" "@types/form-data@0.0.33": version "0.0.33" - resolved "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz" - integrity sha1-yayFsqX9GENbjIXZ7LUObWyJP/g= + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw== dependencies: "@types/node" "*" "@types/glob@^7.1.1": version "7.2.0" - resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== dependencies: "@types/minimatch" "*" "@types/node" "*" +"@types/http-cache-semantics@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + "@types/json-schema@^7.0.9": version "7.0.11" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - -"@types/level-errors@*": - version "3.0.0" - resolved "https://registry.npmjs.org/@types/level-errors/-/level-errors-3.0.0.tgz" - integrity sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ== - -"@types/levelup@^4.3.0": - version "4.3.3" - resolved "https://registry.npmjs.org/@types/levelup/-/levelup-4.3.3.tgz" - integrity sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA== - dependencies: - "@types/abstract-leveldown" "*" - "@types/level-errors" "*" - "@types/node" "*" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/lru-cache@^5.1.0": version "5.1.1" - resolved "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== "@types/minimatch@*": - version "3.0.5" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== - -"@types/mkdirp@^0.5.2": - version "0.5.2" - resolved "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-0.5.2.tgz" - integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== - dependencies: - "@types/node" "*" + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== -"@types/mocha@^9.0.0": - version "9.1.1" - resolved "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== +"@types/mocha@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" + integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== -"@types/node-fetch@^2.5.5": - version "2.6.1" - resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*", "@types/node@^17.0.8": - version "17.0.30" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.30.tgz" - integrity sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw== +"@types/node@*", "@types/node@^18.11.11": + version "18.11.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.11.tgz#1d455ac0211549a8409d3cdb371cd55cc971e8dc" + integrity sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g== "@types/node@^10.0.3": version "10.17.60" - resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^12.12.6": - version "12.20.50" - resolved "https://registry.npmjs.org/@types/node/-/node-12.20.50.tgz" - integrity sha512-+9axpWx2b2JCVovr7Ilgt96uc6C1zBKOQMpGtRbWT9IoR/8ue32GGMfGA4woP8QyP2gBs6GQWEVM3tCybGCxDA== - "@types/node@^8.0.0": version "8.10.66" - resolved "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== "@types/pbkdf2@^3.0.0": version "3.1.0" - resolved "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== dependencies: "@types/node" "*" "@types/prettier@^2.1.1": - version "2.6.0" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.0.tgz" - integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== + version "2.7.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" + integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== "@types/qs@^6.2.31": version "6.9.7" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/resolve@^0.0.8": - version "0.0.8" - resolved "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== - dependencies: - "@types/node" "*" - "@types/secp256k1@^4.0.1": version "4.0.3" - resolved "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== dependencies: "@types/node" "*" -"@types/sinon-chai@^3.2.3": - version "3.2.8" - resolved "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.8.tgz" - integrity sha512-d4ImIQbT/rKMG8+AXpmcan5T2/PNeSjrYhvkwet6z0p8kzYtfgA32xzOBlbU0yqJfq+/0Ml805iFoODO0LP5/g== - dependencies: - "@types/chai" "*" - "@types/sinon" "*" - -"@types/sinon@*": - version "10.0.11" - resolved "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz" - integrity sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g== - dependencies: - "@types/sinonjs__fake-timers" "*" - -"@types/sinonjs__fake-timers@*": - version "8.1.2" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz" - integrity sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA== - -"@types/underscore@*": - version "1.11.4" - resolved "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.4.tgz" - integrity sha512-uO4CD2ELOjw8tasUrAhvnn2W4A0ZECOvMjCivJr4gA9pGgjv+qxKWY9GLTMVEK8ej85BxQOocUyE7hImmSQYcg== - -"@types/web3@1.0.19": - version "1.0.19" - resolved "https://registry.npmjs.org/@types/web3/-/web3-1.0.19.tgz" - integrity sha512-fhZ9DyvDYDwHZUp5/STa9XW2re0E8GxoioYJ4pEUZ13YHpApSagixj7IAdoYH5uAK+UalGq6Ml8LYzmgRA/q+A== - dependencies: - "@types/bn.js" "*" - "@types/underscore" "*" +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== "@typescript-eslint/eslint-plugin@^5.9.1": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz" - integrity sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg== - dependencies: - "@typescript-eslint/scope-manager" "5.21.0" - "@typescript-eslint/type-utils" "5.21.0" - "@typescript-eslint/utils" "5.21.0" - debug "^4.3.2" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz#ee5b51405f6c9ee7e60e4006d68c69450d3b4536" + integrity sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw== + dependencies: + "@typescript-eslint/scope-manager" "5.45.1" + "@typescript-eslint/type-utils" "5.45.1" + "@typescript-eslint/utils" "5.45.1" + debug "^4.3.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" regexpp "^3.2.0" - semver "^7.3.5" + semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.9.1": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.21.0.tgz" - integrity sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg== - dependencies: - "@typescript-eslint/scope-manager" "5.21.0" - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/typescript-estree" "5.21.0" - debug "^4.3.2" - -"@typescript-eslint/scope-manager@5.21.0": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz" - integrity sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ== - dependencies: - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/visitor-keys" "5.21.0" - -"@typescript-eslint/type-utils@5.21.0": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz" - integrity sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw== - dependencies: - "@typescript-eslint/utils" "5.21.0" - debug "^4.3.2" + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.1.tgz#6440ec283fa1373a12652d4e2fef4cb6e7b7e8c6" + integrity sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA== + dependencies: + "@typescript-eslint/scope-manager" "5.45.1" + "@typescript-eslint/types" "5.45.1" + "@typescript-eslint/typescript-estree" "5.45.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.45.1": + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz#5b87d025eec7035d879b99c260f03be5c247883c" + integrity sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ== + dependencies: + "@typescript-eslint/types" "5.45.1" + "@typescript-eslint/visitor-keys" "5.45.1" + +"@typescript-eslint/type-utils@5.45.1": + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz#cb7d300c3c95802cea9f87c7f8be363cf8f8538c" + integrity sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA== + dependencies: + "@typescript-eslint/typescript-estree" "5.45.1" + "@typescript-eslint/utils" "5.45.1" + debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.21.0": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz" - integrity sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA== +"@typescript-eslint/types@5.45.1": + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.1.tgz#8e1883041cee23f1bb7e1343b0139f97f6a17c14" + integrity sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg== -"@typescript-eslint/typescript-estree@5.21.0": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz" - integrity sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg== +"@typescript-eslint/typescript-estree@5.45.1": + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz#b3dc37f0c4f0fe73e09917fc735e6f96eabf9ba4" + integrity sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng== dependencies: - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/visitor-keys" "5.21.0" - debug "^4.3.2" - globby "^11.0.4" + "@typescript-eslint/types" "5.45.1" + "@typescript-eslint/visitor-keys" "5.45.1" + debug "^4.3.4" + globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.5" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.21.0": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz" - integrity sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q== +"@typescript-eslint/utils@5.45.1": + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.45.1.tgz#39610c98bde82c4792f2a858b29b7d0053448be2" + integrity sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.21.0" - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/typescript-estree" "5.21.0" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.45.1" + "@typescript-eslint/types" "5.45.1" + "@typescript-eslint/typescript-estree" "5.45.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" + semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.21.0": - version "5.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz" - integrity sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA== +"@typescript-eslint/visitor-keys@5.45.1": + version "5.45.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz#204428430ad6a830d24c5ac87c71366a1cfe1948" + integrity sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ== dependencies: - "@typescript-eslint/types" "5.21.0" - eslint-visitor-keys "^3.0.0" - -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -"@yarnpkg/lockfile@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz" - integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + "@typescript-eslint/types" "5.45.1" + eslint-visitor-keys "^3.3.0" abbrev@1: version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== abbrev@1.0.x: version "1.0.9" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz" - integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" -abstract-leveldown@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz" - integrity sha512-KUWx9UWGQD12zsmLNj64/pndaz4iJh/Pj7nopgkfDG6RlCcbMZvT6+9l7dchK4idog2Is8VdC/PvNbFuFmalIQ== - dependencies: - xtend "~4.0.0" - -abstract-leveldown@^2.4.1, abstract-leveldown@~2.7.1: - version "2.7.2" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz" - integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== - dependencies: - xtend "~4.0.0" - -abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz" - integrity sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A== - dependencies: - xtend "~4.0.0" - -abstract-leveldown@^6.2.1: - version "6.3.0" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz" - integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - -abstract-leveldown@~2.6.0: - version "2.6.3" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz" - integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== - dependencies: - xtend "~4.0.0" - -abstract-leveldown@~6.2.1: - version "6.2.3" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz" - integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-jsx@^5.0.0, acorn-jsx@^5.3.1: +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" + +acorn-jsx@^5.0.0, acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: version "8.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^6.0.7: version "6.4.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^8.4.1, acorn@^8.7.0: - version "8.7.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.4.1, acorn@^8.8.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== address@^1.0.1: - version "1.2.0" - resolved "https://registry.npmjs.org/address/-/address-1.2.0.tgz" - integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig== + version "1.2.1" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.1.tgz#25bb61095b7522d65b357baa11bc05492d4c8acd" + integrity sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA== adm-zip@^0.4.16: version "0.4.16" - resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== aes-js@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz" - integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= - -aes-js@^3.1.1: - version "3.1.2" - resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz" - integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== agent-base@6: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -1250,7 +1150,7 @@ aggregate-error@^3.0.0: ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1258,972 +1158,351 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" + integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + amdefine@>=0.0.4: version "1.0.1" - resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== ansi-colors@3.2.3: version "3.2.3" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-colors@4.1.1, ansi-colors@^4.1.1: +ansi-colors@4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-escapes@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-escapes@^4.3.0: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - ansi-regex@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== ansi-regex@^4.1.0: version "4.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz" - integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== antlr4@4.7.1: version "4.7.1" - resolved "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" - resolved "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== anymatch@~3.1.1, anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-back@^1.0.3, array-back@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz" - integrity sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs= - dependencies: - typical "^2.6.0" - -array-back@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz" - integrity sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw== - dependencies: - typical "^2.6.1" - array-back@^3.0.1, array-back@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== array-back@^4.0.1, array-back@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz" - integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== array-includes@^3.1.4: - version "3.1.4" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-uniq@1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" + integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + asap@~2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== asn1@~0.2.3: version "0.2.6" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== assertion-error@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - ast-parents@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz" - integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== astral-regex@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: +async-eventemitter@^0.2.4: version "0.2.4" - resolved "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz" + 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-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async@1.x, async@2.6.2, async@>=2.6.4, async@^1.4.2, async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: - version "3.2.3" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" - integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== +async@1.x, async@>=2.6.4, async@^2.4.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== at-least-node@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - aws-sign2@~0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: version "1.11.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -babel-code-frame@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-core@^6.0.14, babel-core@^6.26.0: - version "6.26.3" - resolved "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz" - integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.1" - debug "^2.6.9" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.8" - slash "^1.0.0" - source-map "^0.5.7" - -babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz" - integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz" - integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ= - dependencies: - babel-helper-explode-assignable-expression "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-call-delegate@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz" - integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340= - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-define-map@^6.24.1: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz" - integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8= - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-explode-assignable-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz" - integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo= - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-function-name@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz" - integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk= - dependencies: - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-get-function-arity@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz" - integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-hoist-variables@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz" - integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-optimise-call-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz" - integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-regex@^6.24.1: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz" - integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI= - dependencies: - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-remap-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz" - integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs= - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-replace-supers@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz" - integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo= - dependencies: - babel-helper-optimise-call-expression "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz" - integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz" - integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-check-es2015-constants@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz" - integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-syntax-async-functions@^6.8.0: - version "6.13.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz" - integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= - -babel-plugin-syntax-exponentiation-operator@^6.8.0: - version "6.13.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz" - integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= - -babel-plugin-syntax-trailing-function-commas@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz" - integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM= - -babel-plugin-transform-async-to-generator@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz" - integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E= - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-arrow-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz" - integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz" - integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoping@^6.23.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz" - integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8= - dependencies: - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-plugin-transform-es2015-classes@^6.23.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz" - integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs= - dependencies: - babel-helper-define-map "^6.24.1" - babel-helper-function-name "^6.24.1" - babel-helper-optimise-call-expression "^6.24.1" - babel-helper-replace-supers "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz" - integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-destructuring@^6.23.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz" - integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz" - integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-for-of@^6.23.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz" - integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz" - integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos= - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz" - integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz" - integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ= - dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: - version "6.26.2" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz" - integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== - dependencies: - babel-plugin-transform-strict-mode "^6.24.1" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-types "^6.26.0" - -babel-plugin-transform-es2015-modules-systemjs@^6.23.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz" - integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM= - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-umd@^6.23.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz" - integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg= - dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz" - integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40= - dependencies: - babel-helper-replace-supers "^6.24.1" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-parameters@^6.23.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz" - integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys= - dependencies: - babel-helper-call-delegate "^6.24.1" - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz" - integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-spread@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz" - integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz" - integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw= - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-template-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz" - integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-typeof-symbol@^6.23.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz" - integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz" - integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek= - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - regexpu-core "^2.0.0" - -babel-plugin-transform-exponentiation-operator@^6.22.0: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz" - integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4= - dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" - babel-plugin-syntax-exponentiation-operator "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-regenerator@^6.22.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz" - integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8= - dependencies: - regenerator-transform "^0.10.0" - -babel-plugin-transform-strict-mode@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz" - integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-preset-env@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz" - integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg== - dependencies: - babel-plugin-check-es2015-constants "^6.22.0" - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-to-generator "^6.22.0" - babel-plugin-transform-es2015-arrow-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.23.0" - babel-plugin-transform-es2015-classes "^6.23.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" - babel-plugin-transform-es2015-destructuring "^6.23.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" - babel-plugin-transform-es2015-for-of "^6.23.0" - babel-plugin-transform-es2015-function-name "^6.22.0" - babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.22.0" - babel-plugin-transform-es2015-modules-commonjs "^6.23.0" - babel-plugin-transform-es2015-modules-systemjs "^6.23.0" - babel-plugin-transform-es2015-modules-umd "^6.23.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.23.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" - babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" - babel-plugin-transform-es2015-template-literals "^6.22.0" - babel-plugin-transform-es2015-typeof-symbol "^6.23.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-exponentiation-operator "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" - browserslist "^3.2.6" - invariant "^2.2.2" - semver "^5.3.0" - -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz" - integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= - dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-template@^6.24.1, babel-template@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz" - integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= - dependencies: - babel-runtime "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - lodash "^4.17.4" - -babel-traverse@^6.24.1, babel-traverse@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz" - integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= - dependencies: - babel-code-frame "^6.26.0" - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - debug "^2.6.8" - globals "^9.18.0" - invariant "^2.2.2" - lodash "^4.17.4" - -babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz" - integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - -babelify@^7.3.0: - version "7.3.0" - resolved "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz" - integrity sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU= - dependencies: - babel-core "^6.0.14" - object-assign "^4.0.0" - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - -backoff@^2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz" - integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= - dependencies: - precond "0.2" - balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.2, base-x@^3.0.8: +base-x@^3.0.2: version "3.0.9" - resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== 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" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - bcrypt-pbkdf@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" bech32@1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -bignumber.js@^9.0.0: - version "9.0.2" - resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz" - integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== +bigint-crypto-utils@^3.0.23: + version "3.1.7" + resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.1.7.tgz#c4c1b537c7c1ab7aadfaecf3edfd45416bf2c651" + integrity sha512-zpCQpIE2Oy5WIQpjC9iYZf8Uh9QqoS51ZCooAcNvzv1AQ3VWdT52D0ksr1+/faeK8HVIej1bxXcP75YcqH3KPA== + 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.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" + integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bip39@2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz" - integrity sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA== - dependencies: - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - safe-buffer "^5.0.1" - unorm "^1.3.3" - blakejs@^1.1.0: version "1.2.1" - resolved "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -bluebird@^3.5.0, bluebird@^3.5.2: - version "3.7.2" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - bn.js@4.11.6: version "4.11.6" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz" - integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.8.0: +bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -body-parser@1.20.0, body-parser@^1.16.0: - version "1.20.0" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz" - integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.10.3" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -brorand@^1.0.1, brorand@^1.1.0: +brorand@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + 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" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: +browserify-aes@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== dependencies: buffer-xor "^1.0.3" @@ -2233,66 +1512,16 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: inherits "^2.0.1" safe-buffer "^5.0.1" -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserslist@^3.2.6: - version "3.2.8" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz" - integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== - dependencies: - caniuse-lite "^1.0.30000844" - electron-to-chromium "^1.3.47" - bs58@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== dependencies: base-x "^3.0.2" bs58check@^2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== dependencies: bs58 "^4.0.0" @@ -2301,40 +1530,26 @@ bs58check@^2.1.2: buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-to-arraybuffer@^0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz" - integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= +buffer-reverse@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" + integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== buffer-xor@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer-xor@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz" - integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== - dependencies: - safe-buffer "^5.1.1" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== +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.1.13" - -bufferutil@^4.0.1: - version "4.0.6" - resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz" - integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw== - dependencies: - node-gyp-build "^4.3.0" + ieee754 "^1.2.1" builtins@^5.0.1: version "5.0.1" @@ -2343,65 +1558,39 @@ builtins@^5.0.1: dependencies: semver "^7.0.0" +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + bytes@3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -bytewise-core@^1.2.2: - version "1.2.3" - resolved "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz" - integrity sha1-P7QQx+kVWOsasiqCg0V3qmvWHUI= - dependencies: - typewise-core "^1.2" - -bytewise@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz" - integrity sha1-HRPL/3F65xWAlKqIGzXQgbOHJT4= - dependencies: - bytewise-core "^1.2.2" - typewise "^1.0.3" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -cachedown@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/cachedown/-/cachedown-1.0.0.tgz" - integrity sha1-1D8DbkUQaWsxJG19sx6/D3rDLRU= - dependencies: - abstract-leveldown "^2.4.1" - lru-cache "^3.2.0" - -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.1: + version "10.2.3" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.3.tgz#25277efe121308ab722c28b4164e51382b4adeb1" + integrity sha512-6BehRBOs7iurNjAYN9iPazTwFDaMQavJO8W1MEm3s2pH8q/tkPTtLDRUZaweWK87WFGf2Y5wLAlaCJlR5kOz3w== + dependencies: + "@types/http-cache-semantics" "^4.0.1" + get-stream "^6.0.1" + http-cache-semantics "^4.1.0" + keyv "^4.5.2" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -2409,70 +1598,73 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: caller-callsite@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== dependencies: callsites "^2.0.0" caller-path@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== dependencies: caller-callsite "^2.0.0" callsites@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^6.0.0: version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30000844: - version "1.0.30001338" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001338.tgz#b5dd7a7941a51a16480bdf6ff82bded1628eec0d" - integrity sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ== - caseless@^0.12.0, caseless@~0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +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== + +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + +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" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" chai@^4.3.4: - version "4.3.6" - resolved "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz" - integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" - deep-eql "^3.0.1" + deep-eql "^4.1.2" get-func-name "^2.0.0" loupe "^2.3.1" pathval "^1.1.1" type-detect "^4.0.5" -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -2481,7 +1673,7 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -2489,29 +1681,22 @@ chalk@^4.0.0, chalk@^4.1.0: chardet@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== "charenc@>= 0.0.1": version "0.0.2" - resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== check-error@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= - -checkpoint-store@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz" - integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= - dependencies: - functional-red-black-tree "^1.0.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== chokidar@3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== dependencies: anymatch "~3.1.1" @@ -2526,7 +1711,7 @@ chokidar@3.3.0: chokidar@3.5.3, chokidar@^3.4.0: version "3.5.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" @@ -2539,72 +1724,57 @@ chokidar@3.5.3, chokidar@^3.4.0: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -cids@^0.7.1: - version "0.7.5" - resolved "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz" - integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== - dependencies: - buffer "^5.5.0" - class-is "^1.1.0" - multibase "~0.6.0" - multicodec "^1.0.0" - multihashes "~0.4.15" - cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" - resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" -class-is@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz" - integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== +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: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" + 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" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== dependencies: restore-cursor "^2.0.0" cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-table3@^0.5.0: version "0.5.1" - resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== dependencies: object-assign "^4.1.0" @@ -2614,7 +1784,7 @@ cli-table3@^0.5.0: cli-truncate@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: slice-ansi "^3.0.0" @@ -2622,7 +1792,7 @@ cli-truncate@^2.1.0: cli-truncate@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== dependencies: slice-ansi "^5.0.0" @@ -2630,21 +1800,12 @@ cli-truncate@^3.1.0: cli-width@^2.0.0: version "2.2.1" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== dependencies: string-width "^3.1.0" @@ -2653,96 +1814,62 @@ cliui@^5.0.0: cliui@^7.0.2: version "7.0.4" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -clone@2.1.2, clone@^2.0.0: - version "2.1.2" - resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.16: - version "2.0.16" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== +colorette@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== colors@1.4.0, colors@^1.1.2: version "1.4.0" - resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" command-exists@^1.2.8: version "1.2.9" - resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -command-line-args@^4.0.7: - version "4.0.7" - resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-4.0.7.tgz" - integrity sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA== - dependencies: - array-back "^2.0.0" - find-replace "^1.0.3" - typical "^2.6.1" - command-line-args@^5.1.1: version "5.2.1" - resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== dependencies: array-back "^3.1.0" @@ -2752,7 +1879,7 @@ command-line-args@^5.1.1: command-line-usage@^6.1.0: version "6.1.3" - resolved "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== dependencies: array-back "^4.0.2" @@ -2762,32 +1889,22 @@ command-line-usage@^6.1.0: commander@2.18.0: version "2.18.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== commander@3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +commander@^9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" + integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== -concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@^1.6.2: +concat-stream@^1.6.0, concat-stream@^1.6.2: version "1.6.2" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" @@ -2795,90 +1912,29 @@ concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@^1.6.2: readable-stream "^2.2.2" typedarray "^0.0.6" -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-hash@^2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz" - integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== - dependencies: - cids "^0.7.1" - multicodec "^0.5.5" - multihashes "^0.4.15" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@^1.5.1: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - cookie@^0.4.1: version "0.4.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookiejar@^2.1.1: - version "2.1.3" - resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz" - integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-js-pure@^3.0.1: - version "3.22.3" - resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.3.tgz" - integrity sha512-oN88zz7nmKROMy8GOjs+LN+0LedIvbMdnB5XsTlhcOg1WGARt9l0LFg0zohdoFmCsEZ1h2ZbSQ6azj3M+vhzwQ== - -core-js@^2.4.0, core-js@^2.5.0: - version "2.6.12" - resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +cookiejar@>=2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== core-util-is@1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@^2.8.1: - version "2.8.5" - resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - cosmiconfig@^5.0.7: version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== dependencies: import-fresh "^2.0.0" @@ -2888,20 +1944,12 @@ cosmiconfig@^5.0.7: crc-32@^1.2.0: version "1.2.2" - resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - 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" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== dependencies: cipher-base "^1.0.1" @@ -2910,9 +1958,9 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: +create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" - resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== dependencies: cipher-base "^1.0.3" @@ -2924,10 +1972,10 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@>=3.1.5, cross-fetch@^2.1.0, cross-fetch@^2.1.1: +cross-fetch@>=3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== @@ -2936,7 +1984,7 @@ cross-fetch@>=3.1.5, cross-fetch@^2.1.0, cross-fetch@^2.1.1: cross-spawn@^6.0.5: version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" @@ -2947,7 +1995,7 @@ cross-spawn@^6.0.5: cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -2956,331 +2004,183 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: "crypt@>= 0.0.1": version "0.0.2" - resolved "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= - -crypto-browserify@3.12.0: - version "3.12.0" - resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== dashdash@^1.12.0: version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== dependencies: assert-plus "^1.0.0" data-uri-to-buffer@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b" integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== death@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/death/-/death-1.1.0.tgz" - integrity sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg= - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" + resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" + integrity sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w== debug@3.2.6: version "3.2.6" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" -debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@4.3.3: - version "4.3.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - ms "2.1.2" + ms "2.0.0" -debug@^3.1.0, debug@^3.2.7: +debug@^3.2.7: version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -decamelize@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - decamelize@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^3.2.0, decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: - mimic-response "^1.0.0" + mimic-response "^3.1.0" -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== +deep-eql@^4.0.1, deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" -deep-equal@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - deep-extend@~0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -deferred-leveldown@~1.2.1: - version "1.2.2" - resolved "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz" - integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== - dependencies: - abstract-leveldown "~2.6.0" - -deferred-leveldown@~4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz" - integrity sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww== - dependencies: - abstract-leveldown "~5.0.0" - inherits "^2.0.3" - -deferred-leveldown@~5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz" - integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== - dependencies: - abstract-leveldown "~6.2.1" - inherits "^2.0.3" +defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== dependencies: has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -defined@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" + object-keys "^1.1.1" -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz" - integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= - dependencies: - repeating "^2.0.0" +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== detect-port@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz" - integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" + integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ== dependencies: address "^1.0.1" - debug "^2.6.0" + debug "4" diff@3.5.0: version "3.5.0" - resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== diff@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" + heap ">= 0.2.0" dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" -dom-walk@^0.1.0: - version "0.1.2" - resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz" - integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== - dotenv@^16.0.0: - version "16.0.0" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz" - integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== - -dotignore@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz" - integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== - dependencies: - minimatch "^3.0.4" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== ecc-jsbn@~0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -electron-to-chromium@^1.3.47: - version "1.4.137" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f" - integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA== - -elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.4, elliptic@>=6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: version "6.5.4" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: bn.js "^4.11.9" @@ -3291,206 +2191,115 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emoji-regex@^10.0.0: - version "10.1.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.1.0.tgz" - integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== +emoji-regex@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f" + integrity sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA== emoji-regex@^7.0.1: version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -encoding-down@5.0.4, encoding-down@~5.0.0: - version "5.0.4" - resolved "https://registry.npmjs.org/encoding-down/-/encoding-down-5.0.4.tgz" - integrity sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw== - dependencies: - abstract-leveldown "^5.0.0" - inherits "^2.0.3" - level-codec "^9.0.0" - level-errors "^2.0.0" - xtend "^4.0.1" - -encoding-down@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz" - integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== - dependencies: - abstract-leveldown "^6.2.1" - inherits "^2.0.3" - level-codec "^9.0.0" - level-errors "^2.0.0" - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - enquirer@^2.3.0: version "2.3.6" - resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -errno@~0.1.1: - version "0.1.8" - resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.5, es-abstract@^1.19.1, es-abstract@^1.19.2: - version "1.19.5" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz" - integrity sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" - -es-abstract@^1.19.0, es-abstract@^1.19.5: - version "1.20.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6" - integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA== +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.20.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" + get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" has "^1.0.3" has-property-descriptors "^1.0.0" has-symbols "^1.0.3" internal-slot "^1.0.3" - is-callable "^1.2.4" + is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" is-weakref "^1.0.2" - object-inspect "^1.12.0" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.4.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + es-shim-unscopables@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== dependencies: has "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.61" - resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz" - integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@1.8.x: version "1.8.1" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz" - integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A== dependencies: esprima "^2.7.1" estraverse "^1.9.1" @@ -3501,7 +2310,7 @@ escodegen@1.8.x: eslint-config-prettier@^8.3.0: version "8.5.0" - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== eslint-config-standard@^17.0.0: @@ -3511,19 +2320,18 @@ eslint-config-standard@^17.0.0: eslint-import-resolver-node@^0.3.6: version "0.3.6" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: debug "^3.2.7" resolve "^1.20.0" eslint-module-utils@^2.7.3: - version "2.7.3" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== dependencies: debug "^3.2.7" - find-up "^2.1.0" eslint-plugin-es@^4.1.0: version "4.1.0" @@ -3535,7 +2343,7 @@ eslint-plugin-es@^4.1.0: eslint-plugin-import@^2.25.4: version "2.26.0" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: array-includes "^3.1.4" @@ -3553,34 +2361,34 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.14.1" eslint-plugin-n@^15.2.0: - version "15.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.2.1.tgz#e62cf800da076ac5a970eb7554efa2136ebfa194" - integrity sha512-uMG50pvKqXK9ab163bSI5OpyZR0F5yIB0pEC4ciGpBLrXVjVDOlx5oTq8GQULWzbelJt7wL5Rw4T+FfAff5Cxg== + version "15.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.6.0.tgz#cfb1d2e2e427d620eb9008f8b3b5a40de0c84120" + integrity sha512-Hd/F7wz4Mj44Jp0H6Jtty13NcE69GNTY0rVlgTIj1XBnGGVI6UTdDrpE6vqu3AHo07bygq/N+7OH/lgz1emUJw== dependencies: builtins "^5.0.1" eslint-plugin-es "^4.1.0" eslint-utils "^3.0.0" ignore "^5.1.1" - is-core-module "^2.9.0" + is-core-module "^2.11.0" minimatch "^3.1.2" - resolve "^1.10.1" - semver "^7.3.7" + resolve "^1.22.1" + semver "^7.3.8" eslint-plugin-prettier@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz" - integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" eslint-plugin-promise@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz" - integrity sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw== + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== eslint-scope@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== dependencies: esrecurse "^4.1.0" @@ -3588,7 +2396,7 @@ eslint-scope@^4.0.3: eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -3596,7 +2404,7 @@ eslint-scope@^5.1.1: eslint-scope@^7.1.1: version "7.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" @@ -3604,43 +2412,43 @@ eslint-scope@^7.1.1: eslint-utils@^1.3.1: version "1.4.3" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: +eslint-visitor-keys@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^5.6.0: version "5.16.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== dependencies: "@babel/code-frame" "^7.0.0" @@ -3681,12 +2489,14 @@ eslint@^5.6.0: text-table "^0.2.0" eslint@^8.6.0: - version "8.14.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz" - integrity sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw== - dependencies: - "@eslint/eslintrc" "^1.2.2" - "@humanwhocodes/config-array" "^0.9.2" + version "8.29.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87" + integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg== + dependencies: + "@eslint/eslintrc" "^1.3.3" + "@humanwhocodes/config-array" "^0.11.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -3696,122 +2506,98 @@ eslint@^8.6.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.1" + espree "^9.4.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.6.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.15.0" + grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" regexpp "^3.2.0" strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" espree@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== dependencies: acorn "^6.0.7" acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" -espree@^9.3.1: - version "9.3.1" - resolved "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz" - integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== +espree@^9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" + integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== dependencies: - acorn "^8.7.0" - acorn-jsx "^5.3.1" + acorn "^8.8.0" + acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" esprima@2.7.x, esprima@^2.7.1: version "2.7.3" - resolved "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" - integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== esprima@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1, esquery@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.1.0, esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^1.9.1: version "1.9.3" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz" - integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -eth-block-tracker@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz" - integrity sha512-WUVxWLuhMmsfenfZvFO5sbl1qFY2IqUlw/FPVmjjdElpqLsZtSG+wPe9Dz7W/sB6e80HgFKknOmKk2eNlznHug== - dependencies: - eth-query "^2.1.0" - ethereumjs-tx "^1.3.3" - ethereumjs-util "^5.1.3" - ethjs-util "^0.1.3" - json-rpc-engine "^3.6.0" - pify "^2.3.0" - tape "^4.6.3" - -eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: - version "2.0.8" - resolved "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz" - integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= - dependencies: - idna-uts46-hx "^2.3.1" - js-sha3 "^0.5.7" - -eth-gas-reporter@^0.2.24: +eth-gas-reporter@^0.2.25: version "0.2.25" - resolved "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz#546dfa946c1acee93cb1a94c2a1162292d6ff566" integrity sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ== dependencies: "@ethersproject/abi" "^5.0.0-beta.146" @@ -3830,130 +2616,16 @@ eth-gas-reporter@^0.2.24: sha1 "^1.1.1" sync-request "^6.0.0" -eth-json-rpc-infura@^3.1.0: - version "3.2.1" - resolved "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.1.tgz" - integrity sha512-W7zR4DZvyTn23Bxc0EWsq4XGDdD63+XPUCEhV2zQvQGavDVC4ZpFDK4k99qN7bd7/fjj37+rxmuBOBeIqCA5Mw== - dependencies: - cross-fetch "^2.1.1" - eth-json-rpc-middleware "^1.5.0" - json-rpc-engine "^3.4.0" - json-rpc-error "^2.0.0" - -eth-json-rpc-middleware@^1.5.0: - version "1.6.0" - resolved "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz" - integrity sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q== - dependencies: - async "^2.5.0" - eth-query "^2.1.2" - eth-tx-summary "^3.1.2" - ethereumjs-block "^1.6.0" - ethereumjs-tx "^1.3.3" - ethereumjs-util "^5.1.2" - ethereumjs-vm "^2.1.0" - fetch-ponyfill "^4.0.0" - json-rpc-engine "^3.6.0" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - tape "^4.6.3" - -eth-lib@0.2.8: - version "0.2.8" - resolved "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz" - integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - xhr-request-promise "^0.1.2" - -eth-lib@^0.1.26: - version "0.1.29" - resolved "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz" - integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - nano-json-stream-parser "^0.1.2" - servify "^0.1.12" - ws "^3.0.0" - xhr-request-promise "^0.1.2" - -eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz" - integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= - dependencies: - json-rpc-random-id "^1.0.0" - xtend "^4.0.1" - -eth-sig-util@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-3.0.0.tgz" - integrity sha512-4eFkMOhpGbTxBQ3AMzVf0haUX2uTur7DpWiHzWyTURa28BVJJtOkcb9Ok5TV0YvEPG61DODPW7ZUATbJTslioQ== - dependencies: - buffer "^5.2.1" - elliptic "^6.4.0" - ethereumjs-abi "0.6.5" - ethereumjs-util "^5.1.1" - tweetnacl "^1.0.0" - tweetnacl-util "^0.15.0" - -eth-sig-util@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-1.4.2.tgz#8d958202c7edbaae839707fba6f09ff327606210" - integrity sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA= - dependencies: - ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" - ethereumjs-util "^5.1.1" - -eth-tx-summary@^3.1.2: - version "3.2.4" - resolved "https://registry.npmjs.org/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz" - integrity sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg== - dependencies: - async "^2.1.2" - clone "^2.0.0" - concat-stream "^1.5.1" - end-of-stream "^1.1.0" - eth-query "^2.0.2" - ethereumjs-block "^1.4.1" - ethereumjs-tx "^1.1.1" - ethereumjs-util "^5.0.1" - ethereumjs-vm "^2.6.0" - through2 "^2.0.3" - -ethashjs@~0.0.7: - version "0.0.8" - resolved "https://registry.npmjs.org/ethashjs/-/ethashjs-0.0.8.tgz" - integrity sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw== - dependencies: - async "^2.1.2" - buffer-xor "^2.0.1" - ethereumjs-util "^7.0.2" - miller-rabin "^4.0.0" - ethereum-bloom-filters@^1.0.6: version "1.0.10" - resolved "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== dependencies: js-sha3 "^0.8.0" -ethereum-common@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz" - integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== - -ethereum-common@^0.0.18: - version "0.0.18" - resolved "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz" - integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= - -ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: version "0.1.3" - resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== dependencies: "@types/pbkdf2" "^3.0.0" @@ -3973,129 +2645,26 @@ ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: setimmediate "^1.0.5" ethereum-cryptography@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.0.3.tgz" - integrity sha512-NQLTW0x0CosoVb/n79x/TRHtfvS3hgNUPTUSCu0vM+9k6IIhHFFrAOJReneexjZsoZxMjJHnJn4lrE8EbnSyqQ== - dependencies: - "@noble/hashes" "1.0.0" - "@noble/secp256k1" "1.5.5" - "@scure/bip32" "1.0.1" - "@scure/bip39" "1.0.0" - -ethereum-waffle@^3.4.0: - version "3.4.4" - resolved "https://registry.npmjs.org/ethereum-waffle/-/ethereum-waffle-3.4.4.tgz" - integrity sha512-PA9+jCjw4WC3Oc5ocSMBj5sXvueWQeAbvCA+hUlb6oFgwwKyq5ka3bWQ7QZcjzIX+TdFkxP4IbFmoY2D8Dkj9Q== - dependencies: - "@ethereum-waffle/chai" "^3.4.4" - "@ethereum-waffle/compiler" "^3.4.4" - "@ethereum-waffle/mock-contract" "^3.4.4" - "@ethereum-waffle/provider" "^3.4.4" - ethers "^5.0.1" - -ethereumjs-abi@0.6.5: - version "0.6.5" - resolved "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz" - integrity sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE= - dependencies: - bn.js "^4.10.0" - ethereumjs-util "^4.3.0" - -ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.8: - version "0.6.8" - resolved "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz" - integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d" + integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ== dependencies: - bn.js "^4.11.8" - ethereumjs-util "^6.0.0" + "@noble/hashes" "1.1.2" + "@noble/secp256k1" "1.6.3" + "@scure/bip32" "1.1.0" + "@scure/bip39" "1.1.0" -"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": +ethereumjs-abi@^0.6.8: version "0.6.8" - resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== dependencies: bn.js "^4.11.8" ethereumjs-util "^6.0.0" -ethereumjs-account@3.0.0, ethereumjs-account@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz" - integrity sha512-WP6BdscjiiPkQfF9PVfMcwx/rDvfZTjFKY0Uwc09zSQr9JfIVH87dYIJu0gNhBhpmovV4yq295fdllS925fnBA== - dependencies: - ethereumjs-util "^6.0.0" - rlp "^2.2.1" - safe-buffer "^5.1.1" - -ethereumjs-account@^2.0.3: - version "2.0.5" - resolved "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz" - integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== - dependencies: - ethereumjs-util "^5.0.0" - rlp "^2.0.0" - safe-buffer "^5.1.1" - -ethereumjs-block@2.2.2, ethereumjs-block@^2.2.2, ethereumjs-block@~2.2.0, ethereumjs-block@~2.2.2: - version "2.2.2" - resolved "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz" - integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== - dependencies: - async "^2.0.1" - ethereumjs-common "^1.5.0" - ethereumjs-tx "^2.1.1" - ethereumjs-util "^5.0.0" - merkle-patricia-tree "^2.1.2" - -ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: - version "1.7.1" - resolved "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz" - integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== - dependencies: - async "^2.0.1" - ethereum-common "0.2.0" - ethereumjs-tx "^1.2.2" - ethereumjs-util "^5.0.0" - merkle-patricia-tree "^2.1.2" - -ethereumjs-blockchain@^4.0.3: - version "4.0.4" - resolved "https://registry.npmjs.org/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.4.tgz" - integrity sha512-zCxaRMUOzzjvX78DTGiKjA+4h2/sF0OYL1QuPux0DHpyq8XiNoF5GYHtb++GUxVlMsMfZV7AVyzbtgcRdIcEPQ== - dependencies: - async "^2.6.1" - ethashjs "~0.0.7" - ethereumjs-block "~2.2.2" - ethereumjs-common "^1.5.0" - ethereumjs-util "^6.1.0" - flow-stoplight "^1.0.0" - level-mem "^3.0.1" - lru-cache "^5.1.1" - rlp "^2.2.2" - semaphore "^1.1.0" - -ethereumjs-common@1.5.0, ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz" - integrity sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ== - -ethereumjs-tx@2.1.2, ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz" - integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== - dependencies: - ethereumjs-common "^1.5.0" - ethereumjs-util "^6.0.0" - -ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: - version "1.3.7" - resolved "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz" - integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== - dependencies: - ethereum-common "^0.0.18" - ethereumjs-util "^5.0.0" - -ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0, ethereumjs-util@^6.2.1: +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: version "6.2.1" - resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== dependencies: "@types/bn.js" "^4.11.3" @@ -4106,34 +2675,10 @@ ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumj ethjs-util "0.1.6" rlp "^2.2.3" -ethereumjs-util@^4.3.0: - version "4.5.1" - resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.1.tgz" - integrity sha512-WrckOZ7uBnei4+AKimpuF1B3Fv25OmoRgmYCpGsP7u8PFxXAmAgiJSYT2kRWnt6fVIlKaQlZvuwXp7PIrmn3/w== - dependencies: - bn.js "^4.8.0" - create-hash "^1.1.2" - elliptic "^6.5.2" - ethereum-cryptography "^0.1.3" - rlp "^2.0.0" - -ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz" - integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== - dependencies: - bn.js "^4.11.0" - create-hash "^1.1.2" - elliptic "^6.5.2" - ethereum-cryptography "^0.1.3" - ethjs-util "^0.1.3" - rlp "^2.0.0" - safe-buffer "^5.1.1" - -ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.3, ethereumjs-util@^7.1.4: - version "7.1.4" - resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz" - integrity sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A== +ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== dependencies: "@types/bn.js" "^5.1.0" bn.js "^5.1.2" @@ -4141,67 +2686,14 @@ ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.1.0, ethereu ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethereumjs-vm@4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz" - integrity sha512-X6qqZbsY33p5FTuZqCnQ4+lo957iUJMM6Mpa6bL4UW0dxM6WmDSHuI4j/zOp1E2TDKImBGCJA9QPfc08PaNubA== - dependencies: - async "^2.1.2" - async-eventemitter "^0.2.2" - core-js-pure "^3.0.1" - ethereumjs-account "^3.0.0" - ethereumjs-block "^2.2.2" - ethereumjs-blockchain "^4.0.3" - ethereumjs-common "^1.5.0" - ethereumjs-tx "^2.1.2" - ethereumjs-util "^6.2.0" - fake-merkle-patricia-tree "^1.0.1" - functional-red-black-tree "^1.0.1" - merkle-patricia-tree "^2.3.2" - rustbn.js "~0.2.0" - safe-buffer "^5.1.1" - util.promisify "^1.0.0" - -ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: - version "2.6.0" - resolved "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz" - integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== - dependencies: - async "^2.1.2" - async-eventemitter "^0.2.2" - ethereumjs-account "^2.0.3" - ethereumjs-block "~2.2.0" - ethereumjs-common "^1.1.0" - ethereumjs-util "^6.0.0" - fake-merkle-patricia-tree "^1.0.1" - functional-red-black-tree "^1.0.1" - merkle-patricia-tree "^2.3.2" - rustbn.js "~0.2.0" - safe-buffer "^5.1.1" - -ethereumjs-wallet@0.6.5: - version "0.6.5" - resolved "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz" - integrity sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA== - dependencies: - aes-js "^3.1.1" - bs58check "^2.1.2" - ethereum-cryptography "^0.1.3" - ethereumjs-util "^6.0.0" - randombytes "^2.0.6" - safe-buffer "^5.1.2" - scryptsy "^1.2.1" - utf8 "^3.0.0" - uuid "^3.3.2" - ethers-eip712@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/ethers-eip712/-/ethers-eip712-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/ethers-eip712/-/ethers-eip712-0.2.0.tgz#52973b3a9a22638f7357283bf66624994c6e91ed" integrity sha512-fgS196gCIXeiLwhsWycJJuxI9nL/AoUPGSQ+yvd+8wdWR+43G+J1n69LmWVWvAON0M6qNaf2BF4/M159U8fujQ== -ethers@^4.0.32, ethers@^4.0.40: +ethers@^4.0.40: version "4.0.49" - resolved "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg== dependencies: aes-js "3.0.0" @@ -4214,53 +2706,53 @@ ethers@^4.0.32, ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.5.3: - version "5.6.4" - resolved "https://registry.npmjs.org/ethers/-/ethers-5.6.4.tgz" - integrity sha512-62UIfxAQXdf67TeeOaoOoPctm5hUlYgfd0iW3wxfj7qRYKDcvvy0f+sJ3W2/Pyx77R8dblvejA8jokj+lS+ATQ== - dependencies: - "@ethersproject/abi" "5.6.1" - "@ethersproject/abstract-provider" "5.6.0" - "@ethersproject/abstract-signer" "5.6.0" - "@ethersproject/address" "5.6.0" - "@ethersproject/base64" "5.6.0" - "@ethersproject/basex" "5.6.0" - "@ethersproject/bignumber" "5.6.0" - "@ethersproject/bytes" "5.6.1" - "@ethersproject/constants" "5.6.0" - "@ethersproject/contracts" "5.6.0" - "@ethersproject/hash" "5.6.0" - "@ethersproject/hdnode" "5.6.0" - "@ethersproject/json-wallets" "5.6.0" - "@ethersproject/keccak256" "5.6.0" - "@ethersproject/logger" "5.6.0" - "@ethersproject/networks" "5.6.2" - "@ethersproject/pbkdf2" "5.6.0" - "@ethersproject/properties" "5.6.0" - "@ethersproject/providers" "5.6.4" - "@ethersproject/random" "5.6.0" - "@ethersproject/rlp" "5.6.0" - "@ethersproject/sha2" "5.6.0" - "@ethersproject/signing-key" "5.6.0" - "@ethersproject/solidity" "5.6.0" - "@ethersproject/strings" "5.6.0" - "@ethersproject/transactions" "5.6.0" - "@ethersproject/units" "5.6.0" - "@ethersproject/wallet" "5.6.0" - "@ethersproject/web" "5.6.0" - "@ethersproject/wordlists" "5.6.0" +ethers@^5.5.3: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" ethjs-unit@0.1.6: version "0.1.6" - resolved "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz" - integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== dependencies: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: +ethjs-util@0.1.6, ethjs-util@^0.1.6: version "0.1.6" - resolved "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== dependencies: is-hex-prefixed "1.0.0" @@ -4268,168 +2760,70 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: 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" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@4.0.4: - version "4.0.4" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz" - integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== - -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: +evp_bytestokey@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== +execa@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" + integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== dependencies: cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" + get-stream "^6.0.1" + human-signals "^3.0.1" + is-stream "^3.0.0" merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -express@^4.14.0: - version "4.18.0" - resolved "https://registry.npmjs.org/express/-/express-4.18.0.tgz" - integrity sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.0" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.10.3" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.6.0" - resolved "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz" - integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== - dependencies: - type "^2.5.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" extend@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" tmp "^0.0.33" -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extsprintf@1.3.0, extsprintf@^1.2.0: +extsprintf@1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== -fake-merkle-patricia-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz" - integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= - dependencies: - checkpoint-store "^1.1.0" +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.2.0" - resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.0.3, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4439,150 +2833,89 @@ fast-glob@^3.0.3, fast-glob@^3.2.9: fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.14.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce" + integrity sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg== dependencies: reusify "^1.0.4" fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.1.5" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.5.tgz" - integrity sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== dependencies: node-domexception "^1.0.0" web-streams-polyfill "^3.0.3" -fetch-ponyfill@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz" - integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= - dependencies: - node-fetch "~1.7.1" - figures@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== dependencies: flat-cache "^2.0.1" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-replace@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/find-replace/-/find-replace-1.0.3.tgz" - integrity sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A= - dependencies: - array-back "^1.0.4" - test-value "^2.1.0" - find-replace@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== dependencies: array-back "^3.0.1" find-up@3.0.0, find-up@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" path-exists "^4.0.0" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - find-up@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== dependencies: locate-path "^2.0.0" -find-yarn-workspace-root@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz" - integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== - dependencies: - fs-extra "^4.0.3" - micromatch "^3.1.4" - -find-yarn-workspace-root@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz" - integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== - dependencies: - micromatch "^4.0.2" - flat-cache@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== dependencies: flatted "^2.0.0" @@ -4591,87 +2924,54 @@ flat-cache@^2.0.1: flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" rimraf "^3.0.2" -flat@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz" - integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== - dependencies: - is-buffer "~2.0.3" - -flat@^5.0.2: +flat@>=5.0.1, flat@^4.1.0, flat@^5.0.2: version "5.0.2" - resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== - -flow-stoplight@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/flow-stoplight/-/flow-stoplight-1.0.0.tgz" - integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s= + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== follow-redirects@^1.12.1: - version "1.14.9" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== - -for-each@^0.3.3, for-each@~0.3.3: - version "0.3.3" - resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== forever-agent@~0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== form-data@^2.2.0: version "2.5.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" mime-types "^2.1.12" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@~2.3.2: version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" @@ -4680,42 +2980,25 @@ form-data@~2.3.2: formdata-polyfill@^4.0.10: version "4.0.10" - resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== dependencies: fetch-blob "^3.1.2" -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - fp-ts@1.19.3: version "1.19.3" - resolved "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== fp-ts@^1.0.0: version "1.19.5" - resolved "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.5.tgz" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - fs-extra@^0.30.0: version "0.30.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz" - integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA== dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" @@ -4723,18 +3006,9 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^4.0.2, fs-extra@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== dependencies: graceful-fs "^4.1.2" @@ -4743,7 +3017,7 @@ fs-extra@^7.0.0, fs-extra@^7.0.1: fs-extra@^8.1.0: version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: graceful-fs "^4.2.0" @@ -4752,7 +3026,7 @@ fs-extra@^8.1.0: fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -4760,36 +3034,36 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^1.2.7: - version "1.2.7" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: - minipass "^2.6.0" + minipass "^3.0.0" fs-readdir-recursive@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.1.1: version "2.1.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== fsevents@~2.3.2: version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== function.prototype.name@^1.1.5: @@ -4802,129 +3076,63 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: +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 sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -ganache-core@^2.13.2: - version "2.13.2" - resolved "https://registry.npmjs.org/ganache-core/-/ganache-core-2.13.2.tgz" - integrity sha512-tIF5cR+ANQz0+3pHWxHjIwHqFXcVo0Mb+kcsNhglNFALcYo49aQpnS9dqHartqPfMFjiHh/qFoD3mYK0d/qGgw== - dependencies: - abstract-leveldown "3.0.0" - async "2.6.2" - bip39 "2.5.0" - cachedown "1.0.0" - clone "2.1.2" - debug "3.2.6" - encoding-down "5.0.4" - eth-sig-util "3.0.0" - ethereumjs-abi "0.6.8" - ethereumjs-account "3.0.0" - ethereumjs-block "2.2.2" - ethereumjs-common "1.5.0" - ethereumjs-tx "2.1.2" - ethereumjs-util "6.2.1" - ethereumjs-vm "4.2.0" - heap "0.2.6" - keccak "3.0.1" - level-sublevel "6.6.4" - levelup "3.1.1" - lodash "4.17.20" - lru-cache "5.1.1" - merkle-patricia-tree "3.0.0" - patch-package "6.2.2" - seedrandom "3.0.1" - source-map-support "0.5.12" - tmp "0.1.0" - web3-provider-engine "14.2.1" - websocket "1.0.32" - optionalDependencies: - ethereumjs-wallet "0.6.5" - web3 "1.2.11" - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-func-name@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-port@^3.1.0: - version "3.2.0" - resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz" - integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== dependencies: - pump "^3.0.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-port@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== -get-stream@^6.0.0: +get-stream@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - getpass@^0.1.1: version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: assert-plus "^1.0.0" ghost-testrpc@^0.0.2: version "0.0.2" - resolved "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz" + resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92" integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ== dependencies: chalk "^2.4.2" @@ -4932,21 +3140,21 @@ ghost-testrpc@^0.0.2: glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob@7.1.3: version "7.1.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" @@ -4958,7 +3166,7 @@ glob@7.1.3: glob@7.1.7: version "7.1.7" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -4968,9 +3176,9 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0, glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@~7.2.0: +glob@7.2.0: version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" @@ -4982,8 +3190,8 @@ glob@7.2.0, glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@~7.2.0: glob@^5.0.15: version "5.0.15" - resolved "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA== dependencies: inflight "^1.0.4" inherits "2" @@ -4991,50 +3199,49 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.0.0, glob@^7.1.2, glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-modules@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== dependencies: global-prefix "^3.0.0" global-prefix@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== dependencies: ini "^1.3.5" kind-of "^6.0.2" which "^1.3.1" -global@~4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/global/-/global-4.4.0.tgz" - integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== - dependencies: - min-document "^2.19.0" - process "^0.11.10" - globals@^11.7.0: version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: - version "13.13.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz" - integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== +globals@^13.15.0: + version "13.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" + integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== dependencies: type-fest "^0.20.2" -globals@^9.18.0: - version "9.18.0" - resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz" - integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== - globby@^10.0.1: version "10.0.2" - resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== dependencies: "@types/glob" "^7.1.1" @@ -5046,9 +3253,9 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^11.0.4: +globby@^11.1.0: version "11.1.0" - resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -5058,56 +3265,41 @@ globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" -got@9.6.0: - version "9.6.0" - resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -got@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +got@>=11.8.5: + version "12.5.3" + resolved "https://registry.yarnpkg.com/got/-/got-12.5.3.tgz#82bdca2dd61258a02e24d668ea6e7abb70ac3598" + integrity sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.1" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + growl@1.10.5: version "1.10.5" - resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== handlebars@^4.0.1: version "4.7.7" - resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" @@ -5119,40 +3311,45 @@ handlebars@^4.0.1: har-schema@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== har-validator@~5.1.3: version "5.1.5" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" har-schema "^2.0.0" hardhat-gas-reporter@^1.0.7: - version "1.0.8" - resolved "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.8.tgz" - integrity sha512-1G5thPnnhcwLHsFnl759f2tgElvuwdkzxlI65fC9PwxYMEe9cmjkVAAWTf3/3y8uP6ZSPiUiOW8PgZnykmZe0g== + version "1.0.9" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz#9a2afb354bc3b6346aab55b1c02ca556d0e16450" + integrity sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg== dependencies: array-uniq "1.0.3" - eth-gas-reporter "^0.2.24" + eth-gas-reporter "^0.2.25" sha1 "^1.1.1" -"hardhat@https://github.com/0age/hardhat/releases/download/viaIR-2.9.3/hardhat-v2.9.3.tgz": - version "2.9.3" - resolved "https://github.com/0age/hardhat/releases/download/viaIR-2.9.3/hardhat-v2.9.3.tgz" - integrity sha512-+7Oz41IJLHmJXuL0/FqE9k3VaA4qJlUiU3j/bg3lq0yh3O6Oy6677cdVZU80Wc9MPpQv8BzLwvfT1UbmABWo3Q== +hardhat@^2.12.1-ir.0: + version "2.12.1-ir.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.12.1-ir.0.tgz#4972a0777e5806cce6b54cf258c52570c58d141a" + integrity sha512-z5TGBaf3tpY92zcDmifPyYdQa1EGuLAcOlmqQUd0wR93Yua8UCdNyZHk+P2PNBJK4sm/DYt5d3DJbaFMN24sog== dependencies: - "@ethereumjs/block" "^3.6.0" - "@ethereumjs/blockchain" "^5.5.0" - "@ethereumjs/common" "^2.6.0" - "@ethereumjs/tx" "^3.4.0" - "@ethereumjs/vm" "^5.6.0" "@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/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" - "@solidity-parser/parser" "^0.14.1" "@types/bn.js" "^5.1.0" "@types/lru-cache" "^5.1.0" abort-controller "^3.0.0" @@ -5165,133 +3362,80 @@ hardhat-gas-reporter@^1.0.7: debug "^4.1.1" enquirer "^2.3.0" env-paths "^2.2.0" - ethereum-cryptography "^0.1.2" + ethereum-cryptography "^1.0.3" ethereumjs-abi "^0.6.8" - ethereumjs-util "^7.1.3" find-up "^2.1.0" fp-ts "1.19.3" fs-extra "^7.0.1" - glob "^7.1.3" + glob "7.2.0" immutable "^4.0.0-rc.12" io-ts "1.10.4" + keccak "^3.0.2" lodash "^4.17.11" - merkle-patricia-tree "^4.2.2" mnemonist "^0.38.0" - mocha "^9.2.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" - slash "^3.0.0" solc "0.7.3" source-map-support "^0.5.13" stacktrace-parser "^0.1.10" - "true-case-path" "^2.2.1" tsort "0.0.1" - undici "^4.14.1" + undici "^5.4.0" uuid "^8.3.2" ws "^7.4.6" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== dependencies: get-intrinsic "^1.1.1" -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - -has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.0, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.3, has@~1.0.3: +has@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hash-base@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== dependencies: inherits "^2.0.4" @@ -5300,7 +3444,7 @@ hash-base@^3.0.0: hash.js@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== dependencies: inherits "^2.0.3" @@ -5308,7 +3452,7 @@ hash.js@1.1.3: hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" @@ -5316,39 +3460,26 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: he@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -heap@0.2.6: - version "0.2.6" - resolved "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" - integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= +"heap@>= 0.2.0": + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== hmac-drbg@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz" - integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" - -hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: - version "2.8.9" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - http-basic@^8.1.1: version "8.1.3" - resolved "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz" + resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== dependencies: caseless "^0.12.0" @@ -5356,14 +3487,14 @@ http-basic@^8.1.1: http-response-object "^3.0.1" parse-cache-control "^1.0.1" -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: depd "2.0.0" @@ -5372,95 +3503,86 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-https@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz" - integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= - http-response-object@^3.0.1: version "3.0.2" - resolved "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== dependencies: "@types/node" "^10.0.3" http-signature@~1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^2.1.10: + version "2.2.0" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.0.tgz#b80ad199d216b7d3680195077bd7b9060fa9d7f3" + integrity sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" + integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== husky@>=6: - version "7.0.4" - resolved "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz" - integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== + version "8.0.2" + resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.2.tgz#5816a60db02650f1f22c8b69b928fd6bcd77a236" + integrity sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -idna-uts46-hx@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz" - integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== - dependencies: - punycode "2.1.0" - -ieee754@^1.1.13: +ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^4.0.6: version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1, ignore@^5.1.8, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -immediate@^3.2.3, immediate@~3.2.3: - version "3.2.3" - resolved "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz" - integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= +ignore@^5.1.1, ignore@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c" + integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA== immutable@^4.0.0-rc.12: - version "4.0.0" - resolved "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz" - integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== import-fresh@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== dependencies: caller-path "^2.0.0" resolve-from "^3.0.0" import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -5468,35 +3590,35 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.5: version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inquirer@^6.2.2: version "6.5.2" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== dependencies: ansi-escapes "^3.2.0" @@ -5515,7 +3637,7 @@ inquirer@^6.2.2: internal-slot@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: get-intrinsic "^1.1.0" @@ -5524,436 +3646,220 @@ internal-slot@^1.0.3: interpret@^1.0.0: version "1.4.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - io-ts@1.10.4: version "1.10.4" - resolved "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== dependencies: fp-ts "^1.0.0" -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: has-bigints "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.2" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-buffer@~2.0.3: +is-buffer@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" +is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.11.0, is-core-module@^2.8.1, is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - is-date-object@^1.0.1: version "1.0.5" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - is-directory@^0.3.1: version "0.3.1" - resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - -is-fn@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz" - integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== -is-function@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz" - integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== - -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-hex-prefixed@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz" - integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== is-negative-zero@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: version "1.0.7" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-object@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz" - integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== - -is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-obj@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.0.4, is-regex@^1.1.4, is-regex@~1.1.4: +is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-retry-allowed@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-shared-array-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== dependencies: call-bind "^1.0.2" -is-stream@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.3, is-typed-array@^1.1.7: - version "1.1.8" - resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz" - integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-url@^1.2.4: - version "1.2.4" - resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - is-weakref@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^2.1.1: - version "2.2.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@~1.0.0: +isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isstream@~0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" +js-sdsl@^4.1.4: + version "4.2.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0" + integrity sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ== -js-sha3@0.5.7, js-sha3@^0.5.7: +js-sha3@0.5.7: version "0.5.7" - resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz" - integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" - resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - js-yaml@3.13.1: version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" @@ -5961,7 +3867,7 @@ js-yaml@3.13.1: js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -5969,478 +3875,208 @@ js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsbn@~0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz" - integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: - version "3.8.0" - resolved "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz" - integrity sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA== - dependencies: - async "^2.0.1" - babel-preset-env "^1.7.0" - babelify "^7.3.0" - json-rpc-error "^2.0.0" - promise-to-callback "^1.0.0" - safe-event-emitter "^1.0.1" - -json-rpc-error@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/json-rpc-error/-/json-rpc-error-2.0.0.tgz" - integrity sha1-p6+cICg4tekFxyUOVH8a/3cligI= - dependencies: - inherits "^2.0.1" - -json-rpc-random-id@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz" - integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= - json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.4.0: +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.4.0, json-schema@>=0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stringify-safe@~5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" +json5@>=1.0.2, json5@^1.0.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^2.1.0: version "2.4.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz" - integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== optionalDependencies: graceful-fs "^4.1.6" jsonfile@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - -jsonschema@^1.2.4: - version "1.4.0" - resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz" - integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== - -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -keccak@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz" - integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== - dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - -keccak@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== - dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - readable-stream "^3.6.0" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -klaw-sync@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz" - integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== - dependencies: - graceful-fs "^4.1.11" - -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz" - integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= - optionalDependencies: - graceful-fs "^4.1.9" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -level-codec@^9.0.0: - version "9.0.2" - resolved "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz" - integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== - dependencies: - buffer "^5.6.0" - -level-codec@~7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz" - integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== - -level-concat-iterator@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz" - integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== - -level-errors@^1.0.3, level-errors@~1.0.3: - version "1.0.5" - resolved "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz" - integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== - dependencies: - errno "~0.1.1" - -level-errors@^2.0.0, level-errors@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz" - integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== - dependencies: - errno "~0.1.1" - -level-iterator-stream@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz" - integrity sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig== +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: - inherits "^2.0.1" - readable-stream "^2.0.5" - xtend "^4.0.0" + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" -level-iterator-stream@~1.3.0: - version "1.3.1" - resolved "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz" - integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= - dependencies: - inherits "^2.0.1" - level-errors "^1.0.3" - readable-stream "^1.0.33" - xtend "^4.0.0" +jsonschema@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== -level-iterator-stream@~3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz" - integrity sha512-nEIQvxEED9yRThxvOrq8Aqziy4EGzrxSZK+QzEFAVuJvQ8glfyZ96GB6BoI4sBbLfjMXm2w4vu3Tkcm9obcY0g== +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: - inherits "^2.0.1" - readable-stream "^2.3.6" - xtend "^4.0.0" + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" -level-iterator-stream@~4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz" - integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== +keccak@^3.0.0, keccak@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== dependencies: - inherits "^2.0.4" - readable-stream "^3.4.0" - xtend "^4.0.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" -level-mem@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/level-mem/-/level-mem-3.0.1.tgz" - integrity sha512-LbtfK9+3Ug1UmvvhR2DqLqXiPW1OJ5jEh0a3m9ZgAipiwpSxGj/qaVVy54RG5vAQN1nCuXqjvprCuKSCxcJHBg== +keyv@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" + integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== dependencies: - level-packager "~4.0.0" - memdown "~3.0.0" + json-buffer "3.0.1" -level-mem@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/level-mem/-/level-mem-5.0.1.tgz" - integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== - dependencies: - level-packager "^5.0.3" - memdown "^5.0.0" +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -level-packager@^5.0.3: - version "5.1.1" - resolved "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz" - integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== - dependencies: - encoding-down "^6.3.0" - levelup "^4.3.2" +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== + optionalDependencies: + graceful-fs "^4.1.9" -level-packager@~4.0.0: +level-supports@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/level-packager/-/level-packager-4.0.1.tgz" - integrity sha512-svCRKfYLn9/4CoFfi+d8krOtrp6RoX8+xm0Na5cgXMqSyRru0AnDYdLl+YI8u1FyS6gGZ94ILLZDE5dh2but3Q== - dependencies: - encoding-down "~5.0.0" - levelup "^3.0.0" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== -level-post@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/level-post/-/level-post-1.0.7.tgz" - integrity sha512-PWYqG4Q00asOrLhX7BejSajByB4EmG2GaKHfj3h5UmmZ2duciXLPGYWIjBzLECFWUGOZWlm5B20h/n3Gs3HKew== - dependencies: - ltgt "^2.1.2" - -level-sublevel@6.6.4: - version "6.6.4" - resolved "https://registry.npmjs.org/level-sublevel/-/level-sublevel-6.6.4.tgz" - integrity sha512-pcCrTUOiO48+Kp6F1+UAzF/OtWqLcQVTVF39HLdZ3RO8XBoXt+XVPKZO1vVr1aUoxHZA9OtD2e1v7G+3S5KFDA== - dependencies: - bytewise "~1.1.0" - level-codec "^9.0.0" - level-errors "^2.0.0" - level-iterator-stream "^2.0.3" - ltgt "~2.1.1" - pull-defer "^0.2.2" - pull-level "^2.0.3" - pull-stream "^3.6.8" - typewiselite "~1.0.0" - xtend "~4.0.0" - -level-supports@~1.0.0: +level-transcoder@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz" - integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== - dependencies: - xtend "^4.0.2" - -level-ws@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz" - integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== dependencies: - readable-stream "~1.0.15" - xtend "~2.1.1" - -level-ws@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/level-ws/-/level-ws-1.0.0.tgz" - integrity sha512-RXEfCmkd6WWFlArh3X8ONvQPm8jNpfA0s/36M4QzLqrLEIt1iJE9WBHLZ5vZJK6haMjJPJGJCQWfjMNnRcq/9Q== - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.8" - xtend "^4.0.1" + buffer "^6.0.3" + module-error "^1.0.1" -level-ws@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/level-ws/-/level-ws-2.0.0.tgz" - integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== +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: - inherits "^2.0.3" - readable-stream "^3.1.0" - xtend "^4.0.1" - -levelup@3.1.1, levelup@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/levelup/-/levelup-3.1.1.tgz" - integrity sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg== - dependencies: - deferred-leveldown "~4.0.0" - level-errors "~2.0.0" - level-iterator-stream "~3.0.0" - xtend "~4.0.0" - -levelup@^1.2.1: - version "1.3.9" - resolved "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz" - integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== - dependencies: - deferred-leveldown "~1.2.1" - level-codec "~7.0.0" - level-errors "~1.0.3" - level-iterator-stream "~1.3.0" - prr "~1.0.1" - semver "~5.4.1" - xtend "~4.0.0" - -levelup@^4.3.2: - version "4.4.0" - resolved "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz" - integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== - dependencies: - deferred-leveldown "~5.3.0" - level-errors "~2.0.0" - level-iterator-stream "~4.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" + browser-level "^1.0.1" + classic-level "^1.2.0" levn@^0.3.0, levn@~0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" levn@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" type-check "~0.4.0" -lilconfig@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +lilconfig@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" + integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== lint-staged@>=10: - version "12.4.1" - resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz" - integrity sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg== + version "13.1.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.1.0.tgz#d4c61aec939e789e489fa51987ec5207b50fd37e" + integrity sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ== dependencies: cli-truncate "^3.1.0" - colorette "^2.0.16" - commander "^8.3.0" - debug "^4.3.3" - execa "^5.1.1" - lilconfig "2.0.4" - listr2 "^4.0.1" - micromatch "^4.0.4" + colorette "^2.0.19" + commander "^9.4.1" + debug "^4.3.4" + execa "^6.1.0" + lilconfig "2.0.6" + listr2 "^5.0.5" + micromatch "^4.0.5" normalize-path "^3.0.0" - object-inspect "^1.12.0" - pidtree "^0.5.0" + object-inspect "^1.12.2" + pidtree "^0.6.0" string-argv "^0.3.1" - supports-color "^9.2.1" - yaml "^1.10.2" + yaml "^2.1.3" -listr2@^4.0.1: - version "4.0.5" - resolved "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz" - integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== +listr2@^5.0.5: + version "5.0.6" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.6.tgz#3c61153383869ffaad08a8908d63edfde481dff8" + integrity sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag== dependencies: cli-truncate "^2.1.0" - colorette "^2.0.16" + colorette "^2.0.19" log-update "^4.0.0" p-map "^4.0.0" rfdc "^1.3.0" - rxjs "^7.5.5" + rxjs "^7.5.7" through "^2.3.8" wrap-ansi "^7.0.0" -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - locate-path@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== dependencies: p-locate "^2.0.0" path-exists "^3.0.0" locate-path@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" @@ -6448,41 +4084,41 @@ locate-path@^3.0.0: locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" -lodash.assign@^4.0.3: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz" - integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= - lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@4.17.20, lodash@>=4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4: +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash@>=4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== dependencies: chalk "^2.4.2" log-symbols@4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -6490,7 +4126,7 @@ log-symbols@4.1.0: log-update@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: ansi-escapes "^4.3.0" @@ -6498,387 +4134,208 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -looper@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/looper/-/looper-2.0.0.tgz" - integrity sha1-Zs0Md0rz1P7axTeU90LbVtqPCew= - -looper@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/looper/-/looper-3.0.0.tgz" - integrity sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k= - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== dependencies: get-func-name "^2.0.0" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== -lru-cache@5.1.1, lru-cache@^5.1.1: +lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + 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@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz" - integrity sha1-cXibO39Tmb7IVl3aOKow0qCX7+4= - dependencies: - pseudomap "^1.0.1" - lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" lru_map@^0.3.3: version "0.3.3" - resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz" - integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= - -ltgt@^2.1.2, ltgt@~2.1.1: - version "2.1.3" - resolved "https://registry.npmjs.org/ltgt/-/ltgt-2.1.3.tgz" - integrity sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ= - -ltgt@~2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz" - integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== make-error@^1.1.1: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - markdown-table@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== mcl-wasm@^0.7.1: version "0.7.9" - resolved "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" 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" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== dependencies: hash-base "^3.0.0" inherits "^2.0.1" safe-buffer "^5.1.2" -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -memdown@^1.0.0: - version "1.4.1" - resolved "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz" - integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= +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-leveldown "~2.7.1" + abstract-level "^1.0.0" functional-red-black-tree "^1.0.1" - immediate "^3.2.3" - inherits "~2.0.1" - ltgt "~2.2.0" - safe-buffer "~5.1.1" - -memdown@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/memdown/-/memdown-5.1.0.tgz" - integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== - dependencies: - abstract-leveldown "~6.2.1" - functional-red-black-tree "~1.0.1" - immediate "~3.2.3" - inherits "~2.0.1" - ltgt "~2.2.0" - safe-buffer "~5.2.0" - -memdown@~3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/memdown/-/memdown-3.0.0.tgz" - integrity sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA== - dependencies: - abstract-leveldown "~5.0.0" - functional-red-black-tree "~1.0.1" - immediate "~3.2.3" - inherits "~2.0.1" - ltgt "~2.2.0" - safe-buffer "~5.1.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" - integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -merkle-patricia-tree@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz" - integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== - dependencies: - async "^2.6.1" - ethereumjs-util "^5.2.0" - level-mem "^3.0.1" - level-ws "^1.0.0" - readable-stream "^3.0.6" - rlp "^2.0.0" - semaphore ">=1.0.1" - -merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz" - integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== - dependencies: - async "^1.4.2" - ethereumjs-util "^5.0.0" - level-ws "0.0.0" - levelup "^1.2.1" - memdown "^1.0.0" - readable-stream "^2.0.0" - rlp "^2.0.0" - semaphore ">=1.0.1" - -merkle-patricia-tree@^4.2.2, merkle-patricia-tree@^4.2.4: - version "4.2.4" - resolved "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz" - integrity sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w== - dependencies: - "@types/levelup" "^4.3.0" - ethereumjs-util "^7.1.4" - level-mem "^5.0.1" - level-ws "^2.0.0" - readable-stream "^3.6.0" - semaphore-async-await "^1.5.1" - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" +merkletreejs@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.3.9.tgz#cdb364a3b974a44f4eff3446522d7066e0cf95de" + integrity sha512-NjlATjJr4NEn9s8v/VEHhgwRWaE1eA/Une07d9SEqKzULJi1Wsh0Y3svwJdP2bYLMmgSBHzOrNydMWM1NN9VeQ== + dependencies: + bignumber.js "^9.0.1" + buffer-reverse "^1.0.1" + crypto-js "^3.1.9-1" + treeify "^1.1.0" + web3-utils "^1.3.4" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" picomatch "^2.3.1" -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - mime-db@1.52.0: version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - mimic-fn@^1.0.0: version "1.2.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= - dependencies: - dom-walk "^0.1.0" +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minipass@^2.6.0, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== +"minimatch@2 || 3", minimatch@3.0.4, minimatch@5.0.1, minimatch@>=3.0.5, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "5.1.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.1.tgz#6c9dffcf9927ff2a31e74b5af11adf8b9604b022" + integrity sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g== dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" + brace-expansion "^2.0.1" -minizlib@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" +minimist@>=1.2.6, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" + yallist "^4.0.0" -mkdirp-promise@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz" - integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: - mkdirp "*" - -mkdirp@*, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + minipass "^3.0.0" + yallist "^4.0.0" mkdirp@0.5.5: version "0.5.5" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@^0.5.5: +mkdirp@0.5.x, mkdirp@^0.5.1: version "0.5.6" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mnemonist@^0.38.0: version "0.38.5" - resolved "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== dependencies: obliterator "^2.0.0" -mocha@^7.1.1: - version "7.2.0" - resolved "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz" - integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== +mocha@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6" + integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" @@ -6905,296 +4362,222 @@ mocha@^7.1.1: yargs-parser "13.1.2" yargs-unparser "1.6.0" -mocha@^9.2.0: - version "9.2.2" - resolved "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz" - integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== +mocha@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.1.0.tgz#dbf1114b7c3f9d0ca5de3133906aea3dfc89ef7a" + integrity sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" chokidar "3.5.3" - debug "4.3.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" glob "7.2.0" - growl "1.10.5" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "4.2.1" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.1" + nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - workerpool "6.2.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" -mock-fs@^4.1.0: - version "4.14.0" - resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz" - integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== +mocha@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.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.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@2.1.3, ms@^2.1.1: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multibase@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz" - integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== - dependencies: - base-x "^3.0.8" - buffer "^5.5.0" - -multibase@~0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz" - integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== - dependencies: - base-x "^3.0.8" - buffer "^5.5.0" - -multicodec@^0.5.5: - version "0.5.7" - resolved "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz" - integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== - dependencies: - varint "^5.0.0" - -multicodec@^1.0.0: - version "1.0.4" - resolved "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz" - integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== - dependencies: - buffer "^5.6.0" - varint "^5.0.0" - -multihashes@^0.4.15, multihashes@~0.4.15: - version "0.4.21" - resolved "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz" - integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== - dependencies: - buffer "^5.5.0" - multibase "^0.7.0" - varint "^5.0.0" - mute-stream@0.0.7: version "0.0.7" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== -nano-json-stream-parser@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz" - integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= - -nanoid@3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== -natural-compare@^1.4.0: +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-lite@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== neo-async@^2.6.0: version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - nice-try@^1.0.4: version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-addon-api@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== node-domexception@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== node-emoji@^1.10.0: version "1.11.0" - resolved "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== dependencies: lodash "^4.17.21" node-environment-flags@1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== dependencies: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" -node-fetch@2.6.7, node-fetch@>=2.6.7, node-fetch@^2.6.1, node-fetch@~1.7.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.4.tgz#3fbca2d8838111048232de54cb532bd3cf134947" - integrity sha512-WvYJRN7mMyOLurFR2YpysQGuwYrJN+qrrpHjJDuKMcSPdfFccRUla/kng2mz6HWSBxJcqPbvatS6Gb4RhOzCJw== +node-fetch@2.6.7, node-fetch@>=2.6.7: + version "3.3.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.0.tgz#37e71db4ecc257057af828d523a7243d651d91e4" + integrity sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA== dependencies: data-uri-to-buffer "^4.0.0" fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.4.0" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== nopt@3.x: version "3.0.6" - resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg== dependencies: abbrev "1" -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +normalize-url@>=4.5.1, normalize-url@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.0.tgz#593dbd284f743e8dcf6a5ddf8fadff149c82701a" + integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== dependencies: - path-key "^3.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + path-key "^4.0.0" number-to-bn@1.7.0: version "1.7.0" - resolved "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz" - integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== dependencies: bn.js "4.11.6" strip-hex-prefix "1.0.0" oauth-sign@~0.9.0: version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4, object-assign@^4.0.0, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.0: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.12.0, object-inspect@^1.9.0, object-inspect@~1.12.0: - version "1.12.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" +object-inspect@^1.12.2, 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== object-keys@^1.0.11, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-keys@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" - integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - object.assign@4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== dependencies: define-properties "^1.1.2" @@ -7202,99 +4585,71 @@ object.assign@4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" - integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -obliterator@^2.0.0: - version "2.0.3" - resolved "https://registry.npmjs.org/obliterator/-/obliterator-2.0.3.tgz" - integrity sha512-qN5lHhArxl/789Bp3XCpssAYy7cvOdRzxzflmGEJaiipAT2b/USr1XvKjYyssPOwQ/3KjV1e8Ed9po9rie6E6A== - -oboe@2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz" - integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: - http-https "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" -oboe@2.1.5: +object.getownpropertydescriptors@^2.0.3: version "2.1.5" - resolved "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz" - integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" + integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== dependencies: - http-https "^1.0.0" + array.prototype.reduce "^1.0.5" + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== +object.values@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: - ee-first "1.1.1" + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + +once@1.x, once@^1.3.0, once@^1.3.1: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.0: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -open@^7.4.2: - version "7.4.2" - resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz" - integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" + mimic-fn "^4.0.0" optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -7306,7 +4661,7 @@ optionator@^0.8.1, optionator@^0.8.2: optionator@^0.9.1: version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -7316,268 +4671,153 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" +ordinal@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d" + integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ== -os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== p-limit@^1.1.0: version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.0.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== dependencies: p-limit "^1.1.0" p-locate@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-map@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= - dependencies: - p-finally "^1.0.0" - p-try@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - parse-cache-control@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz" - integrity sha1-juqz5U+laSD+Fro493+iGqzC104= - -parse-headers@^2.0.0: - version "2.0.5" - resolved "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz" - integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" + resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -patch-package@6.2.2: - version "6.2.2" - resolved "https://registry.npmjs.org/patch-package/-/patch-package-6.2.2.tgz" - integrity sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg== - dependencies: - "@yarnpkg/lockfile" "^1.1.0" - chalk "^2.4.2" - cross-spawn "^6.0.5" - find-yarn-workspace-root "^1.2.1" - fs-extra "^7.0.1" - is-ci "^2.0.0" - klaw-sync "^6.0.0" - minimist "^1.2.0" - rimraf "^2.6.3" - semver "^5.6.0" - slash "^2.0.0" - tmp "^0.0.33" - -patch-package@^6.2.2: - version "6.4.7" - resolved "https://registry.npmjs.org/patch-package/-/patch-package-6.4.7.tgz" - integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== - dependencies: - "@yarnpkg/lockfile" "^1.1.0" - chalk "^2.4.2" - cross-spawn "^6.0.5" - find-yarn-workspace-root "^2.0.0" - fs-extra "^7.0.1" - is-ci "^2.0.0" - klaw-sync "^6.0.0" - minimist "^1.2.0" - open "^7.4.2" - rimraf "^2.6.3" - semver "^5.6.0" - slash "^2.0.0" - tmp "^0.0.33" - -path-browserify@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" - integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: +path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-is-inside@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== path-key@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@>=1.0.7, path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pathval@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: +pbkdf2@^3.0.17: version "3.1.2" - resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== dependencies: create-hash "^1.1.2" @@ -7588,250 +4828,94 @@ pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: performance-now@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pidtree@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz" - integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== - -pify@^2.0.0, pify@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pify@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postinstall-postinstall@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz" - integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== - -precond@0.2: - version "0.2.3" - resolved "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz" - integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= - prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" -prettier-plugin-solidity@^1.0.0-beta.19: - version "1.0.0-beta.19" - resolved "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz" - integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== +prettier-plugin-solidity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.0.tgz#a417d104b48a43af3adbfb96b65dbce34fd21429" + integrity sha512-5gq0T49ifvXH/6x1STuKyWjTUgi6ICoV65yNtKlg/vZEvocFtSpByJOJICBfqPwNsnv4vhhWIqkLGSUJmWum2w== dependencies: - "@solidity-parser/parser" "^0.14.0" - emoji-regex "^10.0.0" + "@solidity-parser/parser" "^0.14.5" + emoji-regex "^10.2.1" escape-string-regexp "^4.0.0" - semver "^7.3.5" + semver "^7.3.8" solidity-comments-extractor "^0.0.7" string-width "^4.2.3" prettier@^1.14.3: version "1.19.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@^2.1.2, prettier@^2.3.1, prettier@^2.5.1: - version "2.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== - -private@^0.1.6, private@^0.1.8: - version "0.1.8" - resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== +prettier@^2.3.1, prettier@^2.5.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" + integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.10: - version "0.11.10" - resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - progress@^2.0.0: version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-to-callback@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/promise-to-callback/-/promise-to-callback-1.0.0.tgz" - integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= - dependencies: - is-fn "^1.0.0" - set-immediate-shim "^1.0.1" - promise@^8.0.0: - version "8.1.0" - resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz" - integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== dependencies: asap "~2.0.6" -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -pseudomap@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.28: - version "1.8.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pull-cat@^1.1.9: - version "1.1.11" - resolved "https://registry.npmjs.org/pull-cat/-/pull-cat-1.1.11.tgz" - integrity sha1-tkLdElXaN2pwa220+pYvX9t0wxs= - -pull-defer@^0.2.2: - version "0.2.3" - resolved "https://registry.npmjs.org/pull-defer/-/pull-defer-0.2.3.tgz" - integrity sha512-/An3KE7mVjZCqNhZsr22k1Tx8MACnUnHZZNPSJ0S62td8JtYr/AiRG42Vz7Syu31SoTLUzVIe61jtT/pNdjVYA== - -pull-level@^2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/pull-level/-/pull-level-2.0.4.tgz" - integrity sha512-fW6pljDeUThpq5KXwKbRG3X7Ogk3vc75d5OQU/TvXXui65ykm+Bn+fiktg+MOx2jJ85cd+sheufPL+rw9QSVZg== - dependencies: - level-post "^1.0.7" - pull-cat "^1.1.9" - pull-live "^1.0.1" - pull-pushable "^2.0.0" - pull-stream "^3.4.0" - pull-window "^2.1.4" - stream-to-pull-stream "^1.7.1" - -pull-live@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/pull-live/-/pull-live-1.0.1.tgz" - integrity sha1-pOzuAeMwFV6RJLu89HYfIbOPUfU= - dependencies: - pull-cat "^1.1.9" - pull-stream "^3.4.0" - -pull-pushable@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/pull-pushable/-/pull-pushable-2.2.0.tgz" - integrity sha1-Xy867UethpGfAbEqLpnW8b13ZYE= - -pull-stream@^3.2.3, pull-stream@^3.4.0, pull-stream@^3.6.8: - version "3.6.14" - resolved "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.14.tgz" - integrity sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew== - -pull-window@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/pull-window/-/pull-window-2.1.4.tgz" - integrity sha1-/DuG/uvRkgx64pdpHiP3BfiFUvA= - dependencies: - looper "^2.0.0" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz" - integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.10.3, qs@^6.4.0, qs@^6.7.0: - version "6.10.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== +qs@^6.4.0, qs@^6.7.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" @@ -7840,48 +4924,26 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -queue-microtask@^1.2.2: +queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.1, raw-body@^2.4.1: +raw-body@^2.4.1: version "2.5.1" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: bytes "3.1.2" @@ -7889,36 +4951,9 @@ raw-body@2.5.1, raw-body@^2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -readable-stream@^1.0.33: - version "1.1.14" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.2.2: version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -7929,86 +4964,49 @@ readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@~1.0.15: - version "1.0.34" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readdirp@~3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== dependencies: picomatch "^2.0.4" readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" rechoir@^0.6.2: version "0.6.2" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== dependencies: resolve "^1.1.6" recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== dependencies: - minimatch "3.0.4" + minimatch "^3.0.5" reduce-flatten@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== -regenerate@^1.2.1: - version "1.4.2" - resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator-transform@^0.10.0: - version "0.10.1" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz" - integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q== - dependencies: - babel-runtime "^6.18.0" - babel-types "^6.19.0" - private "^0.1.6" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1: +regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -8019,85 +5017,47 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1: regexpp@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== regexpp@^3.0.0, regexpp@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz" - integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA= - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - -regjsgen@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz" - integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= - -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz" - integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= - dependencies: - jsesc "~0.5.0" - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - req-cwd@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz" - integrity sha1-1AgrTURZgDZkD7c93qAe1T20nrw= + resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" + integrity sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ== dependencies: req-from "^2.0.0" req-from@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz" - integrity sha1-10GI5H+TeW9Kpx327jWuaJ8+DnA= + resolved "https://registry.yarnpkg.com/req-from/-/req-from-2.0.0.tgz#d74188e47f93796f4aa71df6ee35ae689f3e0e70" + integrity sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA== dependencies: resolve-from "^3.0.0" request-promise-core@1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: lodash "^4.17.19" request-promise-native@^1.0.5: version "1.0.9" - resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.79.0, request@^2.85.0, request@^2.88.0: +request@^2.88.0: version "2.88.2" - resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" @@ -8123,209 +5083,189 @@ request@^2.79.0, request@^2.85.0, request@^2.88.0: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-from-string@^1.1.0: - version "1.2.1" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz" - integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.0: +require-from-string@^2.0.0, require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-from@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - resolve@1.1.x: version "1.1.7" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== resolve@1.17.0: version "1.17.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.8.1, resolve@~1.22.0: - version "1.22.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== +resolve@^1.1.6, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== dependencies: - lowercase-keys "^1.0.0" + lowercase-keys "^3.0.0" restore-cursor@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== dependencies: onetime "^2.0.0" signal-exit "^3.0.2" restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" -resumer@~0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz" - integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= - dependencies: - through "~2.3.4" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - reusify@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== rimraf@2.6.3: version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: glob "^7.1.3" -rimraf@^2.2.8, rimraf@^2.6.3: +rimraf@^2.2.8: version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== dependencies: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: +rlp@^2.2.3, rlp@^2.2.4: version "2.2.7" - resolved "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== dependencies: bn.js "^5.2.0" run-async@^2.2.0: version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +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" + run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 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" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== rxjs@^6.4.0: version "6.6.7" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" -rxjs@^7.5.5: - version "7.5.5" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz" - integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== +rxjs@^7.5.7: + version "7.6.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2" + integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ== dependencies: tslib "^2.1.0" -safe-buffer@5.2.1, 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.1, safe-buffer@~5.2.0: +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" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-event-emitter@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz" - integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== - dependencies: - events "^3.0.0" - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== dependencies: - ret "~0.1.10" + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sc-istanbul@^0.4.5: version "0.4.6" - resolved "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz" + resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== dependencies: abbrev "1.0.x" @@ -8345,21 +5285,14 @@ sc-istanbul@^0.4.5: scrypt-js@2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== -scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: +scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -scryptsy@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz" - integrity sha1-oyJfpLJST4AnAHYeKFW987LZIWM= - dependencies: - pbkdf2 "^3.0.3" - scuffed-abi@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/scuffed-abi/-/scuffed-abi-1.0.4.tgz#bc88129877856de5026d8afaea49de9a1972b6cf" @@ -8367,135 +5300,60 @@ scuffed-abi@^1.0.4: secp256k1@^4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== dependencies: elliptic "^6.5.4" node-addon-api "^2.0.0" node-gyp-build "^4.2.0" -seedrandom@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.1.tgz" - integrity sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg== - -semaphore-async-await@^1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz" - integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= - -semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz" - integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: +semver@^5.5.0, semver@^5.5.1, semver@^5.7.0: version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.3.0: version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.0.0, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" -semver@~5.4.1: - version "5.4.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz" - integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== - -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - serialize-javascript@6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - -servify@^0.1.12: - version "0.1.12" - resolved "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz" - integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== - dependencies: - body-parser "^1.16.0" - cors "^2.8.1" - express "^4.14.0" - request "^2.79.0" - xhr "^2.3.3" - set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" - integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== setimmediate@1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz" - integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog== setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== dependencies: inherits "^2.0.1" @@ -8503,39 +5361,39 @@ sha.js@^2.4.0, sha.js@^2.4.8: sha1@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz" - integrity sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg= + resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" + integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA== dependencies: charenc ">= 0.0.1" crypt ">= 0.0.1" shebang-command@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shelljs@^0.8.3: version "0.8.5" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" @@ -8544,50 +5402,40 @@ shelljs@^0.8.3: side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-concat@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== -simple-get@^2.7.0: - version "2.8.2" - resolved "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz" - integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== +simple-get@>=2.8.2: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== dependencies: - decompress-response "^3.3.0" + decompress-response "^6.0.0" once "^1.3.1" simple-concat "^1.0.0" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== dependencies: ansi-styles "^3.2.0" @@ -8596,7 +5444,7 @@ slice-ansi@^2.1.0: slice-ansi@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== dependencies: ansi-styles "^4.0.0" @@ -8605,7 +5453,7 @@ slice-ansi@^3.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -8614,75 +5462,20 @@ slice-ansi@^4.0.0: slice-ansi@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== dependencies: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - solc@0.7.3: - version "0.7.3" - resolved "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz" - integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== - dependencies: - command-exists "^1.2.8" - commander "3.0.2" - follow-redirects "^1.12.1" - fs-extra "^0.30.0" - js-sha3 "0.8.0" - memorystream "^0.3.1" - require-from-string "^2.0.0" - semver "^5.5.0" - tmp "0.0.33" - -solc@^0.4.20: - version "0.4.26" - resolved "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz" - integrity sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA== - dependencies: - fs-extra "^0.30.0" - memorystream "^0.3.1" - require-from-string "^1.1.0" - semver "^5.3.0" - yargs "^4.7.1" - -solc@^0.6.3: - version "0.6.12" - resolved "https://registry.npmjs.org/solc/-/solc-0.6.12.tgz" - integrity sha512-Lm0Ql2G9Qc7yPP2Ba+WNmzw2jwsrd3u4PobHYlSOxaut3TtUbj9+5ZrT6f4DUpNPEoBaFUOEg9Op9C0mk7ge9g== + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== dependencies: command-exists "^1.2.8" commander "3.0.2" + follow-redirects "^1.12.1" fs-extra "^0.30.0" js-sha3 "0.8.0" memorystream "^0.3.1" @@ -8692,7 +5485,7 @@ solc@^0.6.3: solhint@^3.3.6: version "3.3.7" - resolved "https://registry.npmjs.org/solhint/-/solhint-3.3.7.tgz" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.7.tgz#b5da4fedf7a0fee954cb613b6c55a5a2b0063aa7" integrity sha512-NjjjVmXI3ehKkb3aNtRJWw55SUVJ8HMKKodwe0HnejA+k0d2kmhw7jvpa+MCTbcEgt8IWSwx0Hu6aCo/iYOZzQ== dependencies: "@solidity-parser/parser" "^0.14.1" @@ -8714,130 +5507,63 @@ solhint@^3.3.6: solidity-comments-extractor@^0.0.7: version "0.0.7" - resolved "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== -solidity-coverage@^0.7.0: - version "0.7.21" - resolved "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.7.21.tgz" - integrity sha512-O8nuzJ9yXiKUx3NdzVvHrUW0DxoNVcGzq/I7NzewNO9EZE3wYAQ4l8BwcnV64r4aC/HB6Vnw/q2sF0BQHv/3fg== +solidity-coverage@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.2.tgz#bc39604ab7ce0a3fa7767b126b44191830c07813" + integrity sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ== dependencies: - "@solidity-parser/parser" "^0.14.0" - "@truffle/provider" "^0.2.24" + "@ethersproject/abi" "^5.0.9" + "@solidity-parser/parser" "^0.14.1" chalk "^2.4.2" death "^1.1.0" detect-port "^1.3.0" + difflib "^0.2.4" fs-extra "^8.1.0" ghost-testrpc "^0.0.2" global-modules "^2.0.0" globby "^10.0.1" jsonschema "^1.2.4" lodash "^4.17.15" + mocha "7.1.2" node-emoji "^1.10.0" pify "^4.0.1" recursive-readdir "^2.2.2" sc-istanbul "^0.4.5" semver "^7.3.4" shelljs "^0.8.3" - web3-utils "^1.3.0" - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@0.5.12: - version "0.5.12" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" + web3-utils "^1.3.6" source-map-support@^0.5.13: version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.5.6, source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@~0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz" - integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA== dependencies: amdefine ">=0.0.4" -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== sshpk@^1.7.0: version "1.17.0" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== dependencies: asn1 "~0.2.3" @@ -8852,64 +5578,39 @@ sshpk@^1.7.0: stacktrace-parser@^0.1.10: version "0.1.10" - resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== dependencies: type-fest "^0.7.1" -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - statuses@2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== stealthy-require@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - -stream-to-pull-stream@^1.7.1: - version "1.7.3" - resolved "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz" - integrity sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg== - dependencies: - looper "^3.0.0" - pull-stream "^3.2.3" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g== -strict-uri-encode@^1.0.0: +streamsearch@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== string-argv@^0.3.1: version "0.3.1" - resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== string-format@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - "string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" @@ -8917,7 +5618,7 @@ string-width@^1.0.1: string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" @@ -8926,7 +5627,7 @@ string-width@^3.0.0, string-width@^3.1.0: string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -8935,214 +5636,143 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^5.0.0: version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.trim@~1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz#824960787db37a9e24711802ed0c1d1c0254f83e" - integrity sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + es-abstract "^1.20.4" string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - strip-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== dependencies: ansi-regex "^3.0.0" strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== dependencies: ansi-regex "^6.0.1" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== strip-hex-prefix@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz" - integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== dependencies: is-hex-prefixed "1.0.0" strip-json-comments@2.0.1, strip-json-comments@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== dependencies: has-flag "^3.0.0" supports-color@8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^3.1.0: version "3.2.3" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A== dependencies: has-flag "^1.0.0" supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^9.2.1: - version "9.2.2" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz" - integrity sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA== - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swarm-js@^0.1.40: - version "0.1.40" - resolved "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz" - integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== - dependencies: - bluebird "^3.5.0" - buffer "^5.0.5" - eth-lib "^0.1.26" - fs-extra "^4.0.2" - got "^7.1.0" - mime-types "^2.1.16" - mkdirp-promise "^5.0.1" - mock-fs "^4.1.0" - setimmediate "^1.0.5" - tar "^4.0.2" - xhr-request "^1.0.1" - sync-request@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== dependencies: http-response-object "^3.0.1" @@ -9151,14 +5781,14 @@ sync-request@^6.0.0: sync-rpc@^1.2.1: version "1.3.6" - resolved "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== dependencies: get-port "^3.1.0" table-layout@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== dependencies: array-back "^4.0.1" @@ -9168,7 +5798,7 @@ table-layout@^1.0.2: table@^5.2.3: version "5.4.6" - resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== dependencies: ajv "^6.10.2" @@ -9176,61 +5806,37 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tape@^4.6.3: - version "4.15.1" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.15.1.tgz#88fb662965a11f9be1bddb04c11662d7eceb129e" - integrity sha512-k7F5pyr91n9D/yjSJwbLLYDCrTWXxMSXbbmHX2n334lSIc2rxeXyFkaBv4UuUd2gBYMrAOalPutAiCxC6q1qbw== - dependencies: - call-bind "~1.0.2" - deep-equal "~1.1.1" - defined "~1.0.0" - dotignore "~0.1.2" - for-each "~0.3.3" - glob "~7.2.0" - has "~1.0.3" - inherits "~2.0.4" - is-regex "~1.1.4" - minimist "~1.2.6" - object-inspect "~1.12.0" - resolve "~1.22.0" - resumer "~0.0.0" - string.prototype.trim "~1.2.5" - through "~2.3.8" - -tar@^4.0.2: - version "4.4.19" - resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz" - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== - dependencies: - chownr "^1.1.4" - fs-minipass "^1.2.7" - minipass "^2.9.0" - minizlib "^1.3.3" - mkdirp "^0.5.5" - safe-buffer "^5.2.1" - yallist "^3.1.1" - -test-value@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz" - integrity sha1-Edpv9nDzRxpztiXKTz/c97t0gpE= +table@^6.8.0: + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== dependencies: - array-back "^1.0.3" - typical "^2.6.0" + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" -testrpc@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/testrpc/-/testrpc-0.0.1.tgz" - integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA== +tar@>=4.4.18: + version "6.1.12" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" + integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" text-table@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== then-request@^6.0.0: version "6.0.2" - resolved "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== dependencies: "@types/concat-stream" "^1.6.0" @@ -9245,149 +5851,64 @@ then-request@^6.0.0: promise "^8.0.0" qs "^6.4.0" -through2@^2.0.3: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@^2.3.6, through@^2.3.8, through@~2.3.4, through@~2.3.8: +through@^2.3.6, through@^2.3.8: version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -timed-out@^4.0.0, timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tmp@0.0.33, tmp@^0.0.33: version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" -tmp@0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz" - integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== - dependencies: - rimraf "^2.6.3" - -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" - integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" punycode "^2.1.1" -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -"true-case-path@^2.2.1": - version "2.2.1" - resolved "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz" - integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== ts-command-line-args@^2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.2.1.tgz" - integrity sha512-mnK68QA86FYzQYTSA/rxIjT/8EpKsvQw9QkawPic8I8t0gjAOw3Oa509NIRoaY1FmH7hdrncMp7t7o+vYoceNQ== + version "2.3.1" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz#b6188e42efc6cf7a8898e438a873fbb15505ddd6" + integrity sha512-FR3y7pLl/fuUNSmnPhfLArGqRrpojQgIEEOVzYx9DhTmfIN7C9RWSfpkJEF4J+Gk7aVx5pak8I7vWZsaN4N84g== dependencies: chalk "^4.1.0" command-line-args "^5.1.1" command-line-usage "^6.1.0" string-format "^2.0.0" -ts-essentials@^1.0.0: - version "1.0.4" - resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-1.0.4.tgz" - integrity sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ== - -ts-essentials@^6.0.3: - version "6.0.7" - resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-6.0.7.tgz" - integrity sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw== - ts-essentials@^7.0.1: version "7.0.3" - resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== -ts-generator@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/ts-generator/-/ts-generator-0.1.1.tgz" - integrity sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ== - dependencies: - "@types/mkdirp" "^0.5.2" - "@types/prettier" "^2.1.1" - "@types/resolve" "^0.0.8" - chalk "^2.4.1" - glob "^7.1.2" - mkdirp "^0.5.1" - prettier "^2.1.2" - resolve "^1.8.1" - ts-essentials "^1.0.0" - ts-node@^10.4.0: - version "10.7.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz" - integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== dependencies: - "@cspotcode/source-map-support" "0.7.0" + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -9398,12 +5919,12 @@ ts-node@^10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - v8-compile-cache-lib "^3.0.0" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" tsconfig-paths@^3.14.1: version "3.14.1" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" @@ -9413,117 +5934,86 @@ tsconfig-paths@^3.14.1: tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tsort@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz" - integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y= + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== dependencies: safe-buffer "^5.0.1" -tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: +tweetnacl-util@^0.15.1: version "0.15.1" - resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -tweetnacl@^1.0.0, tweetnacl@^1.0.3: +tweetnacl@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== dependencies: prelude-ls "~1.1.2" type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + 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" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.7.1: version "0.7.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.npmjs.org/type/-/type-1.2.0.tgz" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.5.0: - version "2.6.0" - resolved "https://registry.npmjs.org/type/-/type-2.6.0.tgz" - integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== - -typechain@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/typechain/-/typechain-3.0.0.tgz" - integrity sha512-ft4KVmiN3zH4JUFu2WJBrwfHeDf772Tt2d8bssDTo/YcckKW2D+OwFrHXRC6hJvO3mHjFQTihoMV6fJOi0Hngg== - dependencies: - command-line-args "^4.0.7" - debug "^4.1.1" - fs-extra "^7.0.0" - js-sha3 "^0.8.0" - lodash "^4.17.15" - ts-essentials "^6.0.3" - ts-generator "^0.1.1" - typechain@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/typechain/-/typechain-8.0.0.tgz" - integrity sha512-rqDfDYc9voVAhmfVfAwzg3VYFvhvs5ck1X9T/iWkX745Cul4t+V/smjnyqrbDzWDbzD93xfld1epg7Y/uFAesQ== + version "8.1.1" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.1.1.tgz#9c2e8012c2c4c586536fc18402dcd7034c4ff0bd" + integrity sha512-uF/sUvnXTOVF2FHKhQYnxHk4su4JjZR8vr4mA2mBaRwHTbwh0jIlqARz9XJr1tA0l7afJGvEa1dTSi4zt039LQ== dependencies: "@types/prettier" "^2.1.1" debug "^4.3.1" @@ -9536,68 +6026,34 @@ typechain@^8.0.0: ts-command-line-args "^2.2.0" ts-essentials "^7.0.1" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typedarray@^0.0.6: version "0.0.6" - resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@^4.5.4: - version "4.6.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz" - integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== - -typewise-core@^1.2, typewise-core@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz" - integrity sha1-l+uRgFx/VdL5QXSPpQ0xXZke8ZU= - -typewise@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz" - integrity sha1-EGeTZUCvl5N8xdz5kiSG6fooRlE= - dependencies: - typewise-core "^1.2.0" - -typewiselite@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/typewiselite/-/typewiselite-1.0.0.tgz" - integrity sha1-yIgvobsQksBgBal/NO9chQjjZk4= - -typical@^2.6.0, typical@^2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz" - integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0= + version "4.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" + integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== typical@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== typical@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== uglify-js@^3.1.4: - version "3.15.4" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz" - integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== - -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== -unbox-primitive@^1.0.1, unbox-primitive@^1.0.2: +unbox-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: call-bind "^1.0.2" @@ -9605,200 +6061,74 @@ unbox-primitive@^1.0.1, unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -underscore@1.9.1, underscore@>=1.12.1: - version "1.13.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.3.tgz#54bc95f7648c5557897e5e968d0f76bc062c34ee" - integrity sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA== - -undici@^4.14.1: - version "4.16.0" - resolved "https://registry.npmjs.org/undici/-/undici-4.16.0.tgz" - integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw== +underscore@>=1.12.1: + version "1.13.6" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== +undici@>=5.8.2, undici@^5.4.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.13.0.tgz#56772fba89d8b25e39bddc8c26a438bd73ea69bb" + integrity sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q== dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" + busboy "^1.6.0" universalify@^0.1.0: version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unorm@^1.3.3: - version "1.6.0" - resolved "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz" - integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unset-value@^1.0.0: +unpipe@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -url-set-query@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz" - integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= - -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -utf-8-validate@^5.0.2: - version "5.0.9" - resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz" - integrity sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q== - dependencies: - node-gyp-build "^4.3.0" - -utf8@3.0.0, utf8@^3.0.0: +utf8@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.1.tgz" - integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - for-each "^0.3.3" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.1" - -util@^0.12.0: - version "0.12.4" - resolved "https://registry.npmjs.org/util/-/util-0.12.4.tgz" - integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - safe-buffer "^5.1.2" - which-typed-array "^1.1.2" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz" - integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= - -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg== uuid@^3.3.2: version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^8.3.2: version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache-lib@^3.0.0: +v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -varint@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz" - integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== - -vary@^1, vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - verror@1.10.0: version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -9806,483 +6136,15 @@ verror@1.10.0: web-streams-polyfill@^3.0.3: version "3.2.1" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== -web3-bzz@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.11.tgz" - integrity sha512-XGpWUEElGypBjeFyUhTkiPXFbDVD6Nr/S5jznE3t8cWUA0FxRf1n3n/NuIZeb0H9RkN2Ctd/jNma/k8XGa3YKg== - dependencies: - "@types/node" "^12.12.6" - got "9.6.0" - swarm-js "^0.1.40" - underscore "1.9.1" - -web3-bzz@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.5.3.tgz" - integrity sha512-SlIkAqG0eS6cBS9Q2eBOTI1XFzqh83RqGJWnyrNZMDxUwsTVHL+zNnaPShVPvrWQA1Ub5b0bx1Kc5+qJVxsTJg== - dependencies: - "@types/node" "^12.12.6" - got "9.6.0" - swarm-js "^0.1.40" - -web3-core-helpers@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz" - integrity sha512-PEPoAoZd5ME7UfbnCZBdzIerpe74GEvlwT4AjOmHeCVZoIFk7EqvOZDejJHt+feJA6kMVTdd0xzRNN295UhC1A== - dependencies: - underscore "1.9.1" - web3-eth-iban "1.2.11" - web3-utils "1.2.11" - -web3-core-helpers@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.5.3.tgz" - integrity sha512-Ip1IjB3S8vN7Kf1PPjK41U5gskmMk6IJQlxIVuS8/1U7n/o0jC8krqtpRwiMfAgYyw3TXwBFtxSRTvJtnLyXZw== - dependencies: - web3-eth-iban "1.5.3" - web3-utils "1.5.3" - -web3-core-method@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.11.tgz" - integrity sha512-ff0q76Cde94HAxLDZ6DbdmKniYCQVtvuaYh+rtOUMB6kssa5FX0q3vPmixi7NPooFnbKmmZCM6NvXg4IreTPIw== - dependencies: - "@ethersproject/transactions" "^5.0.0-beta.135" - underscore "1.9.1" - web3-core-helpers "1.2.11" - web3-core-promievent "1.2.11" - web3-core-subscriptions "1.2.11" - web3-utils "1.2.11" - -web3-core-method@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.5.3.tgz" - integrity sha512-8wJrwQ2qD9ibWieF9oHXwrJsUGrv3XAtEkNeyvyNMpktNTIjxJ2jaFGQUuLiyUrMubD18XXgLk4JS6PJU4Loeg== - dependencies: - "@ethereumjs/common" "^2.4.0" - "@ethersproject/transactions" "^5.0.0-beta.135" - web3-core-helpers "1.5.3" - web3-core-promievent "1.5.3" - web3-core-subscriptions "1.5.3" - web3-utils "1.5.3" - -web3-core-promievent@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz" - integrity sha512-il4McoDa/Ox9Agh4kyfQ8Ak/9ABYpnF8poBLL33R/EnxLsJOGQG2nZhkJa3I067hocrPSjEdlPt/0bHXsln4qA== - dependencies: - eventemitter3 "4.0.4" - -web3-core-promievent@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.5.3.tgz" - integrity sha512-CFfgqvk3Vk6PIAxtLLuX+pOMozxkKCY+/GdGr7weMh033mDXEPvwyVjoSRO1PqIKj668/hMGQsVoIgbyxkJ9Mg== - dependencies: - eventemitter3 "4.0.4" - -web3-core-requestmanager@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz" - integrity sha512-oFhBtLfOiIbmfl6T6gYjjj9igOvtyxJ+fjS+byRxiwFJyJ5BQOz4/9/17gWR1Cq74paTlI7vDGxYfuvfE/mKvA== - dependencies: - underscore "1.9.1" - web3-core-helpers "1.2.11" - web3-providers-http "1.2.11" - web3-providers-ipc "1.2.11" - web3-providers-ws "1.2.11" - -web3-core-requestmanager@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.5.3.tgz" - integrity sha512-9k/Bze2rs8ONix5IZR+hYdMNQv+ark2Ek2kVcrFgWO+LdLgZui/rn8FikPunjE+ub7x7pJaKCgVRbYFXjo3ZWg== - dependencies: - util "^0.12.0" - web3-core-helpers "1.5.3" - web3-providers-http "1.5.3" - web3-providers-ipc "1.5.3" - web3-providers-ws "1.5.3" - -web3-core-subscriptions@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz" - integrity sha512-qEF/OVqkCvQ7MPs1JylIZCZkin0aKK9lDxpAtQ1F8niEDGFqn7DT8E/vzbIa0GsOjL2fZjDhWJsaW+BSoAW1gg== - dependencies: - eventemitter3 "4.0.4" - underscore "1.9.1" - web3-core-helpers "1.2.11" - -web3-core-subscriptions@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.5.3.tgz" - integrity sha512-L2m9vG1iRN6thvmv/HQwO2YLhOQlmZU8dpLG6GSo9FBN14Uch868Swk0dYVr3rFSYjZ/GETevSXU+O+vhCummA== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.5.3" - -web3-core@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-core/-/web3-core-1.2.11.tgz" - integrity sha512-CN7MEYOY5ryo5iVleIWRE3a3cZqVaLlIbIzDPsvQRUfzYnvzZQRZBm9Mq+ttDi2STOOzc1MKylspz/o3yq/LjQ== - dependencies: - "@types/bn.js" "^4.11.5" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-core-requestmanager "1.2.11" - web3-utils "1.2.11" - -web3-core@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-core/-/web3-core-1.5.3.tgz" - integrity sha512-ACTbu8COCu+0eUNmd9pG7Q9EVsNkAg2w3Y7SqhDr+zjTgbSHZV01jXKlapm9z+G3AN/BziV3zGwudClJ4u4xXQ== - dependencies: - "@types/bn.js" "^4.11.5" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.5.3" - web3-core-method "1.5.3" - web3-core-requestmanager "1.5.3" - web3-utils "1.5.3" - -web3-eth-abi@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz" - integrity sha512-PkRYc0+MjuLSgg03QVWqWlQivJqRwKItKtEpRUaxUAeLE7i/uU39gmzm2keHGcQXo3POXAbOnMqkDvOep89Crg== - dependencies: - "@ethersproject/abi" "5.0.0-beta.153" - underscore "1.9.1" - web3-utils "1.2.11" - -web3-eth-abi@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.5.3.tgz" - integrity sha512-i/qhuFsoNrnV130CSRYX/z4SlCfSQ4mHntti5yTmmQpt70xZKYZ57BsU0R29ueSQ9/P+aQrL2t2rqkQkAloUxg== - dependencies: - "@ethersproject/abi" "5.0.7" - web3-utils "1.5.3" - -web3-eth-accounts@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz" - integrity sha512-6FwPqEpCfKIh3nSSGeo3uBm2iFSnFJDfwL3oS9pyegRBXNsGRVpgiW63yhNzL0796StsvjHWwQnQHsZNxWAkGw== - dependencies: - crypto-browserify "3.12.0" - eth-lib "0.2.8" - ethereumjs-common "^1.3.2" - ethereumjs-tx "^2.1.1" - scrypt-js "^3.0.1" - underscore "1.9.1" - uuid "3.3.2" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-utils "1.2.11" - -web3-eth-accounts@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.5.3.tgz" - integrity sha512-pdGhXgeBaEJENMvRT6W9cmji3Zz/46ugFSvmnLLw79qi5EH7XJhKISNVb41eWCrs4am5GhI67GLx5d2s2a72iw== - dependencies: - "@ethereumjs/common" "^2.3.0" - "@ethereumjs/tx" "^3.2.1" - crypto-browserify "3.12.0" - eth-lib "0.2.8" - ethereumjs-util "^7.0.10" - scrypt-js "^3.0.1" - uuid "3.3.2" - web3-core "1.5.3" - web3-core-helpers "1.5.3" - web3-core-method "1.5.3" - web3-utils "1.5.3" - -web3-eth-contract@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz" - integrity sha512-MzYuI/Rq2o6gn7vCGcnQgco63isPNK5lMAan2E51AJLknjSLnOxwNY3gM8BcKoy4Z+v5Dv00a03Xuk78JowFow== - dependencies: - "@types/bn.js" "^4.11.5" - underscore "1.9.1" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-core-promievent "1.2.11" - web3-core-subscriptions "1.2.11" - web3-eth-abi "1.2.11" - web3-utils "1.2.11" - -web3-eth-contract@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.5.3.tgz" - integrity sha512-Gdlt1L6cdHe83k7SdV6xhqCytVtOZkjD0kY/15x441AuuJ4JLubCHuqu69k2Dr3tWifHYVys/vG8QE/W16syGg== - dependencies: - "@types/bn.js" "^4.11.5" - web3-core "1.5.3" - web3-core-helpers "1.5.3" - web3-core-method "1.5.3" - web3-core-promievent "1.5.3" - web3-core-subscriptions "1.5.3" - web3-eth-abi "1.5.3" - web3-utils "1.5.3" - -web3-eth-ens@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz" - integrity sha512-dbW7dXP6HqT1EAPvnniZVnmw6TmQEKF6/1KgAxbo8iBBYrVTMDGFQUUnZ+C4VETGrwwaqtX4L9d/FrQhZ6SUiA== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - underscore "1.9.1" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-promievent "1.2.11" - web3-eth-abi "1.2.11" - web3-eth-contract "1.2.11" - web3-utils "1.2.11" - -web3-eth-ens@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.5.3.tgz" - integrity sha512-QmGFFtTGElg0E+3xfCIFhiUF+1imFi9eg/cdsRMUZU4F1+MZCC/ee+IAelYLfNTGsEslCqfAusliKOT9DdGGnw== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - web3-core "1.5.3" - web3-core-helpers "1.5.3" - web3-core-promievent "1.5.3" - web3-eth-abi "1.5.3" - web3-eth-contract "1.5.3" - web3-utils "1.5.3" - -web3-eth-iban@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz" - integrity sha512-ozuVlZ5jwFC2hJY4+fH9pIcuH1xP0HEFhtWsR69u9uDIANHLPQQtWYmdj7xQ3p2YT4bQLq/axKhZi7EZVetmxQ== - dependencies: - bn.js "^4.11.9" - web3-utils "1.2.11" - -web3-eth-iban@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.5.3.tgz" - integrity sha512-vMzmGqolYZvRHwP9P4Nf6G8uYM5aTLlQu2a34vz78p0KlDC+eV1th3+90Qeaupa28EG7OO0IT1F0BejiIauOPw== - dependencies: - bn.js "^4.11.9" - web3-utils "1.5.3" - -web3-eth-personal@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz" - integrity sha512-42IzUtKq9iHZ8K9VN0vAI50iSU9tOA1V7XU2BhF/tb7We2iKBVdkley2fg26TxlOcKNEHm7o6HRtiiFsVK4Ifw== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-net "1.2.11" - web3-utils "1.2.11" - -web3-eth-personal@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.5.3.tgz" - integrity sha512-JzibJafR7ak/Icas8uvos3BmUNrZw1vShuNR5Cxjo+vteOC8XMqz1Vr7RH65B4bmlfb3bm9xLxetUHO894+Sew== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.5.3" - web3-core-helpers "1.5.3" - web3-core-method "1.5.3" - web3-net "1.5.3" - web3-utils "1.5.3" - -web3-eth@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.11.tgz" - integrity sha512-REvxW1wJ58AgHPcXPJOL49d1K/dPmuw4LjPLBPStOVkQjzDTVmJEIsiLwn2YeuNDd4pfakBwT8L3bz1G1/wVsQ== - dependencies: - underscore "1.9.1" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-core-subscriptions "1.2.11" - web3-eth-abi "1.2.11" - web3-eth-accounts "1.2.11" - web3-eth-contract "1.2.11" - web3-eth-ens "1.2.11" - web3-eth-iban "1.2.11" - web3-eth-personal "1.2.11" - web3-net "1.2.11" - web3-utils "1.2.11" - -web3-eth@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-eth/-/web3-eth-1.5.3.tgz" - integrity sha512-saFurA1L23Bd7MEf7cBli6/jRdMhD4X/NaMiO2mdMMCXlPujoudlIJf+VWpRWJpsbDFdu7XJ2WHkmBYT5R3p1Q== - dependencies: - web3-core "1.5.3" - web3-core-helpers "1.5.3" - web3-core-method "1.5.3" - web3-core-subscriptions "1.5.3" - web3-eth-abi "1.5.3" - web3-eth-accounts "1.5.3" - web3-eth-contract "1.5.3" - web3-eth-ens "1.5.3" - web3-eth-iban "1.5.3" - web3-eth-personal "1.5.3" - web3-net "1.5.3" - web3-utils "1.5.3" - -web3-net@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-net/-/web3-net-1.2.11.tgz" - integrity sha512-sjrSDj0pTfZouR5BSTItCuZ5K/oZPVdVciPQ6981PPPIwJJkCMeVjD7I4zO3qDPCnBjBSbWvVnLdwqUBPtHxyg== - dependencies: - web3-core "1.2.11" - web3-core-method "1.2.11" - web3-utils "1.2.11" - -web3-net@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-net/-/web3-net-1.5.3.tgz" - integrity sha512-0W/xHIPvgVXPSdLu0iZYnpcrgNnhzHMC888uMlGP5+qMCt8VuflUZHy7tYXae9Mzsg1kxaJAS5lHVNyeNw4CoQ== - dependencies: - web3-core "1.5.3" - web3-core-method "1.5.3" - web3-utils "1.5.3" - -web3-provider-engine@14.2.1: - version "14.2.1" - resolved "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-14.2.1.tgz" - integrity sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw== - dependencies: - async "^2.5.0" - backoff "^2.5.0" - clone "^2.0.0" - cross-fetch "^2.1.0" - eth-block-tracker "^3.0.0" - eth-json-rpc-infura "^3.1.0" - eth-sig-util "^1.4.2" - ethereumjs-block "^1.2.2" - ethereumjs-tx "^1.2.0" - ethereumjs-util "^5.1.5" - ethereumjs-vm "^2.3.4" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - readable-stream "^2.2.9" - request "^2.85.0" - semaphore "^1.0.3" - ws "^5.1.1" - xhr "^2.2.0" - xtend "^4.0.1" - -web3-providers-http@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.11.tgz" - integrity sha512-psh4hYGb1+ijWywfwpB2cvvOIMISlR44F/rJtYkRmQ5jMvG4FOCPlQJPiHQZo+2cc3HbktvvSJzIhkWQJdmvrA== - dependencies: - web3-core-helpers "1.2.11" - xhr2-cookies "1.1.0" - -web3-providers-http@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.5.3.tgz" - integrity sha512-5DpUyWGHtDAr2RYmBu34Fu+4gJuBAuNx2POeiJIooUtJ+Mu6pIx4XkONWH6V+Ez87tZAVAsFOkJRTYuzMr3rPw== - dependencies: - web3-core-helpers "1.5.3" - xhr2-cookies "1.1.0" - -web3-providers-ipc@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz" - integrity sha512-yhc7Y/k8hBV/KlELxynWjJDzmgDEDjIjBzXK+e0rHBsYEhdCNdIH5Psa456c+l0qTEU2YzycF8VAjYpWfPnBpQ== - dependencies: - oboe "2.1.4" - underscore "1.9.1" - web3-core-helpers "1.2.11" - -web3-providers-ipc@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.5.3.tgz" - integrity sha512-JmeAptugVpmXI39LGxUSAymx0NOFdgpuI1hGQfIhbEAcd4sv7fhfd5D+ZU4oLHbRI8IFr4qfGU0uhR8BXhDzlg== - dependencies: - oboe "2.1.5" - web3-core-helpers "1.5.3" - -web3-providers-ws@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz" - integrity sha512-ZxnjIY1Er8Ty+cE4migzr43zA/+72AF1myzsLaU5eVgdsfV7Jqx7Dix1hbevNZDKFlSoEyq/3j/jYalh3So1Zg== - dependencies: - eventemitter3 "4.0.4" - underscore "1.9.1" - web3-core-helpers "1.2.11" - websocket "^1.0.31" - -web3-providers-ws@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.5.3.tgz" - integrity sha512-6DhTw4Q7nm5CFYEUHOJM0gAb3xFx+9gWpVveg3YxJ/ybR1BUvEWo3bLgIJJtX56cYX0WyY6DS35a7f0LOI1kVg== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.5.3" - websocket "^1.0.32" - -web3-shh@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.11.tgz" - integrity sha512-B3OrO3oG1L+bv3E1sTwCx66injW1A8hhwpknDUbV+sw3fehFazA06z9SGXUefuFI1kVs4q2vRi0n4oCcI4dZDg== - dependencies: - web3-core "1.2.11" - web3-core-method "1.2.11" - web3-core-subscriptions "1.2.11" - web3-net "1.2.11" - -web3-shh@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-shh/-/web3-shh-1.5.3.tgz" - integrity sha512-COfEXfsqoV/BkcsNLRxQqnWc1Teb8/9GxdGag5GtPC5gQC/vsN+7hYVJUwNxY9LtJPKYTij2DHHnx6UkITng+Q== - dependencies: - web3-core "1.5.3" - web3-core-method "1.5.3" - web3-core-subscriptions "1.5.3" - web3-net "1.5.3" - -web3-utils@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.11.tgz" - integrity sha512-3Tq09izhD+ThqHEaWYX4VOT7dNPdZiO+c/1QMA0s5X2lDFKK/xHJb7cyTRRVzN2LvlHbR7baS1tmQhSua51TcQ== - dependencies: - bn.js "^4.11.9" - eth-lib "0.2.8" - ethereum-bloom-filters "^1.0.6" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - underscore "1.9.1" - utf8 "3.0.0" - -web3-utils@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.5.3.tgz" - integrity sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q== - dependencies: - bn.js "^4.11.9" - eth-lib "0.2.8" - ethereum-bloom-filters "^1.0.6" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - utf8 "3.0.0" - -web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: - version "1.7.3" - resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.7.3.tgz" - integrity sha512-g6nQgvb/bUpVUIxJE+ezVN+rYwYmlFyMvMIRSuqpi1dk6ApDD00YNArrk7sPcZnjvxOJ76813Xs2vIN2rgh4lg== +web3-utils@^1.3.4, web3-utils@^1.3.6: + version "1.8.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.8.1.tgz#f2f7ca7eb65e6feb9f3d61056d0de6bbd57125ff" + integrity sha512-LgnM9p6V7rHHUGfpMZod+NST8cRfGzJ1BTXAyNo7A9cJX9LczBfSRxJp+U/GInYe9mby40t3v22AJdlELibnsQ== dependencies: - bn.js "^4.11.9" + bn.js "^5.2.1" ethereum-bloom-filters "^1.0.6" ethereumjs-util "^7.1.0" ethjs-unit "0.1.6" @@ -10290,59 +6152,9 @@ web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: randombytes "^2.1.0" utf8 "3.0.0" -web3@1.2.11: - version "1.2.11" - resolved "https://registry.npmjs.org/web3/-/web3-1.2.11.tgz" - integrity sha512-mjQ8HeU41G6hgOYm1pmeH0mRAeNKJGnJEUzDMoerkpw7QUQT4exVREgF1MYPvL/z6vAshOXei25LE/t/Bxl8yQ== - dependencies: - web3-bzz "1.2.11" - web3-core "1.2.11" - web3-eth "1.2.11" - web3-eth-personal "1.2.11" - web3-net "1.2.11" - web3-shh "1.2.11" - web3-utils "1.2.11" - -web3@1.5.3: - version "1.5.3" - resolved "https://registry.npmjs.org/web3/-/web3-1.5.3.tgz" - integrity sha512-eyBg/1K44flfv0hPjXfKvNwcUfIVDI4NX48qHQe6wd7C8nPSdbWqo9vLy6ksZIt9NLa90HjI8HsGYgnMSUxn6w== - dependencies: - web3-bzz "1.5.3" - web3-core "1.5.3" - web3-eth "1.5.3" - web3-eth-personal "1.5.3" - web3-net "1.5.3" - web3-shh "1.5.3" - web3-utils "1.5.3" - -websocket@1.0.32, websocket@^1.0.31: - version "1.0.32" - resolved "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz" - integrity sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - -websocket@^1.0.32: - version "1.0.34" - resolved "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz" - integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -10351,88 +6163,58 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - which-module@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which-typed-array@^1.1.2: - version "1.1.7" - resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz" - integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.7" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -which@2.0.2, which@^2.0.1: +which@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wide-align@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" -window-size@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz" - integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= - word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== wordwrapjs@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== dependencies: reduce-flatten "^2.0.0" typical "^5.2.0" -workerpool@6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" - integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== dependencies: ansi-styles "^3.2.0" @@ -10441,7 +6223,7 @@ wrap-ansi@^5.1.0: wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -10450,7 +6232,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -10459,139 +6241,59 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write@1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/write/-/write-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== dependencies: mkdirp "^0.5.1" -ws@7.4.6: - version "7.4.6" - resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - -ws@^3.0.0: - version "3.3.3" - resolved "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - -ws@^5.1.1: - version "5.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" - integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== - dependencies: - async-limiter "~1.0.0" - -ws@^7.4.6: - version "7.5.7" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== - -xhr-request-promise@^0.1.2: - version "0.1.3" - resolved "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz" - integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== - dependencies: - xhr-request "^1.1.0" - -xhr-request@^1.0.1, xhr-request@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz" - integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== - dependencies: - buffer-to-arraybuffer "^0.0.5" - object-assign "^4.1.1" - query-string "^5.0.1" - simple-get "^2.7.0" - timed-out "^4.0.1" - url-set-query "^1.0.0" - xhr "^2.0.4" - -xhr2-cookies@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz" - integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= - dependencies: - cookiejar "^2.1.1" - -xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: - version "2.6.0" - resolved "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz" - integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== - dependencies: - global "~4.4.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" +ws@7.4.6, ws@>=5.2.3, ws@^7.4.6: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== xmlhttprequest@1.8.0: version "1.8.0" - resolved "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz" - integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= - -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -xtend@~2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz" - integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= - dependencies: - object-keys "~0.4.0" - -y18n@^3.2.1: - version "3.2.2" - resolved "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz" - integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= - -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: +yallist@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207" + integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg== -yargs-parser@13.1.2, yargs-parser@20.2.4, yargs-parser@>=5.0.1, yargs-parser@^13.1.2, yargs-parser@^2.4.1, yargs-parser@^20.2.2: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@13.1.2, yargs-parser@20.2.4, yargs-parser@>=5.0.1, yargs-parser@^13.1.2, yargs-parser@^20.2.2: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== dependencies: flat "^4.1.0" @@ -10600,7 +6302,7 @@ yargs-unparser@1.6.0: yargs-unparser@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" @@ -10610,7 +6312,7 @@ yargs-unparser@2.0.0: yargs@13.3.2, yargs@^13.3.0: version "13.3.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: cliui "^5.0.0" @@ -10626,7 +6328,7 @@ yargs@13.3.2, yargs@^13.3.0: yargs@16.2.0: version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -10637,32 +6339,12 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^4.7.1: - version "4.8.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz" - integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= - dependencies: - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - lodash.assign "^4.0.3" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.1" - which-module "^1.0.0" - window-size "^0.2.0" - y18n "^3.2.1" - yargs-parser "^2.4.1" - yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==