├── .eslintignore ├── .eslintrc.js ├── .gas-snapshot ├── .gas_reports ├── 10d78fe57eb635a6d8a85e34236aa29548a1b52f.json └── 3fab586d8f77266bf79226b17cbc65cb867689e6.json ├── .github ├── pull_request_template.md └── workflows │ ├── docs.yml │ ├── stale.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── codecov.yml ├── config ├── .solcover-reference.js ├── .solcover.js ├── .solhint.json └── .solhintignore ├── constants └── constants.js ├── contracts ├── Seaport.sol ├── conduit │ ├── Conduit.sol │ └── ConduitController.sol ├── helpers │ ├── ArrayHelpers.sol │ ├── SeaportRouter.sol │ ├── TransferHelper.sol │ ├── navigator │ │ ├── SeaportNavigator.sol │ │ └── lib │ │ │ ├── CriteriaHelper.sol │ │ │ ├── CriteriaHelperLib.sol │ │ │ ├── ExecutionsHelper.sol │ │ │ ├── FulfillmentsHelper.sol │ │ │ ├── HelperInterface.sol │ │ │ ├── HelperItemLib.sol │ │ │ ├── MerkleLib.sol │ │ │ ├── NavigatorAdvancedOrderLib.sol │ │ │ ├── NavigatorContextLib.sol │ │ │ ├── NavigatorCriteriaResolverLib.sol │ │ │ ├── NavigatorDetailsLib.sol │ │ │ ├── NavigatorExecutionsLib.sol │ │ │ ├── NavigatorFulfillmentsLib.sol │ │ │ ├── NavigatorRequestValidatorLib.sol │ │ │ ├── NavigatorSeaportValidatorLib.sol │ │ │ ├── NavigatorSuggestedActionLib.sol │ │ │ ├── OrderAvailabilityLib.sol │ │ │ ├── OrderDetailsHelper.sol │ │ │ ├── OrderStructureLib.sol │ │ │ ├── RequestValidator.sol │ │ │ ├── SeaportNavigatorInterface.sol │ │ │ ├── SeaportNavigatorTypes.sol │ │ │ ├── SuggestedActionHelper.sol │ │ │ └── ValidatorHelper.sol │ └── order-validator │ │ ├── SeaportValidator.sol │ │ └── lib │ │ ├── ConsiderationTypeHashes.sol │ │ ├── ErrorsAndWarnings.sol │ │ ├── Murky.sol │ │ ├── ReadOnlyOrderValidator.sol │ │ ├── SafeStaticCall.sol │ │ ├── SeaportValidatorHelper.sol │ │ ├── SeaportValidatorInterface.sol │ │ └── SeaportValidatorTypes.sol ├── interfaces │ ├── ImmutableCreate2FactoryInterface.sol │ ├── ZoneInteractionErrors.sol │ └── ZoneInterface.sol ├── test │ ├── ConduitControllerMock.sol │ ├── ConduitMock.sol │ ├── ConduitMockInvalidMagic.sol │ ├── ConduitMockRevertBytes.sol │ ├── ConduitMockRevertNoReason.sol │ ├── EIP1271Wallet.sol │ ├── ERC1155BatchRecipient.sol │ ├── ERC2981.sol │ ├── ERC721ReceiverMock.sol │ ├── ExcessReturnDataRecipient.sol │ ├── HashCalldataContractOfferer.sol │ ├── HashValidationZoneOfferer.sol │ ├── InvalidERC721Recipient.sol │ ├── InvalidEthRecipient.sol │ ├── OffererZoneFailureReason.sol │ ├── Reenterer.sol │ ├── TestBadContractOfferer.sol │ ├── TestCalldataHashContractOfferer.sol │ ├── TestContractOfferer.sol │ ├── TestContractOffererNativeToken.sol │ ├── TestERC1155.sol │ ├── TestERC1155Revert.sol │ ├── TestERC1271.sol │ ├── TestERC20.sol │ ├── TestERC20NotOk.sol │ ├── TestERC20Panic.sol │ ├── TestERC20Revert.sol │ ├── TestERC721.sol │ ├── TestERC721Fee.sol │ ├── TestERC721Funky.sol │ ├── TestERC721Revert.sol │ ├── TestInvalidContractOfferer.sol │ ├── TestInvalidContractOfferer165.sol │ ├── TestInvalidContractOffererRatifyOrder.sol │ ├── TestInvalidZone.sol │ ├── TestPostExecution.sol │ ├── TestTransferValidationZoneOfferer.sol │ ├── TestZone.sol │ └── TypehashDirectory.sol └── zones │ ├── PausableZone.sol │ ├── PausableZoneController.sol │ └── interfaces │ ├── PausableZoneControllerInterface.sol │ ├── PausableZoneEventsAndErrors.sol │ └── PausableZoneInterface.sol ├── diagrams ├── README.md ├── Seaport.drawio └── Seaport.drawio.svg ├── docs ├── AuditLink.md ├── Code4rena-Guidelines.md ├── Deployment.md ├── FunctionSignatures.md ├── OrderValidator.md ├── Overview.md ├── SeaportDiagramLink.md ├── SeaportDocumentation.md ├── ZoneDocumentation.md └── prepare-docs.js ├── eip-712-types ├── bulkOrder.js ├── domain.js └── order.js ├── foundry.toml ├── hardhat-coverage.config.ts ├── hardhat-reference-coverage.config.ts ├── hardhat-reference.config.ts ├── hardhat-validator.config.ts ├── hardhat.config.ts ├── img └── Seaport-banner.png ├── package.json ├── reference ├── ReferenceConsideration.sol ├── conduit │ ├── ReferenceConduit.sol │ └── ReferenceConduitController.sol ├── lib │ ├── ReferenceAmountDeriver.sol │ ├── ReferenceAssertions.sol │ ├── ReferenceBasicOrderFulfiller.sol │ ├── ReferenceConsiderationBase.sol │ ├── ReferenceConsiderationStructs.sol │ ├── ReferenceCounterManager.sol │ ├── ReferenceCriteriaResolution.sol │ ├── ReferenceExecutor.sol │ ├── ReferenceFulfillmentApplier.sol │ ├── ReferenceGenerateOrderReturndataDecoder.sol │ ├── ReferenceGettersAndDerivers.sol │ ├── ReferenceOrderCombiner.sol │ ├── ReferenceOrderFulfiller.sol │ ├── ReferenceOrderValidator.sol │ ├── ReferenceReentrancyGuard.sol │ ├── ReferenceSignatureVerification.sol │ ├── ReferenceTokenTransferrer.sol │ ├── ReferenceVerifiers.sol │ └── ReferenceZoneInteraction.sol └── shim │ └── Shim.sol ├── script ├── CallNavigator.s.sol ├── NavigatorDeployer.s.sol ├── SeaportDeployer.s.sol └── TransferHelperDeployer.s.sol ├── scripts ├── comment-table.ts ├── compare_reports.ts ├── find_optimizer_runs.sh ├── plot_metrics.ts ├── print_report.ts ├── utils.ts └── write_reports.ts ├── templates ├── GenericEnumerableSet.template ├── GenericStructSortLib.template └── replace.sh ├── test ├── advanced.spec.ts ├── basic.spec.ts ├── conduit.spec.ts ├── counter.spec.ts ├── findings │ ├── AdditionalRecipientsOffByOne.spec.ts │ ├── CriteriaResolverUnhashedLeaves.spec.ts │ ├── FulfillmentOverflowWithMissingItems.spec.ts │ └── PartialFillFractionOverflow.spec.ts ├── foundry │ ├── BulkSignature.t.sol │ ├── CeilEquivalenceTest.t.sol │ ├── ConsiderationErrors.t.sol │ ├── ConstantsTest.t.sol │ ├── FulfillAdvancedOrder.t.sol │ ├── FulfillAdvancedOrderCriteria.t.sol │ ├── FulfillAvailableAdvancedOrder.t.sol │ ├── FulfillAvailableAdvancedOrderCriteria.t.sol │ ├── FulfillBasicOrderTest.t.sol │ ├── FulfillOrderTest.t.sol │ ├── FullfillAvailableOrder.t.sol │ ├── GetterTests.t.sol │ ├── MatchAdvancedOrder.t.sol │ ├── MatchAdvancedOrderUnspentOffer.t.sol │ ├── MatchOrders.t.sol │ ├── NonMatchSelectorTest.t.sol │ ├── NonReentrant.t.sol │ ├── SignatureVerification.t.sol │ ├── TestNewHelpers.t.sol │ ├── TokenTransferrer.t.sol │ ├── TransferHelperMultipleRecipientsTest.sol │ ├── TransferHelperSingleRecipientTest.sol │ ├── conduit │ │ ├── BaseConduitTest.sol │ │ ├── ConduitExecute.t.sol │ │ ├── ConduitExecuteBatch1155.t.sol │ │ └── ConduitExecuteWithBatch1155.t.sol │ ├── interfaces │ │ ├── OwnableDelegateProxy.sol │ │ └── ProxyRegistry.sol │ ├── new │ │ ├── BaseOrderTest.sol │ │ ├── CriteriaResolverHelper.t.sol │ │ ├── ExpectedBalanceSerializer.sol │ │ ├── ExpectedBalances.t.sol │ │ ├── FractionUtil.t.sol │ │ ├── FuzzCoverage.t.sol │ │ ├── FuzzEngine.t.sol │ │ ├── FuzzGenerators.t.sol │ │ ├── FuzzHelpers.t.sol │ │ ├── FuzzInscribers.t.sol │ │ ├── FuzzMain.t.sol │ │ ├── FuzzSetup.t.sol │ │ ├── SeaportNavigator.t.sol │ │ ├── SeaportNavigatorTest.sol │ │ ├── SeaportValidator.t.sol │ │ ├── SeaportValidatorTest.sol │ │ ├── SelfRestricted.t.sol │ │ ├── SelfRestrictedContractOfferer.t.sol │ │ ├── helpers │ │ │ ├── ArithmeticUtil.sol │ │ │ ├── BaseSeaportTest.sol │ │ │ ├── CriteriaResolverHelper.sol │ │ │ ├── DebugUtil.sol │ │ │ ├── DifferentialTest.sol │ │ │ ├── EIP1271Offerer.sol │ │ │ ├── EIP712MerkleTree.sol │ │ │ ├── ERC1155Recipient.sol │ │ │ ├── ERC721Recipient.sol │ │ │ ├── ExpectedBalances.sol │ │ │ ├── FractionUtil.sol │ │ │ ├── FuzzAmendments.sol │ │ │ ├── FuzzChecks.sol │ │ │ ├── FuzzDerivers.sol │ │ │ ├── FuzzEngine.sol │ │ │ ├── FuzzEngineLib.sol │ │ │ ├── FuzzExecutor.sol │ │ │ ├── FuzzGeneratorContextLib.sol │ │ │ ├── FuzzGenerators.sol │ │ │ ├── FuzzHelpers.sol │ │ │ ├── FuzzInscribers.sol │ │ │ ├── FuzzMutationHelpers.sol │ │ │ ├── FuzzMutationSelectorLib.sol │ │ │ ├── FuzzMutations.sol │ │ │ ├── FuzzSetup.sol │ │ │ ├── FuzzTestContextLib.sol │ │ │ ├── Labeler.sol │ │ │ ├── Metrics.sol │ │ │ ├── PreapprovedERC721.sol │ │ │ ├── Searializer.sol │ │ │ ├── VmUtils.sol │ │ │ ├── event-utils │ │ │ │ ├── EventHashes.sol │ │ │ │ ├── EventSerializer.sol │ │ │ │ ├── ExecutionsFlattener.sol │ │ │ │ ├── ExpectedEventsUtil.sol │ │ │ │ ├── ForgeEventsLib.sol │ │ │ │ ├── OrderFulfilledEventsLib.sol │ │ │ │ ├── OrdersMatchedEventsLib.sol │ │ │ │ └── TransferEventsLib.sol │ │ │ └── sol │ │ │ │ ├── FulfillAvailableHelper.t.sol │ │ │ │ ├── MatchFulfillmentHelper.t.sol │ │ │ │ ├── MatchFulfillmentPriv.t.sol │ │ │ │ └── lib │ │ │ │ ├── fulfillment │ │ │ │ └── AmountDeriverHelper.t.sol │ │ │ │ └── types │ │ │ │ └── MatchComponentType.t.sol │ │ └── zones │ │ │ └── ValidationOffererZone.sol │ ├── offerers │ │ ├── AdjustedAmountOfferer.t.sol │ │ ├── BadOfferer.t.sol │ │ ├── ContractOffersNativeTokenOfferItems.t.sol │ │ ├── OffererCriteriaAdvanced.t.sol │ │ ├── StatefulOfferer.t.sol │ │ ├── TestPoolOffererImpl.t.sol │ │ ├── TestPoolOffererTest.t.sol │ │ └── impl │ │ │ ├── AdjustedAmountOfferer.sol │ │ │ ├── BadOfferer.sol │ │ │ ├── PassthroughOfferer.sol │ │ │ ├── StatefulRatifierOfferer.sol │ │ │ ├── TestPoolFactory.sol │ │ │ └── TestPoolOfferer.sol │ ├── token │ │ ├── CustomERC721.sol │ │ ├── StubERC1155.sol │ │ ├── StubERC20.sol │ │ └── StubERC721.sol │ ├── utils │ │ ├── ArithmeticUtil.sol │ │ ├── BaseConsiderationTest.sol │ │ ├── BaseOrderTest.sol │ │ ├── ConsiderationErrorsWrapper.sol │ │ ├── DifferentialTest.sol │ │ ├── EIP712MerkleTree.sol │ │ ├── ERC1155Recipient.sol │ │ ├── ERC721Recipient.sol │ │ ├── ExternalCounter.sol │ │ ├── OfferConsiderationItemAdder.sol │ │ ├── OrderBuilder.sol │ │ ├── PseudoRandom.sol │ │ ├── StructCopier.sol │ │ ├── TestTokenMinter.sol │ │ └── reentrancy │ │ │ ├── ReentrantEnums.sol │ │ │ └── ReentrantStructs.sol │ └── zone │ │ ├── PreAndPostFulfillmentCheck.t.sol │ │ ├── TestTransferValidationZoneFuzz.t.sol │ │ ├── TestZoneCalldataFidelity.t.sol │ │ ├── UnauthorizedOrderSkip.t.sol │ │ └── impl │ │ ├── BadZone.sol │ │ ├── HashCalldataTestZone.sol │ │ ├── StatefulTestZone.sol │ │ ├── TestZone.sol │ │ └── VerboseAuthZone.sol ├── getter.spec.ts ├── revert.spec.ts ├── router.spec.ts ├── transferhelper.spec.ts ├── typehashdirectory.spec.ts ├── utils │ ├── contracts.ts │ ├── criteria.ts │ ├── eip712 │ │ ├── Eip712MerkleTree.ts │ │ ├── bulk-orders.ts │ │ ├── defaults.ts │ │ └── utils.ts │ ├── encoding.ts │ ├── events.ts │ ├── faucet.ts │ ├── fixtures │ │ ├── conduit.ts │ │ ├── create2.ts │ │ ├── index.ts │ │ ├── marketplace.ts │ │ └── tokens.ts │ ├── helpers.ts │ ├── reports │ │ ├── comment-table.ts │ │ └── report_parser.ts │ ├── seeded-rng.js │ └── types.ts └── zone.spec.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .eslintrc* 3 | artifacts 4 | cache 5 | constants 6 | coverage 7 | lib/murky 8 | lib/openzeppelin-contracts 9 | order-validator/test -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es2021: true, 5 | mocha: true, 6 | node: true, 7 | }, 8 | plugins: ["@typescript-eslint", "import"], 9 | extends: [ 10 | "standard", 11 | "plugin:prettier/recommended", 12 | "eslint:recommended", 13 | "plugin:import/recommended", 14 | "plugin:import/typescript", 15 | ], 16 | parser: "@typescript-eslint/parser", 17 | parserOptions: { 18 | ecmaVersion: 12, 19 | project: "./tsconfig.json", 20 | }, 21 | rules: { 22 | "@typescript-eslint/consistent-type-imports": "error", 23 | "@typescript-eslint/prefer-nullish-coalescing": "error", 24 | camelcase: [ 25 | "error", 26 | { allow: ["Conduit__factory", "EIP1271Wallet__factory"] }, 27 | ], 28 | "import/order": [ 29 | "error", 30 | { 31 | alphabetize: { 32 | order: "asc", 33 | }, 34 | groups: [ 35 | "object", 36 | ["builtin", "external"], 37 | "parent", 38 | "sibling", 39 | "index", 40 | "type", 41 | ], 42 | "newlines-between": "always", 43 | }, 44 | ], 45 | "object-shorthand": "error", 46 | "prefer-const": "error", 47 | "sort-imports": ["error", { ignoreDeclarationSort: true }], 48 | }, 49 | overrides: [ 50 | { 51 | files: ["test/**/*.spec.ts"], 52 | rules: { 53 | "no-unused-expressions": "off", 54 | }, 55 | }, 56 | ], 57 | }; 58 | -------------------------------------------------------------------------------- /.gas-snapshot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectOpenSea/seaport/687dfd72b80a0fbf30fc61008388bb7f508b6d70/.gas-snapshot -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ## Motivation 11 | 12 | 19 | 20 | ## Solution 21 | 22 | 26 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docs to Central Repository 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Copy developer docs to repository 11 | if: github.ref == 'refs/heads/main' 12 | uses: nkoppel/push-files-to-another-repository@v1.1.1 13 | env: 14 | API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} 15 | with: 16 | source-files: "docs/" 17 | destination-username: "ProjectOpenSea" 18 | destination-repository: "developer-docs" 19 | destination-directory: "seaport" 20 | destination-branch: "main" 21 | commit-username: "ProjectOpenSea-seaport" 22 | commit-message: "Latest docs from seaport" -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v8 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | stale-issue-message: "This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions. If you believe this was a mistake, please comment." 14 | stale-pr-message: "This PR has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions. If you believe this was a mistake, please comment." 15 | days-before-stale: 60 16 | days-before-close: 14 17 | operations-per-run: 100 18 | exempt-pr-labels: "work-in-progress,Informational" 19 | exempt-issue-labels: "work-in-progress,Informational" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain-types 6 | yarn-error.log 7 | 8 | #Hardhat files 9 | artifacts/ 10 | artifacts-ref/ 11 | hh-cache/ 12 | hh-cache-ref/ 13 | 14 | 15 | #foundry test compilation files 16 | cache 17 | out 18 | reference-out 19 | optimized-out 20 | reference-working 21 | offerers-out 22 | 23 | #foundry script run output files 24 | broadcast/ 25 | 26 | .DS_Store 27 | 28 | # VScode 29 | .vscode 30 | __pycache__ 31 | 32 | Seaport.yul 33 | 34 | # coverage 35 | html 36 | lcov.info 37 | 38 | test/utils/eip712/gen.sol 39 | .gas_reports/*.md 40 | 41 | # fuzz metrics 42 | metrics.txt 43 | *-metrics.txt 44 | 45 | fuzz_debug.json 46 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/murky"] 2 | path = lib/murky 3 | url = https://github.com/dmfxyz/murky 4 | [submodule "lib/openzeppelin-contracts"] 5 | path = lib/openzeppelin-contracts 6 | url = https://github.com/openzeppelin/openzeppelin-contracts 7 | [submodule "lib/solmate"] 8 | path = lib/solmate 9 | url = https://github.com/transmissions11/solmate 10 | [submodule "lib/forge-std"] 11 | path = lib/forge-std 12 | url = https://github.com/foundry-rs/forge-std 13 | branch = v1.5.0 14 | [submodule "lib/solady"] 15 | path = lib/solady 16 | url = https://github.com/vectorized/solady 17 | branch = v0.0.84 18 | [submodule "lib/solarray"] 19 | path = lib/solarray 20 | url = https://github.com/emo-eth/solarray 21 | [submodule "lib/ds-test"] 22 | path = lib/ds-test 23 | url = https://github.com/dapphub/ds-test 24 | [submodule "lib/seaport-types"] 25 | path = lib/seaport-types 26 | url = https://github.com/projectopensea/seaport-types 27 | [submodule "lib/seaport-core"] 28 | path = lib/seaport-core 29 | url = https://github.com/projectopensea/seaport-core 30 | [submodule "lib/seaport-sol"] 31 | path = lib/seaport-sol 32 | url = https://github.com/projectopensea/seaport-sol 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | hardhat.config.ts 2 | scripts 3 | test 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # .npmrc 2 | engine-strict=true -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.15.1 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | 7 | typechain-types/ 8 | 9 | lib/ds-test/ 10 | lib/murky/ 11 | lib/seaport-core/ 12 | lib/seaport-types/ 13 | lib/solarray/ 14 | lib/forge-std/ 15 | lib/openzeppelin-contracts/ 16 | lib/seaport-sol/ 17 | lib/solady/ 18 | lib/solmate/ 19 | 20 | docs/OrderValidator.md -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | overrides: [ 3 | { 4 | files: "*.sol", 5 | options: { 6 | tabWidth: 4, 7 | printWidth: 80, 8 | bracketSpacing: true, 9 | compiler: "0.8.17", 10 | }, 11 | }, 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Seaport Contributors 2 | 3 | Contributor | ENS 4 | ------------------------------ | ------------------------------ 5 | 0age | `0age.eth` 6 | d1ll0n | `d1ll0n.eth` 7 | transmissions11 | `t11s.eth` 8 | James Wenzel | `emo.eth` 9 | Kartik | `slokh.eth` 10 | LeFevre | `lefevre.eth` 11 | Joseph Schiarizzi | `CupOJoseph.eth` 12 | Aspyn Palatnick | `stuckinaboot.eth` 13 | Stephan Min | `stephanm.eth` 14 | Ryan Ghods | `ralxz.eth` 15 | Daniel Viau | `snotrocket.eth` 16 | 0xPatissier | 17 | pcaversaccio | 18 | David Eiber | 19 | hack3r-0m | `hack3r-0m.eth` 20 | csanuragjain | 21 | Diego Estevez | `antidiego.eth` 22 | Chomtana | `chomtana.eth` 23 | Saw-mon and Natalie | `sawmonandnatalie.eth` 24 | 0xBeans | `0xBeans.eth` 25 | 0x4non | `punkdev.eth` 26 | Laurence E. Day | `norsefire.eth` 27 | vectorized.eth | `vectorized.eth` 28 | karmacoma | `karmacoma.eth` 29 | horsefacts | `horsefacts.eth` 30 | UncarvedBlock | `uncarvedblock.eth` 31 | Zoraiz Mahmood | `zorz.eth` 32 | William Poulin | `wpoulin.eth` 33 | Rajiv Patel-O'Connor | `rajivpoc.eth` 34 | tserg | `tserg.eth` 35 | cygaar | `cygaar.eth` 36 | Meta0xNull | `meta0xnull.eth` 37 | sach1r0 | `sach1r0.eth` 38 | gpersoon | `gpersoon.eth` 39 | Matt Solomon | `msolomon.eth` 40 | twojoy0 | 41 | Weikang Song | `weikangs.eth` 42 | zer0dot | `zer0dot.eth` 43 | Mudit Gupta | `mudit.eth` 44 | ori_dabush | 45 | leonardoalt | `leoalt.eth` 46 | cmichel | `cmichel.eth` 47 | Daniel Gelfand | 48 | PraneshASP | `pranesh.eth` 49 | JasperAlexander | `jasperalexander.eth` 50 | okkothejawa | 51 | FlameHorizon | 52 | vdrg | 53 | Ellahi | `ellahi.eth` 54 | zaz | `1zaz1.eth` 55 | berndartmueller | `berndartmueller.eth` 56 | dmfxyz | `dmfxyz.eth` 57 | daltoncoder | `dontkillrobots.eth` 58 | 0xf4ce | `0xf4ce.eth` 59 | phaze | `phaze.eth` 60 | hrkrshnn | `hrkrshnn.eth` 61 | axic | `axic.eth` 62 | leastwood | `leastwood.eth` 63 | 0xsanson | `sanson.eth` 64 | blockdev | `blockd3v.eth` 65 | dmitriia | 66 | bokeh-eth | 67 | fiveoutofnine | `fiveoutofnine.eth` 68 | asutorufos | 69 | rfart(rfa) | 70 | shuklaayush | `shuklaayush.eth` 71 | Riley Holterhus | 72 | big-tech-sux | 73 | naps62 | `naps62.eth` 74 | Dravee | `dravee.eth` 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Ozone Networks, Inc. 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 100..100 3 | round: down 4 | precision: 2 5 | status: 6 | project: 7 | default: 8 | target: auto 9 | threshold: 0% 10 | base: auto 11 | if_ci_failed: error 12 | 13 | flag_management: 14 | default_rules: # the rules that will be followed for any flag added, generally 15 | statuses: 16 | - type: project 17 | target: 100% 18 | threshold: 0% 19 | - type: patch 20 | target: 100% 21 | threshold: 0% 22 | individual_flags: # exceptions to the default rules above, stated flag by flag 23 | - name: foundry 24 | statuses: 25 | - type: project 26 | target: 100% 27 | threshold: 0% 28 | - type: patch 29 | target: 100% 30 | threshold: 0% 31 | 32 | # Ignore folders that forge coverage is reporting on 33 | ignore: 34 | - offerers 35 | - script 36 | - test 37 | - contracts/test 38 | - contracts/helpers 39 | - contracts/zones 40 | - contracts/lib/ConsiderationStructs.sol 41 | -------------------------------------------------------------------------------- /config/.solcover-reference.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | "conduit/Conduit.sol", 4 | "conduit/ConduitController.sol", 5 | "conduit/lib/ConduitEnums.sol", 6 | "conduit/lib/ConduitStructs.sol", 7 | "Consideration.sol", 8 | "helpers/PointerLibraries.sol", 9 | "interfaces/AbridgedProxyInterfaces.sol", 10 | "interfaces/AbridgedTokenInterfaces.sol", 11 | "interfaces/ConduitControllerInterface.sol", 12 | "interfaces/ConduitInterface.sol", 13 | "interfaces/ConsiderationEventsAndErrors.sol", 14 | "interfaces/ConsiderationInterface.sol", 15 | "interfaces/ContractOffererInterface.sol", 16 | "interfaces/EIP1271Interface.sol", 17 | "interfaces/ERC165.sol", 18 | "interfaces/SeaportInterface.sol", 19 | "interfaces/ZoneInterface.sol", 20 | "lib/ConsiderationBase.sol", 21 | "lib/ConsiderationConstants.sol", 22 | "lib/ConsiderationEnums.sol", 23 | "lib/ConsiderationInternal.sol", 24 | "lib/ConsiderationInternalView.sol", 25 | "lib/ConsiderationPure.sol", 26 | "lib/ConsiderationStructs.sol", 27 | "lib/TokenTransferrer.sol", 28 | "test/EIP1271Wallet.sol", 29 | "test/ExcessReturnDataRecipient.sol", 30 | "test/ERC1155BatchRecipient.sol", 31 | "test/InvalidEthRecipient.sol", 32 | "test/Reenterer.sol", 33 | "test/TestERC1155.sol", 34 | "test/TestERC1155Revert.sol", 35 | "test/TestERC20.sol", 36 | "test/TestERC20NotOk.sol", 37 | "test/TestERC721.sol", 38 | "test/TestERC721Revert.sol", 39 | "test/TestContractOfferer.sol", 40 | "test/TestContractOffererNativeToken.sol", 41 | "test/TestInvalidContractOfferer.sol", 42 | "test/TestInvalidContractOffererRatifyOrder.sol", 43 | "test/TestBadContractOfferer.sol", 44 | "test/TestPostExecution.sol", 45 | "test/TestZone.sol", 46 | "test/TestERC20Panic.sol", 47 | "test/TestERC20Revert.sol", 48 | "test/InvalidERC721Recipient.sol", 49 | "test/ERC721ReceiverMock.sol", 50 | "test/ConduitControllerMock.sol", 51 | "test/ConduitMock.sol", 52 | "test/ConduitMockErrors.sol", 53 | "test/ConduitMockInvalidMagic.sol", 54 | "test/ConduitMockRevertBytes.sol", 55 | "test/ConduitMockRevertNoReason.sol", 56 | "test/TestTransferValidationZoneOfferer.sol", 57 | "zones/PausableZone.sol", 58 | "zones/PausableZoneController.sol", 59 | "zones/interfaces/PausableZoneControllerInterface.sol", 60 | "zones/interfaces/PausableZoneEventsAndErrors.sol", 61 | "zones/interfaces/PausableZoneInterface.sol", 62 | "../reference/shim/Shim.sol", 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /config/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | "conduit/lib/ConduitEnums.sol", 4 | "conduit/lib/ConduitStructs.sol", 5 | "helpers/PointerLibraries.sol", 6 | "interfaces/AbridgedProxyInterfaces.sol", 7 | "interfaces/AbridgedTokenInterfaces.sol", 8 | "interfaces/ConduitControllerInterface.sol", 9 | "interfaces/ConduitInterface.sol", 10 | "interfaces/ConsiderationEventsAndErrors.sol", 11 | "interfaces/ConsiderationInterface.sol", 12 | "interfaces/ContractOffererInterface.sol", 13 | "interfaces/EIP1271Interface.sol", 14 | "interfaces/ERC165.sol", 15 | "interfaces/SeaportInterface.sol", 16 | "interfaces/ZoneInterface.sol", 17 | "lib/ConsiderationConstants.sol", 18 | "lib/ConsiderationEnums.sol", 19 | "lib/ConsiderationStructs.sol", 20 | "test/EIP1271Wallet.sol", 21 | "test/ExcessReturnDataRecipient.sol", 22 | "test/ERC1155BatchRecipient.sol", 23 | "test/InvalidEthRecipient.sol", 24 | "test/Reenterer.sol", 25 | "test/TestERC1155.sol", 26 | "test/TestERC1155Revert.sol", 27 | "test/TestERC20.sol", 28 | "test/TestERC20NotOk.sol", 29 | "test/TestERC721.sol", 30 | "test/TestERC721Revert.sol", 31 | "test/TestContractOfferer.sol", 32 | "test/TestContractOffererNativeToken.sol", 33 | "test/TestInvalidContractOfferer.sol", 34 | "test/TestInvalidContractOffererRatifyOrder.sol", 35 | "test/TestBadContractOfferer.sol", 36 | "test/TestPostExecution.sol", 37 | "test/TestZone.sol", 38 | "test/TestERC20Panic.sol", 39 | "test/TestERC20Revert.sol", 40 | "test/InvalidERC721Recipient.sol", 41 | "test/ERC721ReceiverMock.sol", 42 | "test/ConduitControllerMock.sol", 43 | "test/ConduitMock.sol", 44 | "test/ConduitMockErrors.sol", 45 | "test/ConduitMockInvalidMagic.sol", 46 | "test/ConduitMockRevertBytes.sol", 47 | "test/ConduitMockRevertNoReason.sol", 48 | "test/TestTransferValidationZoneOfferer.sol", 49 | "zones/PausableZone.sol", 50 | "zones/PausableZoneController.sol", 51 | "zones/interfaces/PausableZoneControllerInterface.sol", 52 | "zones/interfaces/PausableZoneEventsAndErrors.sol", 53 | "zones/interfaces/PausableZoneInterface.sol", 54 | ], 55 | configureYulOptimizer: true, 56 | solcOptimizerDetails: { 57 | yul: true, 58 | yulDetails: { 59 | stackAllocation: true, 60 | }, 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /config/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:all", 3 | "rules": { 4 | "compiler-version": ["error", ">=0.8.7"], 5 | "func-visibility": ["warn", { "ignoreConstructors": true }], 6 | "no-empty-blocks": "off", 7 | "no-inline-assembly": "off", 8 | "avoid-low-level-calls": "off", 9 | "not-rely-on-time": "off", 10 | "var-name-mixedcase": "off", 11 | "func-name-mixedcase": "off", 12 | "max-line-length": ["warn", 80], 13 | "function-max-lines": "off", 14 | "code-complexity": ["warn", 15] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /config/.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | contracts/test/ 4 | contracts/zones/PausableZone.sol 5 | 6 | test/ 7 | lib/ -------------------------------------------------------------------------------- /contracts/conduit/Conduit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { Conduit as CoreConduit } from "seaport-core/src/conduit/Conduit.sol"; 5 | 6 | /** 7 | * @title Conduit 8 | * @author 0age 9 | * @notice This contract serves as an originator for "proxied" transfers. Each 10 | * conduit is deployed and controlled by a "conduit controller" that can 11 | * add and remove "channels" or contracts that can instruct the conduit 12 | * to transfer approved ERC20/721/1155 tokens. *IMPORTANT NOTE: each 13 | * conduit has an owner that can arbitrarily add or remove channels, and 14 | * a malicious or negligent owner can add a channel that allows for any 15 | * approved ERC20/721/1155 tokens to be taken immediately — be extremely 16 | * cautious with what conduits you give token approvals to!* 17 | */ 18 | contract LocalConduit is CoreConduit { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /contracts/conduit/ConduitController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConduitController as CoreConduitController 6 | } from "seaport-core/src/conduit/ConduitController.sol"; 7 | 8 | /** 9 | * @title ConduitController 10 | * @author 0age 11 | * @notice ConduitController enables deploying and managing new conduits, or 12 | * contracts that allow registered callers (or open "channels") to 13 | * transfer approved ERC20/721/1155 tokens on their behalf. 14 | */ 15 | contract LocalConduitController is CoreConduitController { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/CriteriaHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | NavigatorCriteriaResolverLib 6 | } from "./NavigatorCriteriaResolverLib.sol"; 7 | 8 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 9 | 10 | import { HelperInterface } from "./HelperInterface.sol"; 11 | 12 | contract CriteriaHelper is HelperInterface { 13 | using NavigatorCriteriaResolverLib for NavigatorContext; 14 | 15 | function prepare( 16 | NavigatorContext memory context 17 | ) public pure returns (NavigatorContext memory) { 18 | return context.withCriteria(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/ExecutionsHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { NavigatorExecutionsLib } from "./NavigatorExecutionsLib.sol"; 5 | 6 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 7 | 8 | import { HelperInterface } from "./HelperInterface.sol"; 9 | 10 | contract ExecutionsHelper is HelperInterface { 11 | using NavigatorExecutionsLib for NavigatorContext; 12 | 13 | function prepare( 14 | NavigatorContext memory context 15 | ) public pure returns (NavigatorContext memory) { 16 | return context.withExecutions(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/FulfillmentsHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { NavigatorFulfillmentsLib } from "./NavigatorFulfillmentsLib.sol"; 5 | 6 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 7 | 8 | import { HelperInterface } from "./HelperInterface.sol"; 9 | 10 | contract FulfillmentsHelper is HelperInterface { 11 | using NavigatorFulfillmentsLib for NavigatorContext; 12 | 13 | function prepare( 14 | NavigatorContext memory context 15 | ) public pure returns (NavigatorContext memory) { 16 | return context.withFulfillments(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/HelperInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 5 | 6 | interface HelperInterface { 7 | function prepare( 8 | NavigatorContext memory context 9 | ) external view returns (NavigatorContext memory); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/NavigatorContextLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | MatchComponent 6 | } from "seaport-sol/src/lib/types/MatchComponentType.sol"; 7 | 8 | import { OrderDetails } from "seaport-sol/src/fulfillments/lib/Structs.sol"; 9 | 10 | import { 11 | AdvancedOrder, 12 | CriteriaResolver, 13 | Execution, 14 | Fulfillment, 15 | FulfillmentComponent 16 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 17 | 18 | import { 19 | NavigatorContext, 20 | NavigatorRequest, 21 | NavigatorResponse 22 | } from "./SeaportNavigatorTypes.sol"; 23 | 24 | import { ErrorsAndWarnings } from "../../order-validator/SeaportValidator.sol"; 25 | 26 | library NavigatorContextLib { 27 | /** 28 | * @dev Creates a new NavigatorContext from a NavigatorRequest, which just 29 | * means slotting the request into the context's request field and 30 | * ignoring the response field. 31 | */ 32 | function from( 33 | NavigatorRequest memory request 34 | ) internal pure returns (NavigatorContext memory context) { 35 | context.request = request; 36 | } 37 | 38 | /** 39 | * @dev Adds an empty response to the context. 40 | */ 41 | function withEmptyResponse( 42 | NavigatorContext memory context 43 | ) internal pure returns (NavigatorContext memory) { 44 | context.response = NavigatorResponse({ 45 | orders: new AdvancedOrder[](0), 46 | criteriaResolvers: new CriteriaResolver[](0), 47 | suggestedActionName: "", 48 | suggestedCallData: hex"", 49 | validationErrors: new ErrorsAndWarnings[](0), 50 | orderDetails: new OrderDetails[](0), 51 | offerFulfillments: new FulfillmentComponent[][](0), 52 | considerationFulfillments: new FulfillmentComponent[][](0), 53 | fulfillments: new Fulfillment[](0), 54 | unspentOfferComponents: new MatchComponent[](0), 55 | unmetConsiderationComponents: new MatchComponent[](0), 56 | explicitExecutions: new Execution[](0), 57 | implicitExecutions: new Execution[](0), 58 | implicitExecutionsPre: new Execution[](0), 59 | implicitExecutionsPost: new Execution[](0), 60 | nativeTokensReturned: 0 61 | }); 62 | return context; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/NavigatorCriteriaResolverLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | AdvancedOrder, 6 | CriteriaResolver 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { 10 | NavigatorAdvancedOrder, 11 | NavigatorContext 12 | } from "./SeaportNavigatorTypes.sol"; 13 | 14 | import { NavigatorAdvancedOrderLib } from "./NavigatorAdvancedOrderLib.sol"; 15 | 16 | library NavigatorCriteriaResolverLib { 17 | using NavigatorAdvancedOrderLib for NavigatorAdvancedOrder[]; 18 | 19 | /** 20 | * @dev Calculate criteria resolvers, merkle proofs, and criteria merkle 21 | * roots for the provided orders and criteria constraints. Modifies 22 | * orders in place to add criteria merkle roots to the appropriate 23 | * offer/consdieration items. Adds calculated criteria resolvers to 24 | * the NavigatorResponse. 25 | */ 26 | function withCriteria( 27 | NavigatorContext memory context 28 | ) internal pure returns (NavigatorContext memory) { 29 | ( 30 | AdvancedOrder[] memory orders, 31 | CriteriaResolver[] memory resolvers 32 | ) = context.request.orders.toAdvancedOrders(); 33 | context.response.orders = orders; 34 | if (context.request.criteriaResolvers.length > 0) { 35 | context.response.criteriaResolvers = context 36 | .request 37 | .criteriaResolvers; 38 | return context; 39 | } else { 40 | context.response.criteriaResolvers = resolvers; 41 | return context; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/NavigatorDetailsLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { UnavailableReason } from "seaport-sol/src/SpaceEnums.sol"; 5 | 6 | import { AdvancedOrder } from "seaport-types/src/lib/ConsiderationStructs.sol"; 7 | 8 | import { AdvancedOrderLib } from "seaport-sol/src/lib/AdvancedOrderLib.sol"; 9 | 10 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 11 | 12 | import { OrderAvailabilityLib } from "./OrderAvailabilityLib.sol"; 13 | 14 | library NavigatorDetailsLib { 15 | using AdvancedOrderLib for AdvancedOrder[]; 16 | using OrderAvailabilityLib for AdvancedOrder[]; 17 | 18 | /** 19 | * @dev Calculate OrderDetails for each order and add them to the NavigatorResponse. 20 | */ 21 | function withDetails( 22 | NavigatorContext memory context 23 | ) internal view returns (NavigatorContext memory) { 24 | UnavailableReason[] memory unavailableReasons = context 25 | .response 26 | .orders 27 | .unavailableReasons( 28 | context.request.maximumFulfilled, 29 | context.request.seaport 30 | ); 31 | bytes32[] memory orderHashes = context.response.orders.getOrderHashes( 32 | address(context.request.seaport) 33 | ); 34 | context.response.orderDetails = context.response.orders.getOrderDetails( 35 | context.response.criteriaResolvers, 36 | orderHashes, 37 | unavailableReasons 38 | ); 39 | return context; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/NavigatorFulfillmentsLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { Fulfillment } from "seaport-types/src/lib/ConsiderationStructs.sol"; 5 | 6 | import { 7 | FulfillmentGeneratorLib 8 | } from "seaport-sol/src/fulfillments/lib/FulfillmentLib.sol"; 9 | 10 | import { 11 | FulfillmentComponent, 12 | MatchComponent, 13 | OrderDetails 14 | } from "seaport-sol/src/fulfillments/lib/Structs.sol"; 15 | 16 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 17 | 18 | library NavigatorFulfillmentsLib { 19 | using FulfillmentGeneratorLib for OrderDetails[]; 20 | 21 | /** 22 | * @dev Calculate fulfillments and match components for the provided orders 23 | * and add them to the NavigatorResponse. 24 | */ 25 | function withFulfillments( 26 | NavigatorContext memory context 27 | ) internal pure returns (NavigatorContext memory) { 28 | ( 29 | , 30 | FulfillmentComponent[][] memory offerFulfillments, 31 | FulfillmentComponent[][] memory considerationFulfillments, 32 | Fulfillment[] memory fulfillments, 33 | MatchComponent[] memory unspentOfferComponents, 34 | MatchComponent[] memory unmetConsiderationComponents 35 | ) = context.response.orderDetails.getFulfillments( 36 | context.request.fulfillmentStrategy, 37 | context.request.recipient, 38 | context.request.caller, 39 | context.request.seed 40 | ); 41 | 42 | context.response.offerFulfillments = offerFulfillments; 43 | context.response.considerationFulfillments = considerationFulfillments; 44 | context.response.fulfillments = fulfillments; 45 | context.response.unspentOfferComponents = unspentOfferComponents; 46 | context 47 | .response 48 | .unmetConsiderationComponents = unmetConsiderationComponents; 49 | return context; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/NavigatorRequestValidatorLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { AdvancedOrder } from "seaport-types/src/lib/ConsiderationStructs.sol"; 5 | 6 | import { Type, OrderStructureLib } from "./OrderStructureLib.sol"; 7 | 8 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 9 | 10 | library NavigatorRequestValidatorLib { 11 | using OrderStructureLib for AdvancedOrder; 12 | 13 | /** 14 | * @dev Bad request error: provided orders include at least one contract order. 15 | * The order helper does not currently support contract orders. 16 | */ 17 | error ContractOrdersNotSupported(); 18 | 19 | /** 20 | * @dev Validate the provided orders. Checks that none of the provided orders 21 | * are contract orders and applies basic criteria constraint validations. 22 | */ 23 | function validate( 24 | NavigatorContext memory context 25 | ) internal pure returns (NavigatorContext memory) { 26 | validateNoContractOrders(context); 27 | return context; 28 | } 29 | 30 | /** 31 | * @dev Checks that none of the provided orders are contract orders. 32 | */ 33 | function validateNoContractOrders( 34 | NavigatorContext memory context 35 | ) internal pure returns (NavigatorContext memory) { 36 | for (uint256 i; i < context.response.orders.length; i++) { 37 | AdvancedOrder memory order = context.response.orders[i]; 38 | if (order.getType() == Type.CONTRACT) { 39 | revert ContractOrdersNotSupported(); 40 | } 41 | } 42 | return context; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/NavigatorSeaportValidatorLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { AdvancedOrder } from "seaport-types/src/lib/ConsiderationStructs.sol"; 5 | 6 | import { AdvancedOrderLib } from "seaport-sol/src/lib/AdvancedOrderLib.sol"; 7 | 8 | import { ErrorsAndWarnings } from "../../order-validator/SeaportValidator.sol"; 9 | 10 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 11 | 12 | library NavigatorSeaportValidatorLib { 13 | using AdvancedOrderLib for AdvancedOrder; 14 | 15 | /** 16 | * @dev Validate each order using SeaportValidator and add the results to 17 | * the NavigatorResponse. 18 | */ 19 | function withErrors( 20 | NavigatorContext memory context 21 | ) internal view returns (NavigatorContext memory) { 22 | AdvancedOrder[] memory orders = context.response.orders; 23 | 24 | ErrorsAndWarnings[] memory errors = new ErrorsAndWarnings[]( 25 | orders.length 26 | ); 27 | for (uint256 i; i < orders.length; i++) { 28 | errors[i] = context.request.validator.isValidOrder( 29 | orders[i].toOrder(), 30 | address(context.request.seaport) 31 | ); 32 | } 33 | context.response.validationErrors = errors; 34 | return context; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/OrderAvailabilityLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ConsiderationInterface 6 | } from "seaport-types/src/interfaces/ConsiderationInterface.sol"; 7 | 8 | import { AdvancedOrder } from "seaport-types/src/lib/ConsiderationStructs.sol"; 9 | 10 | import { UnavailableReason } from "seaport-sol/src/SpaceEnums.sol"; 11 | 12 | import { OrderStructureLib, State } from "./OrderStructureLib.sol"; 13 | 14 | /** 15 | * @notice Helper library for determining order availability. 16 | */ 17 | library OrderAvailabilityLib { 18 | using OrderStructureLib for AdvancedOrder; 19 | 20 | /** 21 | * @notice Returns true if the order is available for fulfillment. 22 | */ 23 | function isAvailable( 24 | AdvancedOrder memory order, 25 | ConsiderationInterface seaport 26 | ) internal view returns (bool) { 27 | return unavailableReason(order, seaport) == UnavailableReason.AVAILABLE; 28 | } 29 | 30 | /** 31 | * @notice Returns the order's UnavailableReason. Available orders will 32 | * return UnavailableReason.AVAILABLE to indicate that they are 33 | * available for fulfillment. 34 | */ 35 | function unavailableReason( 36 | AdvancedOrder memory order, 37 | ConsiderationInterface seaport 38 | ) internal view returns (UnavailableReason) { 39 | if (order.parameters.endTime <= block.timestamp) { 40 | return UnavailableReason.EXPIRED; 41 | } 42 | if (order.parameters.startTime > block.timestamp) { 43 | return UnavailableReason.STARTS_IN_FUTURE; 44 | } 45 | if (order.getState(seaport) == State.CANCELLED) { 46 | return UnavailableReason.CANCELLED; 47 | } 48 | if (order.getState(seaport) == State.FULLY_FILLED) { 49 | return UnavailableReason.ALREADY_FULFILLED; 50 | } 51 | return UnavailableReason.AVAILABLE; 52 | } 53 | 54 | /** 55 | * @notice Return an array of UnavailableReasons for the provided orders. 56 | */ 57 | function unavailableReasons( 58 | AdvancedOrder[] memory orders, 59 | uint256 maximumFulfilled, 60 | ConsiderationInterface seaport 61 | ) internal view returns (UnavailableReason[] memory) { 62 | UnavailableReason[] memory reasons = new UnavailableReason[]( 63 | orders.length 64 | ); 65 | uint256 totalAvailable; 66 | UnavailableReason reason; 67 | for (uint256 i = 0; i < orders.length; i++) { 68 | if (totalAvailable < maximumFulfilled) { 69 | reason = unavailableReason(orders[i], seaport); 70 | } else { 71 | reason = UnavailableReason.MAX_FULFILLED_SATISFIED; 72 | } 73 | reasons[i] = reason; 74 | if (reason == UnavailableReason.AVAILABLE) { 75 | totalAvailable++; 76 | } 77 | } 78 | return reasons; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/OrderDetailsHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { NavigatorDetailsLib } from "./NavigatorDetailsLib.sol"; 5 | 6 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 7 | 8 | import { HelperInterface } from "./HelperInterface.sol"; 9 | 10 | contract OrderDetailsHelper is HelperInterface { 11 | using NavigatorDetailsLib for NavigatorContext; 12 | 13 | function prepare( 14 | NavigatorContext memory context 15 | ) public view returns (NavigatorContext memory) { 16 | return context.withDetails(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/RequestValidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | NavigatorRequestValidatorLib 6 | } from "./NavigatorRequestValidatorLib.sol"; 7 | 8 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 9 | 10 | import { HelperInterface } from "./HelperInterface.sol"; 11 | 12 | contract RequestValidator is HelperInterface { 13 | using NavigatorRequestValidatorLib for NavigatorContext; 14 | 15 | function prepare( 16 | NavigatorContext memory context 17 | ) public pure returns (NavigatorContext memory) { 18 | return context.validate(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/SeaportNavigatorInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | NavigatorRequest, 6 | NavigatorResponse 7 | } from "./SeaportNavigatorTypes.sol"; 8 | 9 | interface SeaportNavigatorInterface { 10 | function prepare( 11 | NavigatorRequest memory request 12 | ) external view returns (NavigatorResponse memory); 13 | 14 | /** 15 | * @notice Generate a criteria merkle root from an array of `tokenIds`. Use 16 | * this helper to construct an order item's `identifierOrCriteria`. 17 | * 18 | * @param tokenIds An array of integer token IDs to be converted to a merkle 19 | * root. 20 | * 21 | * @return The bytes32 merkle root of a criteria tree containing the given 22 | * token IDs. 23 | */ 24 | function criteriaRoot( 25 | uint256[] memory tokenIds 26 | ) external pure returns (bytes32); 27 | 28 | /** 29 | * @notice Generate a criteria merkle proof that `id` is a member of 30 | * `tokenIds`. Reverts if `id` is not a member of `tokenIds`. Use 31 | * this helper to construct proof data for criteria resolvers. 32 | * 33 | * @param tokenIds An array of integer token IDs. 34 | * @param id The integer token ID to generate a proof for. 35 | * 36 | * @return Merkle proof that the given token ID is amember of the criteria 37 | * tree containing the given token IDs. 38 | */ 39 | function criteriaProof( 40 | uint256[] memory tokenIds, 41 | uint256 id 42 | ) external pure returns (bytes32[] memory); 43 | } 44 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/SuggestedActionHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { NavigatorSuggestedActionLib } from "./NavigatorSuggestedActionLib.sol"; 5 | 6 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 7 | 8 | import { HelperInterface } from "./HelperInterface.sol"; 9 | 10 | contract SuggestedActionHelper is HelperInterface { 11 | using NavigatorSuggestedActionLib for NavigatorContext; 12 | 13 | function prepare( 14 | NavigatorContext memory context 15 | ) public view returns (NavigatorContext memory) { 16 | return context.withSuggestedAction(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/helpers/navigator/lib/ValidatorHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | NavigatorSeaportValidatorLib 6 | } from "./NavigatorSeaportValidatorLib.sol"; 7 | 8 | import { NavigatorContext } from "./SeaportNavigatorTypes.sol"; 9 | 10 | import { HelperInterface } from "./HelperInterface.sol"; 11 | 12 | contract ValidatorHelper is HelperInterface { 13 | using NavigatorSeaportValidatorLib for NavigatorContext; 14 | 15 | function prepare( 16 | NavigatorContext memory context 17 | ) public view returns (NavigatorContext memory) { 18 | return context.withErrors(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/helpers/order-validator/lib/SafeStaticCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | library SafeStaticCall { 5 | function safeStaticCallBool( 6 | address target, 7 | bytes memory callData, 8 | bool expectedReturn 9 | ) internal view returns (bool) { 10 | (bool success, bytes memory res) = target.staticcall(callData); 11 | if (!success) return false; 12 | if (res.length != 32) return false; 13 | 14 | if ( 15 | bytes32(res) & 16 | 0x0000000000000000000000000000000000000000000000000000000000000001 != 17 | bytes32(res) 18 | ) { 19 | return false; 20 | } 21 | 22 | return expectedReturn ? res[31] == 0x01 : res[31] == 0; 23 | } 24 | 25 | function safeStaticCallAddress( 26 | address target, 27 | bytes memory callData, 28 | address expectedReturn 29 | ) internal view returns (bool) { 30 | (bool success, bytes memory res) = target.staticcall(callData); 31 | if (!success) return false; 32 | if (res.length != 32) return false; 33 | 34 | if ( 35 | bytes32(res) & 36 | 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF != 37 | bytes32(res) 38 | ) { 39 | // Ensure only 20 bytes used 40 | return false; 41 | } 42 | 43 | return abi.decode(res, (address)) == expectedReturn; 44 | } 45 | 46 | function safeStaticCallUint256( 47 | address target, 48 | bytes memory callData, 49 | uint256 minExpectedReturn 50 | ) internal view returns (bool) { 51 | (bool success, bytes memory res) = target.staticcall(callData); 52 | if (!success) return false; 53 | if (res.length != 32) return false; 54 | 55 | return abi.decode(res, (uint256)) >= minExpectedReturn; 56 | } 57 | 58 | function safeStaticCallBytes4( 59 | address target, 60 | bytes memory callData, 61 | bytes4 expectedReturn 62 | ) internal view returns (bool) { 63 | (bool success, bytes memory res) = target.staticcall(callData); 64 | if (!success) return false; 65 | if (res.length != 32) return false; 66 | if ( 67 | bytes32(res) & 68 | 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 != 69 | bytes32(res) 70 | ) { 71 | // Ensure only 4 bytes used 72 | return false; 73 | } 74 | 75 | return abi.decode(res, (bytes4)) == expectedReturn; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/interfaces/ZoneInteractionErrors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | /** 5 | * @title ZoneInteractionErrors 6 | * @author 0age 7 | * @notice ZoneInteractionErrors contains errors related to zone interaction. 8 | */ 9 | interface ZoneInteractionErrors { 10 | /** 11 | * @dev Revert with an error when attempting to fill an order that specifies 12 | * a restricted submitter as its order type when not submitted by 13 | * either the offerer or the order's zone or approved as valid by the 14 | * zone in question via a call to `isValidOrder`. 15 | * 16 | * @param orderHash The order hash for the invalid restricted order. 17 | */ 18 | error InvalidRestrictedOrder(bytes32 orderHash); 19 | 20 | /** 21 | * @dev Revert with an error when attempting to fill a contract order that 22 | * fails to generate an order successfully, that does not adhere to the 23 | * requirements for minimum spent or maximum received supplied by the 24 | * fulfiller, or that fails the post-execution `ratifyOrder` check.. 25 | * 26 | * @param orderHash The order hash for the invalid contract order. 27 | */ 28 | error InvalidContractOrder(bytes32 orderHash); 29 | } 30 | -------------------------------------------------------------------------------- /contracts/interfaces/ZoneInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ZoneParameters, 6 | Schema 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { IERC165 } from "seaport-types/src/interfaces/IERC165.sol"; 10 | 11 | /** 12 | * @title ZoneInterface 13 | * @notice Contains functions exposed by a zone. 14 | */ 15 | interface ZoneInterface is IERC165 { 16 | /** 17 | * @dev Validates an order. 18 | * 19 | * @param zoneParameters The context about the order fulfillment and any 20 | * supplied extraData. 21 | * 22 | * @return validOrderMagicValue The magic value that indicates a valid 23 | * order. 24 | */ 25 | function validateOrder( 26 | ZoneParameters calldata zoneParameters 27 | ) external returns (bytes4 validOrderMagicValue); 28 | 29 | /** 30 | * @dev Returns the metadata for this zone. 31 | * 32 | * @return name The name of the zone. 33 | * @return schemas The schemas that the zone implements. 34 | */ 35 | function getSeaportMetadata() 36 | external 37 | view 38 | returns ( 39 | string memory name, 40 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 41 | ); 42 | 43 | function supportsInterface( 44 | bytes4 interfaceId 45 | ) external view override returns (bool); 46 | } 47 | -------------------------------------------------------------------------------- /contracts/test/ConduitMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConduitInterface 6 | } from "seaport-types/src/interfaces/ConduitInterface.sol"; 7 | 8 | import { 9 | ConduitBatch1155Transfer, 10 | ConduitTransfer 11 | } from "seaport-types/src/conduit/lib/ConduitStructs.sol"; 12 | 13 | contract ConduitMock is ConduitInterface { 14 | constructor() {} 15 | 16 | function execute( 17 | ConduitTransfer[] calldata /* transfers */ 18 | ) external pure override returns (bytes4) { 19 | // Return the valid magic value. 20 | return 0x4ce34aa2; 21 | } 22 | 23 | function executeBatch1155( 24 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 25 | ) external view override returns (bytes4 magicValue) {} 26 | 27 | function executeWithBatch1155( 28 | ConduitTransfer[] calldata /* standardTransfers */, 29 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 30 | ) external view override returns (bytes4 magicValue) {} 31 | 32 | function updateChannel(address channel, bool isOpen) external override {} 33 | } 34 | -------------------------------------------------------------------------------- /contracts/test/ConduitMockInvalidMagic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConduitInterface 6 | } from "seaport-types/src/interfaces/ConduitInterface.sol"; 7 | 8 | import { 9 | ConduitBatch1155Transfer, 10 | ConduitTransfer 11 | } from "seaport-types/src/conduit/lib/ConduitStructs.sol"; 12 | 13 | contract ConduitMockInvalidMagic is ConduitInterface { 14 | constructor() {} 15 | 16 | function execute( 17 | ConduitTransfer[] calldata /* transfers */ 18 | ) external pure override returns (bytes4) { 19 | return 0xabcd0000; 20 | } 21 | 22 | function executeBatch1155( 23 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 24 | ) external view override returns (bytes4 magicValue) {} 25 | 26 | function executeWithBatch1155( 27 | ConduitTransfer[] calldata /* standardTransfers */, 28 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 29 | ) external view override returns (bytes4 magicValue) {} 30 | 31 | function updateChannel(address channel, bool isOpen) external override {} 32 | } 33 | -------------------------------------------------------------------------------- /contracts/test/ConduitMockRevertBytes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConduitInterface 6 | } from "seaport-types/src/interfaces/ConduitInterface.sol"; 7 | 8 | import { 9 | ConduitBatch1155Transfer, 10 | ConduitTransfer 11 | } from "seaport-types/src/conduit/lib/ConduitStructs.sol"; 12 | 13 | contract ConduitMockRevertBytes is ConduitInterface { 14 | constructor() {} 15 | 16 | error CustomError(); 17 | 18 | function execute( 19 | ConduitTransfer[] calldata /* transfers */ 20 | ) external pure override returns (bytes4) { 21 | revert CustomError(); 22 | } 23 | 24 | function executeBatch1155( 25 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 26 | ) external view override returns (bytes4 magicValue) {} 27 | 28 | function executeWithBatch1155( 29 | ConduitTransfer[] calldata /* standardTransfers */, 30 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 31 | ) external view override returns (bytes4 magicValue) {} 32 | 33 | function updateChannel(address channel, bool isOpen) external override {} 34 | } 35 | -------------------------------------------------------------------------------- /contracts/test/ConduitMockRevertNoReason.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConduitInterface 6 | } from "seaport-types/src/interfaces/ConduitInterface.sol"; 7 | 8 | import { 9 | ConduitBatch1155Transfer, 10 | ConduitTransfer 11 | } from "seaport-types/src/conduit/lib/ConduitStructs.sol"; 12 | 13 | contract ConduitMockRevertNoReason is ConduitInterface { 14 | constructor() {} 15 | 16 | function execute( 17 | ConduitTransfer[] calldata /* transfers */ 18 | ) external pure override returns (bytes4) { 19 | // Revert without reason string. 20 | revert(); 21 | } 22 | 23 | function executeBatch1155( 24 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 25 | ) external view override returns (bytes4 magicValue) {} 26 | 27 | function executeWithBatch1155( 28 | ConduitTransfer[] calldata /* standardTransfers */, 29 | ConduitBatch1155Transfer[] calldata /* batch1155Transfers */ 30 | ) external view override returns (bytes4 magicValue) {} 31 | 32 | function updateChannel(address channel, bool isOpen) external override {} 33 | } 34 | -------------------------------------------------------------------------------- /contracts/test/EIP1271Wallet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | interface ERC20ApprovalInterface { 5 | function approve(address, uint256) external returns (bool); 6 | } 7 | 8 | interface NFTApprovalInterface { 9 | function setApprovalForAll(address, bool) external; 10 | } 11 | 12 | contract EIP1271Wallet { 13 | bytes4 private constant _EIP_1271_MAGIC_VALUE = 0x1626ba7e; 14 | 15 | address public immutable owner; 16 | 17 | bool public showRevertMessage; 18 | 19 | mapping(bytes32 => bool) public digestApproved; 20 | 21 | bool public isValid; 22 | 23 | constructor(address _owner) { 24 | owner = _owner; 25 | showRevertMessage = true; 26 | isValid = true; 27 | } 28 | 29 | function setValid(bool valid) external { 30 | isValid = valid; 31 | } 32 | 33 | function revertWithMessage(bool showMessage) external { 34 | showRevertMessage = showMessage; 35 | } 36 | 37 | function registerDigest(bytes32 digest, bool approved) external { 38 | digestApproved[digest] = approved; 39 | } 40 | 41 | function approveERC20( 42 | ERC20ApprovalInterface token, 43 | address operator, 44 | uint256 amount 45 | ) external { 46 | if (msg.sender != owner) { 47 | revert("Only owner"); 48 | } 49 | 50 | token.approve(operator, amount); 51 | } 52 | 53 | function approveNFT(NFTApprovalInterface token, address operator) external { 54 | if (msg.sender != owner) { 55 | revert("Only owner"); 56 | } 57 | 58 | token.setApprovalForAll(operator, true); 59 | } 60 | 61 | function isValidSignature( 62 | bytes32 digest, 63 | bytes memory signature 64 | ) external view returns (bytes4) { 65 | if (digestApproved[digest]) { 66 | return _EIP_1271_MAGIC_VALUE; 67 | } 68 | 69 | // NOTE: this is obviously not secure, do not use outside of testing. 70 | if (signature.length == 64) { 71 | // All signatures of length 64 are OK as long as valid is true 72 | return isValid ? _EIP_1271_MAGIC_VALUE : bytes4(0xffffffff); 73 | } 74 | 75 | if (signature.length != 65) { 76 | revert(); 77 | } 78 | 79 | bytes32 r; 80 | bytes32 s; 81 | uint8 v; 82 | 83 | assembly { 84 | r := mload(add(signature, 0x20)) 85 | s := mload(add(signature, 0x40)) 86 | v := byte(0, mload(add(signature, 0x60))) 87 | } 88 | 89 | if ( 90 | uint256(s) > 91 | 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 92 | ) { 93 | revert(); 94 | } 95 | 96 | if (v != 27 && v != 28) { 97 | revert(); 98 | } 99 | 100 | address signer = ecrecover(digest, v, r, s); 101 | 102 | if (signer == address(0)) { 103 | revert(); 104 | } 105 | 106 | if (signer != owner) { 107 | if (showRevertMessage) { 108 | revert("BAD SIGNER"); 109 | } 110 | 111 | revert(); 112 | } 113 | 114 | return isValid ? _EIP_1271_MAGIC_VALUE : bytes4(0xffffffff); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /contracts/test/ERC1155BatchRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | contract ERC1155BatchRecipient { 5 | error UnexpectedBatchData(); 6 | 7 | function onERC1155BatchReceived( 8 | address, 9 | address, 10 | uint256[] calldata, 11 | uint256[] calldata, 12 | bytes memory data 13 | ) external pure returns (bytes4) { 14 | if (data.length != 0) { 15 | revert UnexpectedBatchData(); 16 | } 17 | return ERC1155BatchRecipient.onERC1155BatchReceived.selector; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test/ERC721ReceiverMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | IERC721Receiver 6 | } from "seaport-types/src/interfaces/IERC721Receiver.sol"; 7 | 8 | contract ERC721ReceiverMock is IERC721Receiver { 9 | enum Error { 10 | None, 11 | RevertWithMessage, 12 | RevertWithoutMessage, 13 | Panic 14 | } 15 | 16 | bytes4 private immutable _retval; 17 | Error private immutable _error; 18 | 19 | event Received( 20 | address operator, 21 | address from, 22 | uint256 tokenId, 23 | bytes data, 24 | uint256 gas 25 | ); 26 | 27 | constructor(bytes4 retval, Error error) { 28 | _retval = retval; 29 | _error = error; 30 | } 31 | 32 | function onERC721Received( 33 | address operator, 34 | address from, 35 | uint256 tokenId, 36 | bytes memory data 37 | ) public override returns (bytes4) { 38 | if (_error == Error.RevertWithMessage) { 39 | revert("ERC721ReceiverMock: reverting"); 40 | } else if (_error == Error.RevertWithoutMessage) { 41 | revert(); 42 | } else if (_error == Error.Panic) { 43 | uint256 a = uint256(0) / uint256(0); 44 | a; 45 | } 46 | emit Received(operator, from, tokenId, data, gasleft()); 47 | return _retval; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/test/InvalidERC721Recipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | interface IERC721Receiver { 5 | function onERC721Received( 6 | address, 7 | address, 8 | uint256, 9 | bytes calldata 10 | ) external returns (bytes4); 11 | } 12 | 13 | contract InvalidERC721Recipient is IERC721Receiver { 14 | function onERC721Received( 15 | address /* operator */, 16 | address /* from */, 17 | uint256 /* tokenId */, 18 | bytes calldata /* data */ 19 | ) external pure override returns (bytes4) { 20 | return 0xabcd0000; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/test/OffererZoneFailureReason.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | enum OffererZoneFailureReason { 5 | None, 6 | ContractOfferer_generateReverts, // Offerer generateOrder reverts 7 | ContractOfferer_generateReturnsInvalidEncoding, // Bad encoding 8 | ContractOfferer_ratifyReverts, // Offerer ratifyOrder reverts 9 | ContractOfferer_InsufficientMinimumReceived, // too few minimum received items 10 | ContractOfferer_IncorrectMinimumReceived, // incorrect (insufficient amount, wrong token, etc.) minimum received items 11 | ContractOfferer_ExcessMaximumSpent, // too many maximum spent items 12 | ContractOfferer_IncorrectMaximumSpent, // incorrect (too many, wrong token, etc.) maximum spent items 13 | ContractOfferer_InvalidMagicValue, // Offerer did not return correct magic value 14 | Zone_authorizeRevertsMatchReverts, // Zone authorizeOrder call reverts, which triggers a top level revert only on match* 15 | Zone_validateReverts, // Zone validateOrder call reverts 16 | Zone_authorizeInvalidMagicValue, // Zone authorizeOrder call returns invalid magic value 17 | Zone_validateInvalidMagicValue // Zone validateOrder call returns invalid magic value 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/Reenterer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | contract Reenterer { 5 | bool public isPrepared; 6 | address public target; 7 | uint256 public msgValue; 8 | bytes public callData; 9 | 10 | event Reentered(bytes returnData); 11 | 12 | function prepare( 13 | address targetToUse, 14 | uint256 msgValueToUse, 15 | bytes calldata callDataToUse 16 | ) external { 17 | target = targetToUse; 18 | msgValue = msgValueToUse; 19 | callData = callDataToUse; 20 | isPrepared = true; 21 | } 22 | 23 | receive() external payable { 24 | if (isPrepared) { 25 | (bool success, bytes memory returnData) = target.call{ 26 | value: msgValue 27 | }(callData); 28 | 29 | if (!success) { 30 | assembly { 31 | returndatacopy(0, 0, returndatasize()) 32 | revert(0, returndatasize()) 33 | } 34 | } 35 | emit Reentered(returnData); 36 | 37 | isPrepared = false; 38 | } 39 | } 40 | 41 | function execute(address to, uint256 value, bytes memory data) external { 42 | (bool success, ) = payable(to).call{ value: value }(data); 43 | if (!success) { 44 | assembly { 45 | returndatacopy(0, 0, returndatasize()) 46 | revert(0, returndatasize()) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test/TestERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC1155 } from "@rari-capital/solmate/src/tokens/ERC1155.sol"; 5 | 6 | // Used for minting test ERC1155s in our tests 7 | contract TestERC1155 is ERC1155 { 8 | function mint( 9 | address to, 10 | uint256 tokenId, 11 | uint256 amount 12 | ) public returns (bool) { 13 | _mint(to, tokenId, amount, ""); 14 | return true; 15 | } 16 | 17 | function uri(uint256) public pure override returns (string memory) { 18 | return "uri"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/TestERC1155Revert.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | contract TestERC1155Revert { 5 | function safeTransferFrom( 6 | address /* from */, 7 | address /* to */, 8 | uint256 /* id */, 9 | uint256 /* amount */, 10 | bytes calldata /* data */ 11 | ) public pure { 12 | revert( 13 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 14 | ); 15 | } 16 | 17 | function safeBatchTransferFrom( 18 | address /* from */, 19 | address /* to */, 20 | uint256[] memory /* ids */, 21 | uint256[] memory /* values */, 22 | bytes memory /* data */ 23 | ) public pure { 24 | revert("Some ERC1155 revert message for batch transfers"); 25 | } 26 | 27 | function getRevertData() public pure returns (bytes memory) { 28 | assembly { 29 | mstore(0x40, 0) 30 | mstore(0, shl(20, 1)) 31 | mstore(add(0x20, shl(20, 1)), 1) 32 | return(0, add(0x20, shl(20, 1))) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/test/TestERC1271.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; 5 | 6 | contract TestERC1271 is IERC1271 { 7 | address public immutable owner; 8 | 9 | constructor(address owner_) { 10 | owner = owner_; 11 | } 12 | 13 | function isValidSignature( 14 | bytes32 digest, 15 | bytes memory signature 16 | ) external view returns (bytes4) { 17 | bytes32 r; 18 | bytes32 s; 19 | uint8 v; 20 | 21 | assembly { 22 | r := mload(add(signature, 0x20)) 23 | s := mload(add(signature, 0x40)) 24 | v := byte(0, mload(add(signature, 0x60))) 25 | } 26 | 27 | if ( 28 | uint256(s) > 29 | 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 30 | ) { 31 | revert(); 32 | } 33 | 34 | if (v != 27 && v != 28) { 35 | revert(); 36 | } 37 | 38 | address signer = ecrecover(digest, v, r, s); 39 | 40 | if (signer == address(0)) { 41 | revert(); 42 | } 43 | 44 | if (signer != owner) { 45 | revert(); 46 | } 47 | 48 | return IERC1271.isValidSignature.selector; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test/TestERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; 5 | 6 | // Used for minting test ERC20s in our tests 7 | contract TestERC20 is ERC20("Test20", "TST20", 18) { 8 | bool public blocked; 9 | 10 | bool public noReturnData; 11 | 12 | constructor() { 13 | blocked = false; 14 | noReturnData = false; 15 | } 16 | 17 | function blockTransfer(bool blocking) external { 18 | blocked = blocking; 19 | } 20 | 21 | function setNoReturnData(bool noReturn) external { 22 | noReturnData = noReturn; 23 | } 24 | 25 | function mint(address to, uint256 amount) external returns (bool) { 26 | _mint(to, amount); 27 | return true; 28 | } 29 | 30 | function transferFrom( 31 | address from, 32 | address to, 33 | uint256 amount 34 | ) public override returns (bool ok) { 35 | if (blocked) { 36 | return false; 37 | } 38 | 39 | uint256 allowed = allowance[from][msg.sender]; 40 | 41 | if (amount > allowed) { 42 | revert("NOT_AUTHORIZED"); 43 | } 44 | 45 | super.transferFrom(from, to, amount); 46 | 47 | if (noReturnData) { 48 | assembly { 49 | return(0, 0) 50 | } 51 | } 52 | 53 | ok = true; 54 | } 55 | 56 | function increaseAllowance( 57 | address spender, 58 | uint256 amount 59 | ) external returns (bool) { 60 | uint256 current = allowance[msg.sender][spender]; 61 | uint256 remaining = type(uint256).max - current; 62 | if (amount > remaining) { 63 | amount = remaining; 64 | } 65 | approve(spender, current + amount); 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/test/TestERC20NotOk.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; 5 | 6 | // Used for minting test ERC20s in our tests. 7 | contract TestERC20NotOk is ERC20("Test20NotOk", "TST20NO", 18) { 8 | bool public notOk; 9 | 10 | function mint(address to, uint256 amount) external returns (bool) { 11 | _mint(to, amount); 12 | return true; 13 | } 14 | 15 | function transferFrom( 16 | address /* from */, 17 | address /* to */, 18 | uint256 /* amount */ 19 | ) public pure override returns (bool) { 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/test/TestERC20Panic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; 5 | 6 | contract TestERC20Panic is ERC20("TestPanic", "PANIC", 18) { 7 | function mint(address to, uint256 amount) external returns (bool) { 8 | _mint(to, amount); 9 | return true; 10 | } 11 | 12 | function transferFrom( 13 | address /* from */, 14 | address /* to */, 15 | uint256 /* amount */ 16 | ) public pure override returns (bool) { 17 | uint256 a = uint256(0) / uint256(0); 18 | a; 19 | 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/test/TestERC20Revert.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; 5 | 6 | contract TestERC20Revert is ERC20("TestRevert", "REVERT", 18) { 7 | function mint(address to, uint256 amount) external { 8 | _mint(to, amount); 9 | } 10 | 11 | function transferFrom( 12 | address /* from */, 13 | address /* to */, 14 | uint256 /* amount */ 15 | ) public pure override returns (bool) { 16 | revert( 17 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/TestERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol"; 5 | 6 | // Used for minting test ERC721s in our tests 7 | contract TestERC721 is ERC721("Test721", "TST721") { 8 | function mint(address to, uint256 tokenId) public returns (bool) { 9 | _mint(to, tokenId); 10 | return true; 11 | } 12 | 13 | function tokenURI(uint256) public pure override returns (string memory) { 14 | return "tokenURI"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/test/TestERC721Fee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol"; 5 | import { ERC2981 } from "./ERC2981.sol"; 6 | 7 | contract TestERC721Fee is ERC721, ERC2981 { 8 | /// @notice When set to false, `royaltyInfo` reverts 9 | bool creatorFeeEnabled = false; 10 | /// @notice Below the min transaction price, `royaltyInfo` reverts 11 | uint256 minTransactionPrice = 0; 12 | 13 | constructor() ERC721("Fee", "FEE") {} 14 | 15 | function supportsInterface( 16 | bytes4 interfaceId 17 | ) public view override(ERC721, ERC2981) returns (bool) { 18 | return 19 | ERC721.supportsInterface(interfaceId) || 20 | ERC2981.supportsInterface(interfaceId); 21 | } 22 | 23 | function mint(address to, uint256 id) external { 24 | _mint(to, id); 25 | } 26 | 27 | function burn(uint256 id) external { 28 | _burn(id); 29 | } 30 | 31 | function tokenURI(uint256) public pure override returns (string memory) { 32 | return "tokenURI"; 33 | } 34 | 35 | function royaltyInfo( 36 | uint256, 37 | uint256 _salePrice 38 | ) public view override returns (address, uint256) { 39 | if (!creatorFeeEnabled) { 40 | revert("creator fee disabled"); 41 | } 42 | if (_salePrice < minTransactionPrice) { 43 | revert("sale price too low"); 44 | } 45 | 46 | return ( 47 | 0x000000000000000000000000000000000000fEE2, 48 | (_salePrice * (creatorFeeEnabled ? 250 : 0)) / 10000 49 | ); // 2.5% fee to 0xFEE2 50 | } 51 | 52 | function setCreatorFeeEnabled(bool enabled) public { 53 | creatorFeeEnabled = enabled; 54 | } 55 | 56 | function setMinTransactionPrice(uint256 minTransactionPrice_) public { 57 | minTransactionPrice = minTransactionPrice_; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/test/TestERC721Funky.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol"; 5 | 6 | /** 7 | * @notice TestERC721Funky is an ERC721 that implements ERC2981 with an incorrect return type. 8 | */ 9 | contract TestERC721Funky is ERC721("TestERC721Funky", "TST721FUNKY") { 10 | function mint(address to, uint256 id) external { 11 | _mint(to, id); 12 | } 13 | 14 | function burn(uint256 id) external { 15 | _burn(id); 16 | } 17 | 18 | function tokenURI(uint256) public pure override returns (string memory) { 19 | return "tokenURI"; 20 | } 21 | 22 | function royaltyInfo(uint256, uint256) public pure returns (address) { 23 | return (0x000000000000000000000000000000000000fEE2); // 2.5% fee to 0xFEE2 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/test/TestERC721Revert.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | import { TestERC721 } from "./TestERC721.sol"; 5 | 6 | contract TestERC721Revert is TestERC721 { 7 | function transferFrom( 8 | address /* from */, 9 | address /* to */, 10 | uint256 /* amount */ 11 | ) public pure override { 12 | revert( 13 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/test/TestInvalidContractOfferer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ReceivedItem, 6 | SpentItem 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { TestContractOfferer } from "./TestContractOfferer.sol"; 10 | 11 | contract TestInvalidContractOfferer is TestContractOfferer { 12 | error RevertWithData(bytes revertData); 13 | 14 | constructor(address seaport) TestContractOfferer(seaport) {} 15 | 16 | function generateOrder( 17 | address, 18 | SpentItem[] calldata, 19 | SpentItem[] calldata, 20 | bytes calldata context 21 | ) 22 | external 23 | pure 24 | override 25 | returns (SpentItem[] memory, ReceivedItem[] memory) 26 | { 27 | revert RevertWithData(context); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/test/TestInvalidContractOffererRatifyOrder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ReceivedItem, 6 | SpentItem 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { TestContractOfferer } from "./TestContractOfferer.sol"; 10 | 11 | contract TestInvalidContractOffererRatifyOrder is TestContractOfferer { 12 | constructor(address seaport) TestContractOfferer(seaport) {} 13 | 14 | function ratifyOrder( 15 | SpentItem[] calldata, 16 | ReceivedItem[] calldata, 17 | bytes calldata, 18 | bytes32[] calldata, 19 | uint256 20 | ) external pure override returns (bytes4) { 21 | return bytes4(keccak256("throw")); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/TestInvalidZone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ZoneParameters, 6 | Schema 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 10 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 11 | 12 | contract TestInvalidZone is ERC165, ZoneInterface { 13 | function authorizeOrder( 14 | ZoneParameters calldata 15 | ) public pure returns (bytes4) { 16 | return this.authorizeOrder.selector; 17 | } 18 | 19 | // Returns invalid magic value 20 | function validateOrder( 21 | ZoneParameters calldata 22 | ) external pure returns (bytes4 validOrderMagicValue) { 23 | return ZoneInterface.getSeaportMetadata.selector; 24 | } 25 | 26 | /** 27 | * @dev Returns the metadata for this zone. 28 | */ 29 | function getSeaportMetadata() 30 | external 31 | pure 32 | override 33 | returns ( 34 | string memory name, 35 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 36 | ) 37 | { 38 | schemas = new Schema[](1); 39 | schemas[0].id = 3003; 40 | schemas[0].metadata = new bytes(0); 41 | 42 | return ("TestZone", schemas); 43 | } 44 | 45 | function supportsInterface( 46 | bytes4 interfaceId 47 | ) public view override(ERC165, ZoneInterface) returns (bool) { 48 | return 49 | interfaceId == type(ZoneInterface).interfaceId || 50 | super.supportsInterface(interfaceId); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/test/TestPostExecution.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 5 | 6 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 7 | import { 8 | ERC721Interface 9 | } from "seaport-types/src/interfaces/AbridgedTokenInterfaces.sol"; 10 | 11 | import { ItemType } from "seaport-types/src/lib/ConsiderationEnums.sol"; 12 | 13 | import { 14 | ReceivedItem, 15 | Schema, 16 | ZoneParameters 17 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 18 | 19 | contract TestPostExecution is ERC165, ZoneInterface { 20 | function authorizeOrder( 21 | ZoneParameters calldata 22 | ) public pure returns (bytes4) { 23 | return this.authorizeOrder.selector; 24 | } 25 | 26 | function validateOrder( 27 | ZoneParameters calldata zoneParameters 28 | ) external view override returns (bytes4 validOrderMagicValue) { 29 | if (zoneParameters.consideration.length == 0) { 30 | revert("No consideration items supplied"); 31 | } 32 | 33 | ReceivedItem memory receivedItem = zoneParameters.consideration[0]; 34 | 35 | address currentOwner; 36 | try 37 | ERC721Interface(receivedItem.token).ownerOf(receivedItem.identifier) 38 | returns (address owner) { 39 | currentOwner = owner; 40 | } catch { 41 | revert("Unsupported consideration token type (must implement 721)"); 42 | } 43 | 44 | if (receivedItem.itemType != ItemType.ERC721) { 45 | revert("Validity check performed with unsupported item type"); 46 | } 47 | 48 | // Note that endAmount has been repurposed as recipient; this interface 49 | // still needs to be modified to return spent / received items. 50 | if (receivedItem.amount != 1) { 51 | // Note that this is currently failing in the matchOrder case. 52 | revert("Returned item amount incorrectly modified"); 53 | } 54 | 55 | if (currentOwner != receivedItem.recipient) { 56 | revert("Validity check performed prior to execution"); 57 | } 58 | 59 | validOrderMagicValue = ZoneInterface.validateOrder.selector; 60 | } 61 | 62 | /** 63 | * @dev Returns the metadata for this zone. 64 | */ 65 | function getSeaportMetadata() 66 | external 67 | pure 68 | override 69 | returns ( 70 | string memory name, 71 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 72 | ) 73 | { 74 | schemas = new Schema[](1); 75 | schemas[0].id = 3003; 76 | schemas[0].metadata = new bytes(0); 77 | 78 | return ("TestPostExecution", schemas); 79 | } 80 | 81 | function supportsInterface( 82 | bytes4 interfaceId 83 | ) public view override(ERC165, ZoneInterface) returns (bool) { 84 | return 85 | interfaceId == type(ZoneInterface).interfaceId || 86 | super.supportsInterface(interfaceId); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/test/TestZone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 5 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 6 | import { 7 | Schema, 8 | ZoneParameters 9 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 10 | 11 | contract TestZone is ERC165, ZoneInterface { 12 | function authorizeOrder( 13 | ZoneParameters calldata 14 | ) public pure returns (bytes4) { 15 | return this.authorizeOrder.selector; 16 | } 17 | 18 | function validateOrder( 19 | ZoneParameters calldata zoneParameters 20 | ) external pure override returns (bytes4 validOrderMagicValue) { 21 | if (zoneParameters.extraData.length == 0) { 22 | if (zoneParameters.zoneHash == bytes32(uint256(1))) { 23 | revert("Revert on zone hash 1"); 24 | } else if (zoneParameters.zoneHash == bytes32(uint256(2))) { 25 | assembly { 26 | revert(0, 0) 27 | } 28 | } 29 | } else if (zoneParameters.extraData.length == 4) { 30 | revert("Revert on extraData length 4"); 31 | } else if (zoneParameters.extraData.length == 5) { 32 | assembly { 33 | revert(0, 0) 34 | } 35 | } else if ( 36 | zoneParameters.extraData.length > 32 && 37 | zoneParameters.extraData.length % 32 == 0 38 | ) { 39 | bytes32[] memory expectedOrderHashes = abi.decode( 40 | zoneParameters.extraData, 41 | (bytes32[]) 42 | ); 43 | 44 | uint256 expectedLength = expectedOrderHashes.length; 45 | 46 | if (expectedLength != zoneParameters.orderHashes.length) { 47 | revert("Revert on unexpected order hashes length"); 48 | } 49 | 50 | for (uint256 i = 0; i < expectedLength; ++i) { 51 | if (expectedOrderHashes[i] != zoneParameters.orderHashes[i]) { 52 | revert("Revert on unexpected order hash"); 53 | } 54 | } 55 | } 56 | 57 | validOrderMagicValue = zoneParameters.zoneHash != bytes32(uint256(3)) 58 | ? ZoneInterface.validateOrder.selector 59 | : bytes4(0xffffffff); 60 | } 61 | 62 | /** 63 | * @dev Returns the metadata for this zone. 64 | */ 65 | function getSeaportMetadata() 66 | external 67 | pure 68 | override 69 | returns ( 70 | string memory name, 71 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 72 | ) 73 | { 74 | schemas = new Schema[](1); 75 | schemas[0].id = 3003; 76 | schemas[0].metadata = new bytes(0); 77 | 78 | return ("TestZone", schemas); 79 | } 80 | 81 | function supportsInterface( 82 | bytes4 interfaceId 83 | ) public view override(ERC165, ZoneInterface) returns (bool) { 84 | return 85 | interfaceId == type(ZoneInterface).interfaceId || 86 | super.supportsInterface(interfaceId); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /diagrams/README.md: -------------------------------------------------------------------------------- 1 | # Diagrams 2 | 3 | ## Seaport 4 | 5 | ![Seaport diagram](./Seaport.drawio.svg) 6 | 7 | ## Dev 8 | 9 | To modify diagrams, open in [diagrams.net](https://diagrams.net) (formerly draw.io) and save both file and svg. Export svg with settings `Zoom: 100%` and `Border Width: 5`. -------------------------------------------------------------------------------- /docs/AuditLink.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Audit 3 | category: 6520398b749af50013f52ff4 4 | slug: seaport-audit 5 | parentDocSlug: seaport-overview 6 | order: 6 7 | hidden: false 8 | type: link 9 | link_url: https://github.com/ProjectOpenSea/seaport/tree/main#audits 10 | --- 11 | -------------------------------------------------------------------------------- /docs/FunctionSignatures.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Seaport Functions 3 | category: 6520398b749af50013f52ff4 4 | slug: seaport-functions 5 | parentDocSlug: seaport-overview 6 | order: 4 7 | hidden: false 8 | --- 9 | 10 | # Seaport Function Signatures 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
Function NameSignature (1.2)Signature (1.1)
fulfillOrder0xb3a34c4c0xb3a34c4c
fulfillAdvancedOrder0xe7acab240xe7acab24
matchOrders0xa81744040xa8174404
matchAdvancedOrders0xf2d12b120x55944a42
fulfillAvailableOrders0xed98a5740xed98a574
fulfillAvailableAdvancedOrders0x87201b410x87201b41
fulfillBasicOrder0xfb0f3ee10xfb0f3ee1
fulfillBasicOrder_efficient_6GL6yc0x00000000n/a
cancel0xfd9f1e100xfd9f1e10
validate0x881477320x88147732
incrementCounter0x5b34b9660x5b34b966
-------------------------------------------------------------------------------- /docs/Overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Seaport 3 | category: 6520398b749af50013f52ff4 4 | slug: seaport-overview 5 | order: 0 6 | hidden: false 7 | --- 8 | 9 | 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"). -------------------------------------------------------------------------------- /docs/SeaportDiagramLink.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Seaport Diagram 3 | category: 6520398b749af50013f52ff4 4 | slug: seaport-diagram 5 | parentDocSlug: seaport-overview 6 | order: 5 7 | hidden: false 8 | type: link 9 | link_url: https://github.com/ProjectOpenSea/seaport/blob/main/diagrams/README.md 10 | --- -------------------------------------------------------------------------------- /docs/ZoneDocumentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Seaport Zones 3 | category: 6520398b749af50013f52ff4 4 | slug: seaport-zones 5 | parentDocSlug: seaport-overview 6 | order: 2 7 | hidden: false 8 | --- 9 | 10 | # Zone Documentation 11 | 12 | The `zone` of the order is an optional secondary account attached to the order with two additional privileges: 13 | 14 | 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`). 15 | 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. 16 | 17 | An example zone contract implementation can be found at `/contracts/zones/PausableZone.sol`. 18 | 19 | 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. 20 | 21 | ## Ideas 22 | 23 | New zones can be permissionlessly deployed and utilized to extend the feature set of the core Seaport marketplace. Examples include: 24 | 25 | - Helping to prevent sales of compromised items 26 | - Pausing orders in case of an emergency without invalidating approvals 27 | - Limiting the number of NFTs from a particular collection that can be sold in a given amount of time 28 | - Enforcing a particular floor or ceiling price for certain items 29 | - Making arbitrary calls to outside data sources 30 | - Tracking additional incentives for completing valid orders 31 | -------------------------------------------------------------------------------- /docs/prepare-docs.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const glob = require("glob"); 3 | 4 | /** 5 | * This script is used to prepare the solidity files for use with `forge doc` 6 | * Seaport handles decoding structs from calldata itself, but solc will use its 7 | * default decoding if given names. Thus, to comply with natspec, use @custom 8 | * tags to specify "unnamed" params and their names. This is not compatible with 9 | * forge doc, so they are turned back into normal @param tags before generating 10 | * documentation. 11 | */ 12 | 13 | glob("contracts/**/*.sol", {}, (er, files) => { 14 | files.forEach((file) => { 15 | let content = fs.readFileSync(file, "utf-8"); 16 | 17 | // Restore normal @param tags. 18 | content = content.replace( 19 | /@custom:param ([a-zA-z0-9]+)/g, 20 | "@param $1 " 21 | ); 22 | 23 | // Replace @custom:name tags with name of the param in the correct location. 24 | content = content.replace( 25 | /\/\*\*\s*\*?\s*@custom:name\s*([a-zA-Z0-9]*)\s*\*\/\s*([a-zA-z[\]]+ calldata)\s*(,|\))/g, 26 | "$2 $1$3" 27 | ); 28 | 29 | // Once files have been overwritten, call forge doc to generate 30 | // documentation complete with descriptions for "unnamed" variables. 31 | fs.writeFileSync(file, content, "utf-8"); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /eip-712-types/bulkOrder.js: -------------------------------------------------------------------------------- 1 | const bulkOrderType = { 2 | BulkOrder: [{ name: "tree", type: "OrderComponents[2][2][2][2][2][2][2]" }], 3 | OrderComponents: [ 4 | { name: "offerer", type: "address" }, 5 | { name: "zone", type: "address" }, 6 | { name: "offer", type: "OfferItem[]" }, 7 | { name: "consideration", type: "ConsiderationItem[]" }, 8 | { name: "orderType", type: "uint8" }, 9 | { name: "startTime", type: "uint256" }, 10 | { name: "endTime", type: "uint256" }, 11 | { name: "zoneHash", type: "bytes32" }, 12 | { name: "salt", type: "uint256" }, 13 | { name: "conduitKey", type: "bytes32" }, 14 | { name: "counter", type: "uint256" }, 15 | ], 16 | OfferItem: [ 17 | { name: "itemType", type: "uint8" }, 18 | { name: "token", type: "address" }, 19 | { name: "identifierOrCriteria", type: "uint256" }, 20 | { name: "startAmount", type: "uint256" }, 21 | { name: "endAmount", type: "uint256" }, 22 | ], 23 | ConsiderationItem: [ 24 | { name: "itemType", type: "uint8" }, 25 | { name: "token", type: "address" }, 26 | { name: "identifierOrCriteria", type: "uint256" }, 27 | { name: "startAmount", type: "uint256" }, 28 | { name: "endAmount", type: "uint256" }, 29 | { name: "recipient", type: "address" }, 30 | ], 31 | }; 32 | 33 | module.exports = Object.freeze({ 34 | bulkOrderType, 35 | }); 36 | -------------------------------------------------------------------------------- /eip-712-types/domain.js: -------------------------------------------------------------------------------- 1 | const eip712DomainType = { 2 | EIP712Domain: [ 3 | { name: "name", type: "string" }, 4 | { name: "version", type: "string" }, 5 | { name: "chainId", type: "uint256" }, 6 | { name: "verifyingContract", type: "address" }, 7 | ], 8 | }; 9 | 10 | module.exports = Object.freeze({ 11 | eip712DomainType, 12 | }); 13 | -------------------------------------------------------------------------------- /eip-712-types/order.js: -------------------------------------------------------------------------------- 1 | const orderType = { 2 | OrderComponents: [ 3 | { name: "offerer", type: "address" }, 4 | { name: "zone", type: "address" }, 5 | { name: "offer", type: "OfferItem[]" }, 6 | { name: "consideration", type: "ConsiderationItem[]" }, 7 | { name: "orderType", type: "uint8" }, 8 | { name: "startTime", type: "uint256" }, 9 | { name: "endTime", type: "uint256" }, 10 | { name: "zoneHash", type: "bytes32" }, 11 | { name: "salt", type: "uint256" }, 12 | { name: "conduitKey", type: "bytes32" }, 13 | { name: "counter", type: "uint256" }, 14 | ], 15 | OfferItem: [ 16 | { name: "itemType", type: "uint8" }, 17 | { name: "token", type: "address" }, 18 | { name: "identifierOrCriteria", type: "uint256" }, 19 | { name: "startAmount", type: "uint256" }, 20 | { name: "endAmount", type: "uint256" }, 21 | ], 22 | ConsiderationItem: [ 23 | { name: "itemType", type: "uint8" }, 24 | { name: "token", type: "address" }, 25 | { name: "identifierOrCriteria", type: "uint256" }, 26 | { name: "startAmount", type: "uint256" }, 27 | { name: "endAmount", type: "uint256" }, 28 | { name: "recipient", type: "address" }, 29 | ], 30 | }; 31 | 32 | module.exports = Object.freeze({ 33 | orderType, 34 | }); 35 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc = '0.8.24' 3 | evm_version='cancun' 4 | src = 'contracts' 5 | out = 'out' 6 | libs = ["node_modules", "lib"] 7 | test = 'test/foundry' 8 | remappings = [ 9 | '@rari-capital/solmate/=lib/solmate/', 10 | 'ds-test/=lib/ds-test/src/', 11 | 'forge-std/=lib/forge-std/src/', 12 | 'murky/=lib/murky/src/', 13 | '@openzeppelin/=lib/openzeppelin-contracts/', 14 | 'solarray/=lib/solarray/src/', 15 | 'solady/=lib/solady/', 16 | 'seaport-sol/=lib/seaport-sol/', 17 | 'seaport-types/=lib/seaport-types/', 18 | 'seaport-core/=lib/seaport-core/', 19 | 'seaport/=contracts/' 20 | ] 21 | optimizer_runs = 4_294_967_295 22 | fs_permissions = [ 23 | { access = "read", path = "./optimized-out" }, 24 | { access = "read", path = "./reference-out" }, 25 | { access = "write", path = "./call-metrics.txt" }, 26 | { access = "write", path = "./mutation-metrics.txt" }, 27 | { access = "write", path = "./assume-metrics.txt" }, 28 | { access = "write", path = "./fuzz_debug.json" } 29 | ] 30 | 31 | [profile.validator] 32 | solc = '0.8.17' 33 | src = 'contracts/helpers/order-validator' 34 | optimizer_runs = 1 35 | 36 | [fuzz] 37 | runs = 1_000 38 | max_test_rejects = 1_000_000 39 | 40 | [profile.reference] 41 | solc = '0.8.24' 42 | src = 'reference' 43 | via_ir = false 44 | out = 'reference-out' 45 | script = 'reference' 46 | test = 'test/foundry' 47 | cache_path = 'reference-cache' 48 | 49 | 50 | [profile.optimized] 51 | via_ir = true 52 | out = 'optimized-out' 53 | script = 'contracts' 54 | bytecode_hash = 'none' 55 | # no need to compile tests with via-ir since they load optimized bytecode directly by default 56 | test ='contracts' 57 | 58 | [profile.test] 59 | src = 'test/foundry' 60 | 61 | [profile.test.fuzz] 62 | runs = 1_000 63 | 64 | [profile.lite] 65 | out = 'optimized-out' 66 | 67 | [profile.debug] 68 | src = 'contracts' 69 | optimizer = false 70 | 71 | [profile.moat_debug] 72 | optimizer = false 73 | test = 'test/foundry/new' 74 | 75 | [profile.offerers] 76 | test='test/foundry/offerers' 77 | 78 | [fmt] 79 | line_length = 80 80 | tab_width = 4 81 | bracket_spacing = true 82 | 83 | # See more config options https://github.com/gakonst/foundry/tree/master/config 84 | -------------------------------------------------------------------------------- /hardhat-coverage.config.ts: -------------------------------------------------------------------------------- 1 | import type { HardhatUserConfig } from "hardhat/config"; 2 | 3 | import "dotenv/config"; 4 | import "@nomicfoundation/hardhat-chai-matchers"; 5 | import "@typechain/hardhat"; 6 | import "hardhat-gas-reporter"; 7 | import "solidity-coverage"; 8 | 9 | // You need to export an object to set up your config 10 | // Go to https://hardhat.org/config/ to learn more 11 | 12 | const config: HardhatUserConfig = { 13 | solidity: { 14 | compilers: [ 15 | { 16 | version: "0.8.24", 17 | settings: { 18 | evmVersion: "cancun", 19 | viaIR: false, 20 | optimizer: { 21 | enabled: false, 22 | }, 23 | }, 24 | }, 25 | ], 26 | }, 27 | networks: { 28 | hardhat: { 29 | blockGasLimit: 300_000_000, 30 | throwOnCallFailures: false, 31 | allowUnlimitedContractSize: true, 32 | }, 33 | }, 34 | gasReporter: { 35 | enabled: process.env.REPORT_GAS !== undefined, 36 | currency: "USD", 37 | }, 38 | }; 39 | 40 | export default config; 41 | -------------------------------------------------------------------------------- /hardhat-reference-coverage.config.ts: -------------------------------------------------------------------------------- 1 | import type { HardhatUserConfig } from "hardhat/config"; 2 | 3 | import "dotenv/config"; 4 | import "@nomicfoundation/hardhat-chai-matchers"; 5 | import "@typechain/hardhat"; 6 | import "hardhat-gas-reporter"; 7 | import "solidity-coverage"; 8 | 9 | // You need to export an object to set up your config 10 | // Go to https://hardhat.org/config/ to learn more 11 | 12 | const config: HardhatUserConfig = { 13 | solidity: { 14 | compilers: [ 15 | { 16 | version: "0.8.13", 17 | settings: { 18 | viaIR: false, 19 | optimizer: { 20 | enabled: false, 21 | }, 22 | }, 23 | }, 24 | ], 25 | }, 26 | networks: { 27 | hardhat: { 28 | blockGasLimit: 30_000_000, 29 | allowUnlimitedContractSize: true, 30 | throwOnCallFailures: false, 31 | }, 32 | }, 33 | gasReporter: { 34 | enabled: process.env.REPORT_GAS !== undefined, 35 | currency: "USD", 36 | }, 37 | paths: { 38 | sources: "./reference", 39 | cache: "hh-cache-ref", 40 | artifacts: "./artifacts-ref", 41 | }, 42 | }; 43 | 44 | export default config; 45 | -------------------------------------------------------------------------------- /hardhat-reference.config.ts: -------------------------------------------------------------------------------- 1 | import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; 2 | import { subtask } from "hardhat/config"; 3 | 4 | import type { HardhatUserConfig } from "hardhat/config"; 5 | 6 | import "dotenv/config"; 7 | import "@nomicfoundation/hardhat-chai-matchers"; 8 | import "@typechain/hardhat"; 9 | import "hardhat-gas-reporter"; 10 | import "solidity-coverage"; 11 | 12 | // Filter Reference Contracts 13 | subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( 14 | async (_, __, runSuper) => { 15 | const paths = await runSuper(); 16 | 17 | return paths.filter( 18 | (p: any) => 19 | !p.endsWith("/Consideration.sol") && !p.includes("contracts/lib/") 20 | ); 21 | } 22 | ); 23 | 24 | // You need to export an object to set up your config 25 | // Go to https://hardhat.org/config/ to learn more 26 | 27 | const config: HardhatUserConfig = { 28 | solidity: { 29 | compilers: [ 30 | { 31 | version: "0.8.13", 32 | settings: { 33 | viaIR: false, 34 | optimizer: { 35 | enabled: false, 36 | }, 37 | }, 38 | }, 39 | ], 40 | }, 41 | networks: { 42 | hardhat: { 43 | blockGasLimit: 30_000_000, 44 | allowUnlimitedContractSize: true, 45 | throwOnCallFailures: false, 46 | }, 47 | }, 48 | gasReporter: { 49 | enabled: process.env.REPORT_GAS !== undefined, 50 | currency: "USD", 51 | }, 52 | // specify separate cache for hardhat, since it could possibly conflict with foundry's 53 | paths: { 54 | sources: "./reference", 55 | cache: "hh-cache-ref", 56 | artifacts: "./artifacts-ref", 57 | }, 58 | }; 59 | 60 | export default config; 61 | -------------------------------------------------------------------------------- /hardhat-validator.config.ts: -------------------------------------------------------------------------------- 1 | import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; 2 | import { subtask } from "hardhat/config"; 3 | 4 | import type { HardhatUserConfig } from "hardhat/config"; 5 | 6 | import "dotenv/config"; 7 | import "@nomiclabs/hardhat-ethers"; 8 | import "@nomicfoundation/hardhat-chai-matchers"; 9 | import "@nomiclabs/hardhat-etherscan"; 10 | import "@typechain/hardhat"; 11 | import "hardhat-gas-reporter"; 12 | 13 | subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( 14 | async (_, __, runSuper) => { 15 | const paths = await runSuper(); 16 | 17 | return paths.filter((p: any) => !p.includes("contracts/")); 18 | } 19 | ); 20 | 21 | // You need to export an object to set up your config 22 | // Go to https://hardhat.org/config/ to learn more 23 | 24 | const config: HardhatUserConfig = { 25 | solidity: { 26 | compilers: [ 27 | { 28 | version: "0.8.17", 29 | settings: { 30 | viaIR: false, 31 | optimizer: { 32 | enabled: true, 33 | runs: 1, 34 | }, 35 | }, 36 | }, 37 | ], 38 | }, 39 | networks: { 40 | hardhat: { 41 | blockGasLimit: 30_000_000, 42 | throwOnCallFailures: false, 43 | allowUnlimitedContractSize: true, 44 | forking: { 45 | enabled: true, 46 | url: process.env.ETH_RPC_URL ?? "", 47 | }, 48 | }, 49 | }, 50 | // specify separate cache for hardhat, since it could possibly conflict with foundry's 51 | paths: { 52 | sources: "./contracts", 53 | tests: "./test/order-validator", 54 | cache: "hh-cache", 55 | }, 56 | }; 57 | 58 | export default config; 59 | -------------------------------------------------------------------------------- /img/Seaport-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectOpenSea/seaport/687dfd72b80a0fbf30fc61008388bb7f508b6d70/img/Seaport-banner.png -------------------------------------------------------------------------------- /reference/lib/ReferenceCounterManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConsiderationEventsAndErrors 6 | } from "seaport-types/src/interfaces/ConsiderationEventsAndErrors.sol"; 7 | 8 | import { ReferenceReentrancyGuard } from "./ReferenceReentrancyGuard.sol"; 9 | 10 | /** 11 | * @title CounterManager 12 | * @author 0age 13 | * @notice CounterManager contains a storage mapping and related functionality 14 | * for retrieving and incrementing a per-offerer counter. 15 | */ 16 | contract ReferenceCounterManager is 17 | ConsiderationEventsAndErrors, 18 | ReferenceReentrancyGuard 19 | { 20 | // Only orders signed using an offerer's current counter are fulfillable. 21 | mapping(address => uint256) private _counters; 22 | 23 | /** 24 | * @dev Internal function to cancel all orders from a given offerer in bulk 25 | * by incrementing a counter. Note that only the offerer may increment 26 | * the counter. Note that the counter is incremented by a large, 27 | * quasi-random interval, which makes it infeasible to "activate" 28 | * signed orders by incrementing the counter. This activation 29 | * functionality can be achieved instead with restricted orders or 30 | * contract orders. 31 | * 32 | * @return newCounter The new counter. 33 | */ 34 | function _incrementCounter() internal returns (uint256 newCounter) { 35 | // Use second half of the previous block hash as a quasi-random number. 36 | uint256 quasiRandomNumber = uint256(blockhash(block.number - 1)) >> 128; 37 | 38 | // Retrieve the original counter value. 39 | uint256 originalCounter = _counters[msg.sender]; 40 | 41 | // Increment current counter for the supplied offerer. 42 | newCounter = quasiRandomNumber + originalCounter; 43 | 44 | // Update the counter with the new value. 45 | _counters[msg.sender] = newCounter; 46 | 47 | // Emit an event containing the new counter. 48 | emit CounterIncremented(newCounter, msg.sender); 49 | } 50 | 51 | /** 52 | * @dev Internal view function to retrieve the current counter for a given 53 | * offerer. 54 | * 55 | * @param offerer The offerer in question. 56 | * 57 | * @return currentCounter The current counter. 58 | */ 59 | function _getCounter( 60 | address offerer 61 | ) internal view returns (uint256 currentCounter) { 62 | // Return the counter for the supplied offerer. 63 | currentCounter = _counters[offerer]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /reference/lib/ReferenceGenerateOrderReturndataDecoder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ReceivedItem, 6 | SpentItem 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | contract ReferenceGenerateOrderReturndataDecoder { 10 | function decode( 11 | bytes calldata returnedBytes 12 | ) external pure returns (SpentItem[] memory, ReceivedItem[] memory) { 13 | return abi.decode(returnedBytes, (SpentItem[], ReceivedItem[])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /reference/lib/ReferenceReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | ConsiderationEventsAndErrors 6 | } from "seaport-types/src/interfaces/ConsiderationEventsAndErrors.sol"; 7 | 8 | import { 9 | ReentrancyErrors 10 | } from "seaport-types/src/interfaces/ReentrancyErrors.sol"; 11 | 12 | import { 13 | _ENTERED_AND_ACCEPTING_NATIVE_TOKENS_SSTORE, 14 | _ENTERED_SSTORE, 15 | _NOT_ENTERED_SSTORE 16 | } from "seaport-types/src/lib/ConsiderationConstants.sol"; 17 | 18 | /** 19 | * @title ReentrancyGuard 20 | * @author 0age 21 | * @notice ReentrancyGuard contains a storage variable and related functionality 22 | * for protecting against reentrancy. 23 | */ 24 | contract ReferenceReentrancyGuard is 25 | ConsiderationEventsAndErrors, 26 | ReentrancyErrors 27 | { 28 | // Prevent reentrant calls on protected functions. 29 | uint256 private _reentrancyGuard; 30 | 31 | /** 32 | * @dev Initialize the reentrancy guard during deployment. 33 | */ 34 | constructor() { 35 | // Initialize the reentrancy guard in a cleared state. 36 | _reentrancyGuard = _NOT_ENTERED_SSTORE; 37 | } 38 | 39 | /** 40 | * @dev Modifier to check that the sentinel value for the reentrancy guard 41 | * is not currently set by a previous call. 42 | */ 43 | modifier notEntered() { 44 | if (_reentrancyGuard != _NOT_ENTERED_SSTORE) { 45 | revert NoReentrantCalls(); 46 | } 47 | 48 | _; 49 | } 50 | 51 | /** 52 | * @dev Modifier to set the reentrancy guard sentinel value for the duration 53 | * of the call and check if it is already set by a previous call. 54 | * 55 | * @param acceptNativeTokens A boolean indicating whether native tokens may 56 | * be received during execution or not. 57 | */ 58 | modifier nonReentrant(bool acceptNativeTokens) { 59 | if (_reentrancyGuard != _NOT_ENTERED_SSTORE) { 60 | revert NoReentrantCalls(); 61 | } 62 | 63 | if (acceptNativeTokens) { 64 | _reentrancyGuard = _ENTERED_AND_ACCEPTING_NATIVE_TOKENS_SSTORE; 65 | } else { 66 | _reentrancyGuard = _ENTERED_SSTORE; 67 | } 68 | 69 | _; 70 | 71 | _reentrancyGuard = _NOT_ENTERED_SSTORE; 72 | } 73 | 74 | /** 75 | * @dev Internal view function to ensure that the sentinel value indicating 76 | * native tokens may be received during execution is currently set. 77 | */ 78 | function _assertAcceptingNativeTokens() internal view { 79 | // Ensure that the reentrancy guard is not currently set. 80 | if (_reentrancyGuard != _ENTERED_AND_ACCEPTING_NATIVE_TOKENS_SSTORE) { 81 | revert InvalidMsgValue(msg.value); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /reference/shim/Shim.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | /** 5 | * @dev HardHat doesn't support multiple source folders; so import everything 6 | * extra that reference tests rely on so they get compiled. Allows for faster 7 | * feedback than running an extra yarn build 8 | */ 9 | import { EIP1271Wallet } from "../../contracts/test/EIP1271Wallet.sol"; 10 | import { Reenterer } from "../../contracts/test/Reenterer.sol"; 11 | import { TestERC20 } from "../../contracts/test/TestERC20.sol"; 12 | import { TestERC721 } from "../../contracts/test/TestERC721.sol"; 13 | import { TestERC1155 } from "../../contracts/test/TestERC1155.sol"; 14 | import { TestZone } from "../../contracts/test/TestZone.sol"; 15 | import { TestPostExecution } from "../../contracts/test/TestPostExecution.sol"; 16 | import { 17 | TestContractOfferer 18 | } from "../../contracts/test/TestContractOfferer.sol"; 19 | import { 20 | TestContractOffererNativeToken 21 | } from "../../contracts/test/TestContractOffererNativeToken.sol"; 22 | import { 23 | TestBadContractOfferer 24 | } from "../../contracts/test/TestBadContractOfferer.sol"; 25 | import { 26 | TestInvalidContractOfferer 27 | } from "../../contracts/test/TestInvalidContractOfferer.sol"; 28 | import { 29 | TestInvalidContractOffererRatifyOrder 30 | } from "../../contracts/test/TestInvalidContractOffererRatifyOrder.sol"; 31 | import { 32 | PausableZoneController 33 | } from "../../contracts/zones/PausableZoneController.sol"; 34 | import { TransferHelper } from "../../contracts/helpers/TransferHelper.sol"; 35 | import { 36 | InvalidERC721Recipient 37 | } from "../../contracts/test/InvalidERC721Recipient.sol"; 38 | import { 39 | ERC721ReceiverMock 40 | } from "../../contracts/test/ERC721ReceiverMock.sol"; 41 | import { TestERC20Panic } from "../../contracts/test/TestERC20Panic.sol"; 42 | import { 43 | ConduitControllerMock 44 | } from "../../contracts/test/ConduitControllerMock.sol"; 45 | import { ConduitMock } from "../../contracts/test/ConduitMock.sol"; 46 | import { 47 | ImmutableCreate2FactoryInterface 48 | } from "seaport-types/src/interfaces/ImmutableCreate2FactoryInterface.sol"; 49 | 50 | import { 51 | TestTransferValidationZoneOfferer 52 | } from "../../contracts/test/TestTransferValidationZoneOfferer.sol"; 53 | -------------------------------------------------------------------------------- /script/CallNavigator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.4; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import { 7 | ConsiderationInterface, 8 | NavigatorRequest, 9 | SeaportNavigatorInterface, 10 | SeaportValidatorInterface 11 | } from "../contracts/helpers/navigator/SeaportNavigator.sol"; 12 | 13 | import { 14 | NavigatorAdvancedOrder, 15 | NavigatorConsiderationItem, 16 | NavigatorOfferItem, 17 | NavigatorOrderParameters 18 | } from "../contracts/helpers/navigator/lib/SeaportNavigatorTypes.sol"; 19 | 20 | import { 21 | AggregationStrategy, 22 | FulfillAvailableStrategy, 23 | FulfillmentStrategy, 24 | MatchStrategy 25 | } from "seaport-sol/src/fulfillments/lib/FulfillmentLib.sol"; 26 | 27 | import { OrderType } from "seaport-types/src/lib/ConsiderationEnums.sol"; 28 | 29 | contract CallNavigator is Script { 30 | address private constant GOERLI_NAVIGATOR = 31 | 0x76093Af4C8330D69676d920d550d9901110792D5; 32 | 33 | function run() public view { 34 | // Create an empty request. 35 | NavigatorRequest memory request; 36 | 37 | // Set Seaport and SeaportValidator addresses. 38 | request.seaport = ConsiderationInterface( 39 | 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC 40 | ); 41 | request.validator = SeaportValidatorInterface( 42 | 0xBa7a3AD8aDD5D37a89a73d76e9Fb4270aeD264Ad 43 | ); 44 | 45 | // Set up orders, using navigator order structs. 46 | NavigatorAdvancedOrder[] memory orders = new NavigatorAdvancedOrder[]( 47 | 1 48 | ); 49 | orders[0] = NavigatorAdvancedOrder({ 50 | parameters: NavigatorOrderParameters({ 51 | offerer: 0xcc476d5Adc341B31405891E78694186454775926, 52 | zone: address(0), 53 | offer: new NavigatorOfferItem[](0), 54 | consideration: new NavigatorConsiderationItem[](0), 55 | orderType: OrderType.FULL_OPEN, 56 | startTime: 1686684156, 57 | endTime: 1686687756, 58 | zoneHash: bytes32(0), 59 | salt: uint256( 60 | 0x10a16a76000000000000000000000000000000000000000000000000af0f8c13 61 | ), 62 | conduitKey: bytes32(0), 63 | totalOriginalConsiderationItems: 0 64 | }), 65 | numerator: 1, 66 | denominator: 1, 67 | signature: ( 68 | hex"3c792711cff5e3b9ca789b3fc08f345d069ca3f161d0a3b3e2700ad95c" 69 | hex"691c6c8079a4c12149a9834797d50a4c0856cc11430bdd28bcf02b0798" 70 | hex"7aefb6d21ab2" 71 | ), 72 | extraData: "" 73 | }); 74 | request.orders = orders; 75 | 76 | // Set up call context data 77 | request.caller = 0xcc476d5Adc341B31405891E78694186454775926; 78 | request.recipient = 0xcc476d5Adc341B31405891E78694186454775926; 79 | request.maximumFulfilled = 1; 80 | 81 | // Set fulfillment parameters 82 | request.fulfillmentStrategy = FulfillmentStrategy( 83 | AggregationStrategy.MAXIMUM, 84 | FulfillAvailableStrategy.KEEP_ALL, 85 | MatchStrategy.MAX_INCLUSION 86 | ); 87 | request.preferMatch = true; 88 | 89 | // Call the navigator with the configured request. 90 | SeaportNavigatorInterface(GOERLI_NAVIGATOR).prepare(request); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /script/SeaportDeployer.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.4; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import { Seaport } from "seaport-core/src/Seaport.sol"; 7 | 8 | interface ImmutableCreate2Factory { 9 | function safeCreate2( 10 | bytes32 salt, 11 | bytes calldata initializationCode 12 | ) external payable returns (address deploymentAddress); 13 | } 14 | 15 | // NOTE: This script assumes that the CREATE2-related contracts have already been deployed. 16 | contract SeaportDeployer is Script { 17 | ImmutableCreate2Factory private constant IMMUTABLE_CREATE2_FACTORY = 18 | ImmutableCreate2Factory(0x0000000000FFe8B47B3e2130213B802212439497); 19 | address private constant CONDUIT_CONTROLLER = 20 | 0x00000000F9490004C11Cef243f5400493c00Ad63; 21 | address private constant SEAPORT_ADDRESS = 22 | 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC; 23 | 24 | function run() public { 25 | // Utilizes the locally-defined PRIVATE_KEY environment variable to sign txs. 26 | vm.startBroadcast(vm.envUint("PRIVATE_KEY")); 27 | 28 | // CREATE2 salt (20-byte caller or zero address + 12-byte salt). 29 | bytes32 salt = 0x0000000000000000000000000000000000000000d4b6fcc21169b803f25d2210; 30 | 31 | // Packed and ABI-encoded contract bytecode and constructor arguments. 32 | // NOTE: The Seaport contract *must* be compiled using the optimized profile config. 33 | bytes memory initCode = abi.encodePacked( 34 | type(Seaport).creationCode, 35 | abi.encode(CONDUIT_CONTROLLER) 36 | ); 37 | 38 | // Deploy the Seaport contract via ImmutableCreate2Factory. 39 | address seaport = IMMUTABLE_CREATE2_FACTORY.safeCreate2(salt, initCode); 40 | 41 | // Verify that the deployed contract address matches what we're expecting. 42 | assert(seaport == SEAPORT_ADDRESS); 43 | 44 | vm.stopBroadcast(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /script/TransferHelperDeployer.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.4; 3 | 4 | import "forge-std/Script.sol"; 5 | import { TransferHelper } from "../contracts/helpers/TransferHelper.sol"; 6 | 7 | interface ImmutableCreate2Factory { 8 | function safeCreate2(bytes32, bytes memory) external; 9 | } 10 | 11 | contract TransferHelperDeployer is Script { 12 | function setUp() public {} 13 | 14 | function run() public { 15 | vm.broadcast(); 16 | ImmutableCreate2Factory factory = ImmutableCreate2Factory( 17 | 0x0000000000FFe8B47B3e2130213B802212439497 18 | ); 19 | bytes32 salt = bytes32(0); 20 | factory.safeCreate2( 21 | salt, 22 | abi.encodePacked( 23 | type(TransferHelper).creationCode, 24 | abi.encode(address(0x00000000F9490004C11Cef243f5400493c00Ad63)) 25 | ) 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scripts/comment-table.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | export const err = chalk.bold.red; 4 | export const warn = chalk.hex("#FFA500"); 5 | export const info = chalk.blue; 6 | export const success = chalk.green; 7 | 8 | export function diffPctString( 9 | newValue: number, 10 | oldValue: number, 11 | warnOnIncrease?: boolean, 12 | diffOnly?: boolean 13 | ): string { 14 | if (newValue === null && oldValue === null) { 15 | return warn("null"); 16 | } 17 | const diff = newValue - oldValue; 18 | 19 | if (diff === 0) return info(newValue.toString()); 20 | const pct = +((100 * diff) / oldValue).toFixed(2); 21 | const pctPrefix = pct > 0 ? "+" : ""; 22 | const color = diff > 0 ? (warnOnIncrease ? warn : err) : success; 23 | const valuePrefix = diffOnly && diff > 0 ? "+" : ""; 24 | const value = diffOnly ? diff : newValue; 25 | return `${valuePrefix}${value} (${color(`${pctPrefix}${pct}%`)})`; 26 | } 27 | // eslint-disable-next-line no-control-regex 28 | const stripANSI = (str: string) => str.replace(/\u001b\[.*?m/g, ""); 29 | 30 | export function getColumnSizesAndAlignments( 31 | rows: string[][], 32 | padding = 0 33 | ): Array<[number, boolean]> { 34 | const sizesAndAlignments: Array<[number, boolean]> = []; 35 | const numColumns = rows[0].length; 36 | for (let i = 0; i < numColumns; i++) { 37 | const entries = rows.map((row) => stripANSI(row[i])); 38 | const maxSize = Math.max(...entries.map((e) => e.length)); 39 | const alignLeft = entries 40 | .slice(1) 41 | .filter((e) => !e.includes("null")) 42 | .some((e) => !!e.match(/[a-zA-Z]/g)); 43 | sizesAndAlignments.push([maxSize + padding, alignLeft]); 44 | } 45 | return sizesAndAlignments; 46 | } 47 | 48 | const padColumn = ( 49 | col: string, 50 | size: number, 51 | padWith: string, 52 | alignLeft: boolean 53 | ) => { 54 | const padSize = Math.max(0, size - stripANSI(col).length); 55 | const padding = padWith.repeat(padSize); 56 | if (alignLeft) return `${col}${padding}`; 57 | return `${padding}${col}`; 58 | }; 59 | 60 | export const toCommentTable = (rows: string[][]): string[] => { 61 | const sizesAndAlignments = getColumnSizesAndAlignments(rows); 62 | rows.forEach((row) => { 63 | row.forEach((col, c) => { 64 | const [size, alignLeft] = sizesAndAlignments[c]; 65 | row[c] = padColumn(col, size, " ", alignLeft); 66 | }); 67 | }); 68 | 69 | const completeRows = rows.map((row) => `| ${row.join(" | ")} |`); 70 | const rowSeparator = `==${sizesAndAlignments 71 | .map(([size]) => "=".repeat(size)) 72 | .join("===")}==`; 73 | completeRows.splice(1, 0, rowSeparator); 74 | completeRows.unshift(rowSeparator); 75 | completeRows.push(rowSeparator); 76 | return completeRows; 77 | }; 78 | -------------------------------------------------------------------------------- /scripts/compare_reports.ts: -------------------------------------------------------------------------------- 1 | import { diffPctString, toCommentTable } from "./comment-table"; 2 | import { printLastReport } from "./print_report"; 3 | import { getAllReports } from "./utils"; 4 | import { writeReports } from "./write_reports"; 5 | 6 | import type { ContractReport } from "./utils"; 7 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 8 | 9 | export function compareReports( 10 | oldReport: ContractReport, 11 | newReport: ContractReport 12 | ) { 13 | const rows: string[][] = []; 14 | rows.push([`method`, `min`, `max`, `avg`, `calls`]); 15 | oldReport.methods.forEach((r1, i) => { 16 | const r2 = newReport.methods[i]; 17 | rows.push([ 18 | r1.method, 19 | diffPctString(r2.min, r1.min, false, true), 20 | diffPctString(r2.max, r1.max, false, true), 21 | diffPctString(r2.avg, r1.avg, false, true), 22 | diffPctString(r2.calls, r1.calls, false, true), 23 | ]); 24 | }); 25 | const { bytecodeSize: initSize1, deployedBytecodeSize: size1 } = oldReport; 26 | const { bytecodeSize: initSize2, deployedBytecodeSize: size2 } = newReport; 27 | rows.push([ 28 | `runtime size`, 29 | diffPctString(size2, size1, false, true), 30 | "", 31 | "", 32 | "", 33 | ]); 34 | rows.push([ 35 | `init code size`, 36 | diffPctString(initSize2, initSize1, false, true), 37 | "", 38 | "", 39 | "", 40 | ]); 41 | const table = toCommentTable(rows); 42 | const separator = table[0]; 43 | table.splice(table.length - 3, 0, separator); 44 | console.log(table.join("\n")); 45 | } 46 | 47 | export function compareLastTwoReports(hre: HardhatRuntimeEnvironment) { 48 | writeReports(hre); 49 | const reports = getAllReports(); 50 | if (reports.length === 1) { 51 | printLastReport(hre); 52 | return; 53 | } 54 | if (reports.length < 2) { 55 | return; 56 | } 57 | const [currentReport, previousReport] = reports; 58 | const contractName = "Seaport"; 59 | compareReports( 60 | previousReport.contractReports[contractName], 61 | currentReport.contractReports[contractName] 62 | ); 63 | const ts = Math.floor(Date.now() / 1000); 64 | const currentSuffix = 65 | currentReport.name !== currentReport.commitHash 66 | ? ` @ ${currentReport.commitHash}` 67 | : ""; 68 | const previousSuffix = 69 | previousReport.name !== previousReport.commitHash 70 | ? ` @ ${previousReport.commitHash}` 71 | : ""; 72 | console.log( 73 | `Current Report: ${+((currentReport.timestamp - ts) / 60).toFixed( 74 | 2 75 | )} min ago (${currentReport.name})${currentSuffix}` 76 | ); 77 | console.log( 78 | `Previous Report: ${+((previousReport.timestamp - ts) / 60).toFixed( 79 | 2 80 | )} min ago (${previousReport.name})${previousSuffix}` 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /scripts/find_optimizer_runs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # h/t @DrakeEvansV1 & ChatGPT 3 | 4 | TARGET_SIZE=24.576 5 | MIN_RUNS=1 6 | # NOTE that at time of writing, Etherscan does not support verifying contracts 7 | # that specify more than 10,000,000 optimizer runs. 8 | # Higher numbers do not always result in different bytecode output. If a 9 | # higher number of runs is used, it may be possible verify by spoofing with a 10 | # number that results in the same bytecode output. This is not guaranteed. 11 | MAX_RUNS=$((2**32-1)) 12 | ENV_FILE=".env" 13 | FOUND_RUNS=0 14 | 15 | # Check if the optimizer is enabled 16 | OPTIMIZER_STATUS=$(forge config | grep optimizer | head -n 1) 17 | if [ "$OPTIMIZER_STATUS" != "optimizer = true" ]; then 18 | echo "Error: The optimizer is not enabled. Please enable it and try again." 19 | exit 1 20 | fi 21 | 22 | try_runs() { 23 | local RUNS=$1 24 | printf "Trying with FOUNDRY_OPTIMIZER_RUNS=%d\n" "$RUNS" 25 | RESULT=$(FOUNDRY_OPTIMIZER_RUNS=$RUNS forge build --sizes | grep Seaport | head -n 1) 26 | CONTRACT_SIZE=$(echo $RESULT | awk -F'|' '{print $3}' | awk '{print $1}') 27 | [ "$(echo "$CONTRACT_SIZE<=$TARGET_SIZE" | bc)" -eq 1 ] 28 | } 29 | 30 | if try_runs $MAX_RUNS; then 31 | FOUND_RUNS=$MAX_RUNS 32 | else 33 | while [ $MIN_RUNS -le $MAX_RUNS ]; do 34 | MID_RUNS=$(( (MIN_RUNS + MAX_RUNS) / 2 )) 35 | 36 | if try_runs $MID_RUNS; then 37 | printf "Success with FOUNDRY_OPTIMIZER_RUNS=%d and contract size %.3fKB\n" "$MID_RUNS" "$CONTRACT_SIZE" 38 | MIN_RUNS=$((MID_RUNS + 1)) 39 | FOUND_RUNS=$MID_RUNS 40 | else 41 | printf "Failure with FOUNDRY_OPTIMIZER_RUNS=%d and contract size %.3fKB\n" "$MID_RUNS" "$CONTRACT_SIZE" 42 | MAX_RUNS=$((MID_RUNS - 1)) 43 | fi 44 | done 45 | fi 46 | 47 | printf "Highest FOUNDRY_OPTIMIZER_RUNS found: %d\n" "$FOUND_RUNS" 48 | 49 | if [ -f "$ENV_FILE" ]; then 50 | if grep -q "^FOUNDRY_OPTIMIZER_RUNS=" "$ENV_FILE"; then 51 | awk -v runs="$FOUND_RUNS" '{gsub(/^FOUNDRY_OPTIMIZER_RUNS=.*/, "FOUNDRY_OPTIMIZER_RUNS="runs); print}' "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE" 52 | else 53 | echo "FOUNDRY_OPTIMIZER_RUNS=$FOUND_RUNS" >> "$ENV_FILE" 54 | fi 55 | printf "Updated %s with FOUNDRY_OPTIMIZER_RUNS=%d\n" "$ENV_FILE" "$FOUND_RUNS" 56 | else 57 | printf "Error: %s not found.\n" "$ENV_FILE" 58 | fi 59 | -------------------------------------------------------------------------------- /scripts/plot_metrics.ts: -------------------------------------------------------------------------------- 1 | import barChart from "cli-barchart"; 2 | import fs from "fs"; 3 | 4 | function plotMetrics() { 5 | const file = process.argv.length > 2 ? process.argv[2] : "call-metrics.txt"; 6 | const counter = new Map(); 7 | 8 | const metricsData = fs.readFileSync(file, "utf-8"); 9 | const lines = metricsData.split("\n"); 10 | 11 | for (const line of lines) { 12 | if (line.trim() === "") continue; 13 | const [metric] = line.split("|"); 14 | const [call] = metric.split(":"); 15 | 16 | counter.set(call, (counter.get(call) ?? 0) + 1); 17 | } 18 | 19 | const data = Array.from(counter.entries()) 20 | .map(([key, value]) => ({ 21 | key, 22 | value, 23 | })) 24 | .sort((a, b) => a.key.localeCompare(b.key)); 25 | const totalRuns = data.reduce((acc, item) => acc + item.value, 0); 26 | 27 | type Item = { key: string; value: number }; 28 | const renderLabel = (_item: Item, index: number) => { 29 | const percent = ((data[index].value / totalRuns) * 100).toFixed(2); 30 | return `${data[index].value.toString()} (${percent}%)`; 31 | }; 32 | 33 | const options = { 34 | renderLabel, 35 | }; 36 | 37 | const chart = barChart(data, options); 38 | console.log(`Fuzz test metrics (${totalRuns} runs):\n`); 39 | console.log(chart); 40 | } 41 | 42 | plotMetrics(); 43 | -------------------------------------------------------------------------------- /scripts/print_report.ts: -------------------------------------------------------------------------------- 1 | import { err, toCommentTable, warn } from "./comment-table"; 2 | import { getAllReports } from "./utils"; 3 | import { writeReports } from "./write_reports"; 4 | 5 | import type { ContractReport } from "./utils"; 6 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 7 | 8 | export function printReport(report: ContractReport) { 9 | const rows: string[][] = []; 10 | rows.push([`method`, `min`, `max`, `avg`, `calls`]); 11 | report.methods.forEach(({ method, min, max, avg, calls }) => { 12 | rows.push([ 13 | method, 14 | min?.toString() ?? warn("null"), 15 | max?.toString() ?? warn("null"), 16 | avg?.toString() ?? warn("null"), 17 | calls?.toString() ?? warn("null"), 18 | ]); 19 | }); 20 | rows.push([ 21 | `runtime size`, 22 | report.deployedBytecodeSize.toString(), 23 | "", 24 | "", 25 | "", 26 | ]); 27 | rows.push([`init code size`, report.bytecodeSize.toString(), "", "", ""]); 28 | const table = toCommentTable(rows); 29 | const separator = table[0]; 30 | table.splice(table.length - 3, 0, separator); 31 | console.log(table.join("\n")); 32 | } 33 | 34 | export function printLastReport(hre: HardhatRuntimeEnvironment) { 35 | writeReports(hre); 36 | const reports = getAllReports(); 37 | if (reports.length < 1) { 38 | console.log(err(`No gas reports found`)); 39 | return; 40 | } 41 | const contractName = "Seaport"; 42 | const [currentReport] = reports; 43 | printReport(currentReport.contractReports[contractName]); 44 | const ts = Math.floor(Date.now() / 1000); 45 | const suffix = 46 | currentReport.name !== currentReport.commitHash 47 | ? ` @ ${currentReport.commitHash}` 48 | : ""; 49 | console.log( 50 | `Current Report: ${+((currentReport.timestamp - ts) / 60).toFixed( 51 | 2 52 | )} min ago (${currentReport.name}) ${suffix}` 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /scripts/write_reports.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | 3 | import { getAllRawReports, getCommitHash } from "./utils"; 4 | 5 | import type { CommitGasReport, ContractReport } from "./utils"; 6 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 7 | 8 | export function writeReports(hre: HardhatRuntimeEnvironment): void { 9 | const rawReports = getAllRawReports(); 10 | if (rawReports.length === 0) { 11 | return; 12 | } 13 | if (rawReports.length > 1) { 14 | throw Error( 15 | `Multiple pending reports. Can only process most recent report to obtain current contract sizes` 16 | ); 17 | } 18 | const [rawReport] = rawReports; 19 | const contractReports: Record = {}; 20 | for (const { contract, ...report } of rawReport.report) { 21 | if (!contractReports[contract]) { 22 | const artifact = hre.artifacts.readArtifactSync(contract); 23 | const bytecode = Buffer.from(artifact.bytecode.slice(2), "hex"); 24 | const deployedBytecode = Buffer.from( 25 | artifact.deployedBytecode.slice(2), 26 | "hex" 27 | ); 28 | contractReports[contract] = { 29 | name: contract, 30 | methods: [], 31 | bytecodeSize: bytecode.byteLength, 32 | deployedBytecodeSize: deployedBytecode.byteLength, 33 | }; 34 | } 35 | contractReports[contract].methods.push(report); 36 | } 37 | const report: CommitGasReport = { 38 | commitHash: getCommitHash(), 39 | contractReports, 40 | }; 41 | fs.unlinkSync(rawReport.path); 42 | fs.writeFileSync( 43 | rawReport.path.replace(".md", ".json"), 44 | JSON.stringify(report, null, 2) 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /templates/GenericEnumerableSet.template: -------------------------------------------------------------------------------- 1 | // // SPDX-License-Identifier: MIT 2 | // pragma solidity ^0.8.17; 3 | 4 | // import { } from "seaport-sol/SeaportSol.sol"; 5 | 6 | // struct Set { 7 | // mapping(bytes32 => uint256) offByOneIndex; 8 | // [] enumeration; 9 | // } 10 | 11 | // library SetLib { 12 | // error NotPresent(); 13 | 14 | // function add( 15 | // Set storage set, 16 | // memory value 17 | // ) internal returns (bool added) { 18 | // // add value to enumeration; hash it to set its entry in the offByOneIndex 19 | // bytes32 key = keccak256(abi.encode(value)); 20 | // if (set.offByOneIndex[key] == 0) { 21 | // set.enumeration.push(value); 22 | // set.offByOneIndex[key] = set.enumeration.length; 23 | // added = true; 24 | // } else { 25 | // added = false; 26 | // } 27 | // } 28 | 29 | // // remove value from enumeration and replace it with last member of enumeration 30 | // // if not last member, update offByOneIndex of last member 31 | // function remove( 32 | // Set storage set, 33 | // memory value 34 | // ) internal returns (bool removed) { 35 | // bytes32 key = keccak256(abi.encode(value)); 36 | // uint256 index = set.offByOneIndex[key]; 37 | // if (index > 0) { 38 | // uint256 lastIndex = set.enumeration.length - 1; 39 | // memory lastValue = set.enumeration[lastIndex]; 40 | // set.enumeration[index - 1] = lastValue; 41 | // bytes32 lastKey = keccak256(abi.encode(lastValue)); 42 | // // if lastKey is the same as key, then we are removing the last element; do not update it 43 | // if (lastKey != key) { 44 | // set.offByOneIndex[lastKey] = index; 45 | // } 46 | // set.enumeration.pop(); 47 | // delete set.offByOneIndex[key]; 48 | // removed = true; 49 | // } else { 50 | // removed = false; 51 | // } 52 | // } 53 | 54 | // function removeAll(Set storage set, [] memory values) internal { 55 | // for (uint256 i = 0; i < values.length; i++) { 56 | // remove(set, values[i]); 57 | // } 58 | // } 59 | 60 | // function removeAll(Set storage set, [][] memory values) internal { 61 | // for (uint256 i = 0; i < values.length; i++) { 62 | // removeAll(set, values[i]); 63 | // } 64 | // } 65 | 66 | // function contains( 67 | // Set storage set, 68 | // memory value 69 | // ) internal view returns (bool) { 70 | // return set.offByOneIndex[keccak256(abi.encode(value))] > 0; 71 | // } 72 | 73 | // function length(Set storage set) internal view returns (uint256) { 74 | // return set.enumeration.length; 75 | // } 76 | 77 | // function at( 78 | // Set storage set, 79 | // uint256 index 80 | // ) internal view returns ( memory) { 81 | // return set.enumeration[index]; 82 | // } 83 | 84 | // function clear(Set storage set) internal { 85 | // while (set.enumeration.length > 0) { 86 | // memory component = set.enumeration[set.enumeration.length - 1]; 87 | // delete set.offByOneIndex[keccak256(abi.encode(component))]; 88 | // set.enumeration.pop(); 89 | // } 90 | // } 91 | // } 92 | -------------------------------------------------------------------------------- /templates/GenericStructSortLib.template: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { } from "seaport-sol/SeaportSol.sol"; 5 | 6 | library SortLib { 7 | function key( memory component) internal pure returns (uint256); 8 | 9 | function sort([] memory components) internal pure { 10 | sort(components, key); 11 | } 12 | 13 | // Sorts the array in-place with intro-quicksort. 14 | function sort( 15 | [] memory a, 16 | function( memory) internal pure returns (uint256) accessor 17 | ) internal pure { 18 | if (a.length < 2) { 19 | return; 20 | } 21 | 22 | uint256[] memory stack = new uint256[](2 * a.length); 23 | uint256 stackIndex = 0; 24 | 25 | uint256 l = 0; 26 | uint256 h = a.length - 1; 27 | 28 | stack[stackIndex++] = l; 29 | stack[stackIndex++] = h; 30 | 31 | while (stackIndex > 0) { 32 | h = stack[--stackIndex]; 33 | l = stack[--stackIndex]; 34 | 35 | if (h - l <= 12) { 36 | // Insertion sort for small subarrays 37 | for (uint256 i = l + 1; i <= h; i++) { 38 | memory k = a[i]; 39 | uint256 j = i; 40 | while (j > l && accessor(a[j - 1]) > accessor(k)) { 41 | a[j] = a[j - 1]; 42 | j--; 43 | } 44 | a[j] = k; 45 | } 46 | } else { 47 | // Intro-Quicksort 48 | uint256 p = (l + h) / 2; 49 | 50 | // Median of 3 51 | if (accessor(a[l]) > accessor(a[p])) { 52 | (a[l], a[p]) = (a[p], a[l]); 53 | } 54 | if (accessor(a[l]) > accessor(a[h])) { 55 | (a[l], a[h]) = (a[h], a[l]); 56 | } 57 | if (accessor(a[p]) > accessor(a[h])) { 58 | (a[p], a[h]) = (a[h], a[p]); 59 | } 60 | 61 | uint256 pivot = accessor(a[p]); 62 | uint256 i = l; 63 | uint256 j = h; 64 | 65 | while (i <= j) { 66 | while (accessor(a[i]) < pivot) { 67 | i++; 68 | } 69 | while (accessor(a[j]) > pivot) { 70 | j--; 71 | } 72 | if (i <= j) { 73 | (a[i], a[j]) = (a[j], a[i]); 74 | i++; 75 | j--; 76 | } 77 | } 78 | 79 | if (j > l) { 80 | stack[stackIndex++] = l; 81 | stack[stackIndex++] = j; 82 | } 83 | if (i < h) { 84 | stack[stackIndex++] = i; 85 | stack[stackIndex++] = h; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /templates/replace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # check if both arguments are supplied 4 | if [ $# -ne 2 ]; then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | # check if file exists 10 | if [ ! -f "$1" ]; then 11 | echo "File $1 not found" 12 | exit 1 13 | fi 14 | 15 | # replace all instances of XXX with second argument 16 | sed "s/\/$2/g" "$1" 17 | -------------------------------------------------------------------------------- /test/foundry/CeilEquivalenceTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | contract CeilEquivalenceTest { 6 | function testCeilEquivalence( 7 | uint256 numerator, 8 | uint256 denominator 9 | ) public pure { 10 | // There is intermediate overflow for the unoptimized ceil 11 | // but for the sake of this test we'll ignore those cases. 12 | numerator %= type(uint128).max; 13 | denominator %= type(uint128).max; 14 | denominator++; // Ignore zero. 15 | 16 | uint256 optimized; 17 | assembly { 18 | optimized := mul( 19 | add(div(sub(numerator, 1), denominator), 1), 20 | iszero(iszero(numerator)) 21 | ) 22 | } 23 | 24 | uint256 unoptimized; 25 | assembly { 26 | unoptimized := div(add(numerator, sub(denominator, 1)), denominator) 27 | } 28 | 29 | assert(optimized == unoptimized); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/foundry/NonMatchSelectorTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | //Author: Saw-mon and Natalie 3 | 4 | pragma solidity ^0.8.17; 5 | 6 | import { 7 | ConsiderationInterface 8 | } from "seaport-types/src/interfaces/ConsiderationInterface.sol"; 9 | 10 | import { 11 | NonMatchSelector_MagicMask, 12 | NonMatchSelector_InvalidErrorValue 13 | } from "seaport-types/src/lib/ConsiderationConstants.sol"; 14 | 15 | import { Test } from "forge-std/Test.sol"; 16 | 17 | contract NonMatchSelectorTest is Test { 18 | function testNonMatchSelectorMagicMaskAndInvalidErrorValue() public { 19 | assertEq( 20 | NonMatchSelector_MagicMask + 1, 21 | NonMatchSelector_InvalidErrorValue 22 | ); 23 | } 24 | 25 | function testSelectorMatchOrders() public { 26 | _testSelector(ConsiderationInterface.matchOrders.selector, false); 27 | } 28 | 29 | function testSelectorMatchAdvancedOrders() public { 30 | _testSelector( 31 | ConsiderationInterface.matchAdvancedOrders.selector, 32 | false 33 | ); 34 | } 35 | 36 | function testSelectorFulfillAvailableOrders() public { 37 | _testSelector( 38 | ConsiderationInterface.fulfillAvailableOrders.selector, 39 | true 40 | ); 41 | } 42 | 43 | function testSelectorFulfillAvailableAdvancedOrders() public { 44 | _testSelector( 45 | ConsiderationInterface.fulfillAvailableAdvancedOrders.selector, 46 | true 47 | ); 48 | } 49 | 50 | function _testSelector(bytes4 selector, bool shouldBeSelected) internal { 51 | bool isSelected; 52 | 53 | assembly { 54 | isSelected := eq( 55 | NonMatchSelector_MagicMask, 56 | and(NonMatchSelector_MagicMask, selector) 57 | ) 58 | } 59 | 60 | assertEq(isSelected, shouldBeSelected); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/foundry/conduit/ConduitExecute.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ConduitTransfer, 6 | ConduitItemType 7 | } from "seaport-types/src/conduit/lib/ConduitStructs.sol"; 8 | import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; 9 | import { TestERC20 } from "../../../contracts/test/TestERC20.sol"; 10 | import { TestERC721 } from "../../../contracts/test/TestERC721.sol"; 11 | import { BaseConduitTest } from "./BaseConduitTest.sol"; 12 | import { Conduit } from "seaport-core/src/conduit/Conduit.sol"; 13 | 14 | contract ConduitExecuteTest is BaseConduitTest { 15 | struct FuzzInputs { 16 | ConduitTransferIntermediate[20] intermediates; 17 | } 18 | 19 | struct Context { 20 | Conduit conduit; 21 | ConduitTransfer[] transfers; 22 | } 23 | 24 | function test( 25 | function(Context memory) external fn, 26 | Context memory context 27 | ) internal { 28 | try fn(context) {} catch (bytes memory reason) { 29 | assertPass(reason); 30 | } 31 | } 32 | 33 | function testExecute(FuzzInputs memory inputs) public { 34 | ConduitTransfer[] memory transfers = new ConduitTransfer[](0); 35 | for (uint8 i; i < inputs.intermediates.length; ++i) { 36 | transfers = extendConduitTransferArray( 37 | transfers, 38 | deployTokenAndCreateConduitTransfers(inputs.intermediates[i]) 39 | ); 40 | } 41 | makeRecipientsSafe(transfers); 42 | mintTokensAndSetTokenApprovalsForConduit(transfers); 43 | updateExpectedTokenBalances(transfers); 44 | 45 | test(this.execute, Context(referenceConduit, transfers)); 46 | test(this.execute, Context(conduit, transfers)); 47 | } 48 | 49 | function execute(Context memory context) external stateless { 50 | bytes4 magicValue = context.conduit.execute(context.transfers); 51 | assertEq(magicValue, Conduit.execute.selector); 52 | 53 | for (uint256 i; i < context.transfers.length; ++i) { 54 | ConduitTransfer memory transfer = context.transfers[i]; 55 | ConduitItemType itemType = transfer.itemType; 56 | if (itemType == ConduitItemType.ERC20) { 57 | assertEq( 58 | TestERC20(transfer.token).balanceOf(transfer.to), 59 | getExpectedTokenBalance(transfer) 60 | ); 61 | } else if (itemType == ConduitItemType.ERC1155) { 62 | assertEq( 63 | TestERC1155(transfer.token).balanceOf( 64 | transfer.to, 65 | transfer.identifier 66 | ), 67 | getExpectedTokenBalance(transfer) 68 | ); 69 | } else if (itemType == ConduitItemType.ERC721) { 70 | assertEq( 71 | TestERC721(transfer.token).ownerOf(transfer.identifier), 72 | transfer.to 73 | ); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/foundry/conduit/ConduitExecuteBatch1155.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ConduitBatch1155Transfer 6 | } from "seaport-types/src/conduit/lib/ConduitStructs.sol"; 7 | import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol"; 8 | import { BaseConduitTest } from "./BaseConduitTest.sol"; 9 | import { Conduit } from "seaport-core/src/conduit/Conduit.sol"; 10 | 11 | contract ConduitExecuteBatch1155Test is BaseConduitTest { 12 | struct FuzzInputs { 13 | BatchIntermediate[10] batchIntermediates; 14 | } 15 | 16 | struct Context { 17 | Conduit conduit; 18 | ConduitBatch1155Transfer[] batchTransfers; 19 | } 20 | 21 | function test( 22 | function(Context memory) external fn, 23 | Context memory context 24 | ) internal { 25 | try fn(context) {} catch (bytes memory reason) { 26 | assertPass(reason); 27 | } 28 | } 29 | 30 | function testExecuteBatch1155(FuzzInputs memory inputs) public { 31 | ConduitBatch1155Transfer[] 32 | memory batchTransfers = new ConduitBatch1155Transfer[](0); 33 | for (uint8 j = 0; j < inputs.batchIntermediates.length; ++j) { 34 | batchTransfers = extendConduitTransferArray( 35 | batchTransfers, 36 | deployTokenAndCreateConduitBatch1155Transfer( 37 | inputs.batchIntermediates[j] 38 | ) 39 | ); 40 | } 41 | makeRecipientsSafe(batchTransfers); 42 | mintTokensAndSetTokenApprovalsForConduit(batchTransfers); 43 | updateExpectedTokenBalances(batchTransfers); 44 | test(this.executeBatch1155, Context(referenceConduit, batchTransfers)); 45 | test(this.executeBatch1155, Context(conduit, batchTransfers)); 46 | } 47 | 48 | function executeBatch1155(Context memory context) external stateless { 49 | bytes4 magicValue = context.conduit.executeBatch1155( 50 | context.batchTransfers 51 | ); 52 | assertEq(magicValue, Conduit.executeBatch1155.selector); 53 | 54 | for (uint256 i = 0; i < context.batchTransfers.length; ++i) { 55 | ConduitBatch1155Transfer memory batchTransfer = context 56 | .batchTransfers[i]; 57 | 58 | address[] memory toAddresses = new address[]( 59 | batchTransfer.ids.length 60 | ); 61 | for (uint256 j = 0; j < batchTransfer.ids.length; ++j) { 62 | toAddresses[j] = batchTransfer.to; 63 | } 64 | uint256[] memory actualBatchBalances = TestERC1155( 65 | batchTransfer.token 66 | ).balanceOfBatch(toAddresses, batchTransfer.ids); 67 | uint256[] 68 | memory expectedBatchBalances = getExpectedBatchTokenBalances( 69 | batchTransfer 70 | ); 71 | assertTrue( 72 | actualBatchBalances.length == expectedBatchBalances.length 73 | ); 74 | 75 | for (uint256 j = 0; j < actualBatchBalances.length; ++j) { 76 | assertEq(actualBatchBalances[j], expectedBatchBalances[j]); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/foundry/interfaces/OwnableDelegateProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface OwnableDelegateProxy { 5 | function name() external returns (string memory); 6 | 7 | function proxyOwner() external returns (address); 8 | } 9 | -------------------------------------------------------------------------------- /test/foundry/interfaces/ProxyRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { OwnableDelegateProxy } from "./OwnableDelegateProxy.sol"; 5 | 6 | interface ProxyRegistry { 7 | function delegateProxyImplementation() external returns (address); 8 | 9 | function registerProxy() external returns (OwnableDelegateProxy); 10 | 11 | function proxies( 12 | address _addr 13 | ) external view returns (OwnableDelegateProxy); 14 | } 15 | -------------------------------------------------------------------------------- /test/foundry/new/FuzzMain.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { FuzzEngine } from "./helpers/FuzzEngine.sol"; 5 | 6 | import { FuzzParams } from "./helpers/FuzzTestContextLib.sol"; 7 | 8 | contract FuzzMainTest is FuzzEngine { 9 | /** 10 | * @dev FuzzEngine entry point. Generates a random order configuration, 11 | * selects and calls a Seaport method, and runs all registered checks. 12 | * This test should never revert. For more details on the lifecycle of 13 | * this test, see `FuzzEngine.sol`. 14 | */ 15 | function test_fuzz_generateOrders( 16 | uint256 seed, 17 | uint256 orders, 18 | uint256 maxOfferItemsPerOrder, 19 | uint256 maxConsiderationItemsPerOrder 20 | ) public { 21 | run( 22 | FuzzParams({ 23 | seed: seed, 24 | totalOrders: bound(orders, 1, 10), 25 | maxOfferItems: bound(maxOfferItemsPerOrder, 0, 10), 26 | maxConsiderationItems: bound( 27 | maxConsiderationItemsPerOrder, 28 | 0, 29 | 10 30 | ), 31 | seedInput: abi.encodePacked( 32 | seed, 33 | orders, 34 | maxOfferItemsPerOrder, 35 | maxConsiderationItemsPerOrder 36 | ) 37 | }) 38 | ); 39 | } 40 | 41 | /** 42 | * @dev A helper to convert a fuzz test failure into a concrete test. 43 | * Copy/paste fuzz run parameters into the tuple below and remove the 44 | * leading "x" to run a fuzz failure as a concrete test. 45 | */ 46 | function xtest_concrete() public { 47 | ( 48 | uint256 seed, 49 | uint256 orders, 50 | uint256 maxOfferItemsPerOrder, 51 | uint256 maxConsiderationItemsPerOrder 52 | ) = (0, 0, 0, 0); 53 | bytes memory callData = abi.encodeCall( 54 | this.test_fuzz_generateOrders, 55 | (seed, orders, maxOfferItemsPerOrder, maxConsiderationItemsPerOrder) 56 | ); 57 | (bool success, bytes memory result) = address(this).call(callData); 58 | if (!success) { 59 | if (result.length == 0) revert(); 60 | assembly { 61 | revert(add(0x20, result), mload(result)) 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/foundry/new/SeaportNavigatorTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | CriteriaHelper 6 | } from "../../../contracts/helpers/navigator/lib/CriteriaHelper.sol"; 7 | 8 | import { 9 | ExecutionsHelper 10 | } from "../../../contracts/helpers/navigator/lib/ExecutionsHelper.sol"; 11 | 12 | import { 13 | FulfillmentsHelper 14 | } from "../../../contracts/helpers/navigator/lib/FulfillmentsHelper.sol"; 15 | 16 | import { 17 | HelperInterface 18 | } from "../../../contracts/helpers/navigator/lib/HelperInterface.sol"; 19 | 20 | import { 21 | OrderDetailsHelper 22 | } from "../../../contracts/helpers/navigator/lib/OrderDetailsHelper.sol"; 23 | 24 | import { 25 | RequestValidator 26 | } from "../../../contracts/helpers/navigator/lib/RequestValidator.sol"; 27 | 28 | import { 29 | SeaportNavigator 30 | } from "../../../contracts/helpers/navigator/SeaportNavigator.sol"; 31 | 32 | import { 33 | SuggestedActionHelper 34 | } from "../../../contracts/helpers/navigator/lib/SuggestedActionHelper.sol"; 35 | 36 | import { 37 | ValidatorHelper 38 | } from "../../../contracts/helpers/navigator/lib/ValidatorHelper.sol"; 39 | 40 | contract SeaportNavigatorTest { 41 | HelperInterface internal requestValidator = new RequestValidator(); 42 | HelperInterface internal criteriaHelper = new CriteriaHelper(); 43 | HelperInterface internal validatorHelper = new ValidatorHelper(); 44 | HelperInterface internal orderDetailsHelper = new OrderDetailsHelper(); 45 | HelperInterface internal fulfillmentsHelper = new FulfillmentsHelper(); 46 | HelperInterface internal suggestedActionHelper = 47 | new SuggestedActionHelper(); 48 | HelperInterface internal executionsHelper = new ExecutionsHelper(); 49 | 50 | // Initialize the navigator with all its constituent helpers. 51 | SeaportNavigator internal navigator = 52 | new SeaportNavigator( 53 | address(requestValidator), 54 | address(criteriaHelper), 55 | address(validatorHelper), 56 | address(orderDetailsHelper), 57 | address(fulfillmentsHelper), 58 | address(suggestedActionHelper), 59 | address(executionsHelper) 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /test/foundry/new/SeaportValidatorTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { BaseSeaportTest } from "./helpers/BaseSeaportTest.sol"; 5 | 6 | import { 7 | ReadOnlyOrderValidator, 8 | SeaportValidator, 9 | SeaportValidatorHelper 10 | } from "../../../contracts/helpers/order-validator/SeaportValidator.sol"; 11 | 12 | contract SeaportValidatorTest is BaseSeaportTest { 13 | SeaportValidatorHelper internal seaportValidatorHelper; 14 | SeaportValidator internal validator; 15 | 16 | function setUp() public virtual override { 17 | super.setUp(); 18 | 19 | // Note: this chainId hack prevents the validator from calling a 20 | // hardcoded royalty registry. 21 | uint256 chainId = block.chainid; 22 | vm.chainId(2); 23 | seaportValidatorHelper = new SeaportValidatorHelper(); 24 | vm.chainId(chainId); 25 | 26 | // Initialize the validator. 27 | validator = new SeaportValidator( 28 | address(new ReadOnlyOrderValidator()), 29 | address(seaportValidatorHelper), 30 | address(getConduitController()) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/ArithmeticUtil.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | library ArithmeticUtil { 5 | /** 6 | * @dev utility function to avoid overflows when multiplying fuzzed uints 7 | * with widths <256 8 | */ 9 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 10 | return a * b; 11 | } 12 | 13 | /** 14 | * @dev utility function to avoid overflows when adding fuzzed uints with 15 | * with widths <256 16 | */ 17 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 18 | return a + b; 19 | } 20 | 21 | /** 22 | * @dev utility function to avoid overflows when subtracting fuzzed uints 23 | * with widths <256 24 | */ 25 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 26 | return a - b; 27 | } 28 | 29 | /** 30 | * @dev utility function to avoid overflows when dividing fuzzed uints with 31 | * widths <256 32 | */ 33 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 34 | return a / b; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/DifferentialTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { Test } from "forge-std/Test.sol"; 5 | 6 | contract DifferentialTest is Test { 7 | ///@dev error to supply 8 | error RevertWithFailureStatus(bool status); 9 | error DifferentialTestAssertionFailed(); 10 | 11 | // slot where HEVM stores a bool representing whether or not an assertion has failed 12 | bytes32 HEVM_FAILED_SLOT = bytes32("failed"); 13 | 14 | // hash of the bytes surfaced by `revert RevertWithFailureStatus(false)` 15 | bytes32 PASSING_HASH = 16 | keccak256( 17 | abi.encodeWithSelector(RevertWithFailureStatus.selector, false) 18 | ); 19 | 20 | ///@dev reverts after function body with HEVM failure status, which clears all state changes 21 | /// but still surfaces assertion failure status. 22 | modifier stateless() { 23 | _; 24 | revert RevertWithFailureStatus(readHevmFailureSlot()); 25 | } 26 | 27 | ///@dev revert if the supplied bytes do not match the expected "passing" revert bytes 28 | function assertPass(bytes memory reason) internal view { 29 | // hash the reason and compare to the hash of the passing revert bytes 30 | if (keccak256(reason) != PASSING_HASH) { 31 | revert DifferentialTestAssertionFailed(); 32 | } 33 | } 34 | 35 | ///@dev read the failure slot of the HEVM using the vm.load cheatcode 36 | /// Returns true if there was an assertion failure. recorded. 37 | function readHevmFailureSlot() internal view returns (bool) { 38 | return vm.load(address(vm), HEVM_FAILED_SLOT) == bytes32(uint256(1)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/EIP1271Offerer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { ERC1155Recipient } from "../../utils/ERC1155Recipient.sol"; 5 | 6 | contract EIP1271Offerer is ERC1155Recipient { 7 | error EIP1271OffererInvalidSignature(bytes32 digest, bytes signature); 8 | 9 | bytes4 private constant _EIP_1271_MAGIC_VALUE = 0x1626ba7e; 10 | 11 | mapping(bytes32 => bytes32) public digestToSignatureHash; 12 | 13 | bool private _returnEmpty = false; 14 | 15 | function registerSignature(bytes32 digest, bytes memory signature) public { 16 | digestToSignatureHash[digest] = keccak256(signature); 17 | } 18 | 19 | function isValidSignature( 20 | bytes32 digest, 21 | bytes memory signature 22 | ) external view returns (bytes4) { 23 | if (_returnEmpty) { 24 | return bytes4(0x00000000); 25 | } 26 | 27 | bytes32 signatureHash = keccak256(signature); 28 | if (digestToSignatureHash[digest] == signatureHash) { 29 | return _EIP_1271_MAGIC_VALUE; 30 | } 31 | 32 | // TODO: test for bubbled up revert reasons as well 33 | assembly { 34 | revert(0, 0) 35 | } 36 | } 37 | 38 | function returnEmpty() external { 39 | _returnEmpty = true; 40 | } 41 | 42 | function is1271() external pure returns (bool) { 43 | return true; 44 | } 45 | 46 | receive() external payable {} 47 | } 48 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/ERC1155Recipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ERC1155TokenReceiver 6 | } from "@rari-capital/solmate/src/tokens/ERC1155.sol"; 7 | 8 | contract ERC1155Recipient is ERC1155TokenReceiver { 9 | function onERC1155Received( 10 | address, 11 | address, 12 | uint256, 13 | uint256, 14 | bytes calldata 15 | ) public virtual override returns (bytes4) { 16 | return ERC1155TokenReceiver.onERC1155Received.selector; 17 | } 18 | 19 | function onERC1155BatchReceived( 20 | address, 21 | address, 22 | uint256[] calldata, 23 | uint256[] calldata, 24 | bytes calldata 25 | ) external virtual override returns (bytes4) { 26 | return ERC1155TokenReceiver.onERC1155BatchReceived.selector; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/ERC721Recipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ERC721TokenReceiver 6 | } from "@rari-capital/solmate/src/tokens/ERC721.sol"; 7 | 8 | contract ERC721Recipient is ERC721TokenReceiver { 9 | function onERC721Received( 10 | address, 11 | address, 12 | uint256, 13 | bytes calldata 14 | ) public virtual override returns (bytes4) { 15 | return ERC721TokenReceiver.onERC721Received.selector; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/Labeler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { vm } from "./VmUtils.sol"; 5 | import { LibString } from "solady/src/utils/LibString.sol"; 6 | 7 | address constant LABELER_ADDRESS = address( 8 | uint160(uint256(keccak256(".labeler"))) 9 | ); 10 | 11 | function setLabel(address account, string memory _label) { 12 | vm.store( 13 | LABELER_ADDRESS, 14 | bytes32(uint256(uint160(account))), 15 | LibString.packOne(_label) 16 | ); 17 | } 18 | 19 | function withLabel(address account) pure returns (string memory out) { 20 | out = LibString.toHexString(account); 21 | string memory label = pureGetLabel()(account); 22 | uint256 length; 23 | assembly { 24 | length := mload(label) 25 | } 26 | if (length > 0) { 27 | out = string.concat(out, " (", label, ")"); 28 | } 29 | } 30 | 31 | function getLabel(address account) pure returns (string memory) { 32 | return pureGetLabel()(account); 33 | } 34 | 35 | function getLabelView(address account) view returns (string memory _label) { 36 | bytes32 storedLabel = vm.load( 37 | LABELER_ADDRESS, 38 | bytes32(uint256(uint160(account))) 39 | ); 40 | if (storedLabel != bytes32(0)) { 41 | return LibString.unpackOne(storedLabel); 42 | } 43 | } 44 | 45 | function withLabel(address[] memory accounts) pure returns (string[] memory) { 46 | uint256 length = accounts.length; 47 | string[] memory out = new string[](length); 48 | for (uint256 i; i < length; i++) { 49 | out[i] = withLabel(accounts[i]); 50 | } 51 | return out; 52 | } 53 | 54 | function pureGetLabel() 55 | pure 56 | returns (function(address) internal pure returns (string memory) pureFn) 57 | { 58 | function(address) 59 | internal 60 | view 61 | returns (string memory) viewFn = getLabelView; 62 | assembly { 63 | pureFn := viewFn 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/Metrics.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { vm } from "./VmUtils.sol"; 5 | 6 | function logCall(string memory name) { 7 | logCall(name, true); 8 | } 9 | 10 | /** 11 | * @dev Log a call to "call-metrics.txt" 12 | */ 13 | function logCall(string memory name, bool enabled) { 14 | logCounter("call", name, enabled); 15 | } 16 | 17 | /** 18 | * @dev Log a mutation to "mutation-metrics.txt" 19 | */ 20 | function logMutation(string memory name) { 21 | logCounter("mutation", name, true); 22 | } 23 | 24 | /** 25 | * @dev Log a vm.assume to "assume-metrics.txt" 26 | */ 27 | function logAssume(string memory name) { 28 | logCounter("assume", name, true); 29 | } 30 | 31 | /** 32 | * @dev Log a counter to a metrics file if the SEAPORT_COLLECT_FUZZ_METRICS env 33 | * var is set. Named metrics are written as statsd counters, e.g. 34 | * "metric:1|c". To write to a new file, it must be allowlisted under 35 | * `fs_permissions` in `foundry.toml`. 36 | * 37 | * @param file name of the metrics file to write to. "-metrics.txt" will be 38 | * appended to the name. 39 | * @param metric name of the metric to increment. 40 | * @param enabled flag to enable/disable metrics collection 41 | */ 42 | function logCounter(string memory file, string memory metric, bool enabled) { 43 | if (enabled && vm.envOr("SEAPORT_COLLECT_FUZZ_METRICS", false)) { 44 | string memory counter = string.concat(metric, ":1|c"); 45 | vm.writeLine(string.concat(file, "-metrics.txt"), counter); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/PreapprovedERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { CustomERC721 } from "../../token/CustomERC721.sol"; 5 | 6 | contract PreapprovedERC721 is CustomERC721 { 7 | mapping(address => bool) public preapprovals; 8 | 9 | constructor(address[] memory preapproved) CustomERC721("", "") { 10 | for (uint256 i = 0; i < preapproved.length; i++) { 11 | preapprovals[preapproved[i]] = true; 12 | } 13 | } 14 | 15 | function mint(address to, uint256 amount) external returns (bool) { 16 | _mint(to, amount); 17 | return true; 18 | } 19 | 20 | function isApprovedForAll( 21 | address owner, 22 | address operator 23 | ) public view override returns (bool) { 24 | return 25 | preapprovals[operator] || super.isApprovedForAll(owner, operator); 26 | } 27 | 28 | function tokenURI(uint256) public pure override returns (string memory) { 29 | return ""; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/VmUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { Vm } from "forge-std/Vm.sol"; 5 | import { logAssume } from "./Metrics.sol"; 6 | 7 | address constant VM_ADDRESS = address( 8 | uint160(uint256(keccak256("hevm cheat code"))) 9 | ); 10 | Vm constant vm = Vm(VM_ADDRESS); 11 | 12 | /** 13 | * @dev A wrapper for Foundry vm.assume that logs rejected fuzz runs with a 14 | * named reason. Use this instead of vm.assume in fuzz tests and give 15 | * each assumption a unique name. 16 | */ 17 | function assume(bool condition, string memory name) { 18 | if (!condition) { 19 | logAssume(name); 20 | } 21 | vm.assume(condition); 22 | } 23 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/event-utils/EventHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | /** 5 | * @dev Low level helpers. getTopicsHash and getEventHash are used to generate 6 | * the hashes for topics and events respectively. getEventHashWithTopics is 7 | * a convenience wrapper around the two. 8 | */ 9 | function getTopicsHash( 10 | bytes32 topic0, 11 | bytes32 topic1, 12 | bytes32 topic2, 13 | bytes32 topic3 14 | ) pure returns (bytes32 topicsHash) { 15 | topicsHash = keccak256(abi.encode(topic0, topic1, topic2, topic3)); 16 | } 17 | 18 | function getTopicsHash( 19 | bytes32 topic0, 20 | bytes32 topic1, 21 | bytes32 topic2 22 | ) pure returns (bytes32 topicsHash) { 23 | topicsHash = keccak256(abi.encode(topic0, topic1, topic2)); 24 | } 25 | 26 | function getTopicsHash( 27 | bytes32 topic0, 28 | bytes32 topic1 29 | ) pure returns (bytes32 topicsHash) { 30 | topicsHash = keccak256(abi.encode(topic0, topic1)); 31 | } 32 | 33 | function getTopicsHash(bytes32 topic0) pure returns (bytes32 topicsHash) { 34 | topicsHash = keccak256(abi.encode(topic0)); 35 | } 36 | 37 | function getTopicsHash() pure returns (bytes32 topicsHash) { 38 | topicsHash = keccak256(""); 39 | } 40 | 41 | function getEventHash( 42 | address emitter, 43 | bytes32 topicsHash, 44 | bytes32 dataHash 45 | ) pure returns (bytes32 eventHash) { 46 | return keccak256(abi.encode(emitter, topicsHash, dataHash)); 47 | } 48 | 49 | function getEventHashWithTopics( 50 | address emitter, 51 | bytes32 topic0, 52 | bytes32 topic1, 53 | bytes32 topic2, 54 | bytes32 topic3, 55 | bytes32 dataHash 56 | ) pure returns (bytes32 eventHash) { 57 | bytes32 topicsHash = getTopicsHash(topic0, topic1, topic2, topic3); 58 | return getEventHash(emitter, topicsHash, dataHash); 59 | } 60 | 61 | function getEventHashWithTopics( 62 | address emitter, 63 | bytes32 topic0, 64 | bytes32 topic1, 65 | bytes32 topic2, 66 | bytes32 dataHash 67 | ) pure returns (bytes32 eventHash) { 68 | bytes32 topicsHash = getTopicsHash(topic0, topic1, topic2); 69 | return getEventHash(emitter, topicsHash, dataHash); 70 | } 71 | 72 | function getEventHashWithTopics( 73 | address emitter, 74 | bytes32 topic0, 75 | bytes32 topic1, 76 | bytes32 dataHash 77 | ) pure returns (bytes32 eventHash) { 78 | bytes32 topicsHash = getTopicsHash(topic0, topic1); 79 | return getEventHash(emitter, topicsHash, dataHash); 80 | } 81 | 82 | function getEventHashWithTopics( 83 | address emitter, 84 | bytes32 topic0, 85 | bytes32 dataHash 86 | ) pure returns (bytes32 eventHash) { 87 | bytes32 topicsHash = getTopicsHash(topic0); 88 | return getEventHash(emitter, topicsHash, dataHash); 89 | } 90 | 91 | function getEventHashWithTopics( 92 | address emitter, 93 | bytes32 dataHash 94 | ) pure returns (bytes32 eventHash) { 95 | bytes32 topicsHash = getTopicsHash(); 96 | return getEventHash(emitter, topicsHash, dataHash); 97 | } 98 | -------------------------------------------------------------------------------- /test/foundry/new/helpers/event-utils/OrdersMatchedEventsLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { FuzzTestContext } from "../FuzzTestContextLib.sol"; 5 | 6 | import { getEventHashWithTopics } from "./EventHashes.sol"; 7 | 8 | import { UnavailableReason } from "seaport-sol/src/SpaceEnums.sol"; 9 | 10 | library OrdersMatchedEventsLib { 11 | event OrdersMatched(bytes32[] orderHashes); 12 | 13 | function getOrdersMatchedEventHash( 14 | FuzzTestContext memory context 15 | ) internal pure returns (bytes32 eventHash) { 16 | uint256 totalAvailableOrders = 0; 17 | for ( 18 | uint256 i = 0; 19 | i < context.executionState.orderDetails.length; 20 | ++i 21 | ) { 22 | if ( 23 | context.executionState.orderDetails[i].unavailableReason == 24 | UnavailableReason.AVAILABLE 25 | ) { 26 | ++totalAvailableOrders; 27 | } 28 | } 29 | 30 | bytes32[] memory orderHashes = new bytes32[](totalAvailableOrders); 31 | 32 | totalAvailableOrders = 0; 33 | for ( 34 | uint256 i = 0; 35 | i < context.executionState.orderDetails.length; 36 | ++i 37 | ) { 38 | if ( 39 | context.executionState.orderDetails[i].unavailableReason == 40 | UnavailableReason.AVAILABLE 41 | ) { 42 | orderHashes[totalAvailableOrders++] = context 43 | .executionState 44 | .orderDetails[i] 45 | .orderHash; 46 | } 47 | } 48 | 49 | return 50 | getEventHashWithTopics( 51 | address(context.seaport), // emitter 52 | OrdersMatched.selector, // topic0 53 | keccak256(abi.encode(orderHashes)) // dataHash 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/foundry/offerers/impl/TestPoolFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.7; 3 | 4 | import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | 6 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | import { TestPoolOfferer } from "./TestPoolOfferer.sol"; 9 | 10 | contract TestPoolFactory { 11 | // The address of the Seaport contract. 12 | address immutable seaport; 13 | 14 | // Constructor that takes the Seaport contract address as an argument. 15 | constructor(address _seaport) { 16 | seaport = _seaport; 17 | } 18 | 19 | // Function to create a new TestPoolOfferer contract. 20 | function createPoolOfferer( 21 | address erc721, 22 | uint256[] calldata tokenIds, 23 | address erc20, 24 | uint256 amount 25 | ) external returns (TestPoolOfferer newPool) { 26 | // Create a new TestPoolOfferer contract 27 | newPool = new TestPoolOfferer( 28 | seaport, 29 | erc721, 30 | tokenIds, 31 | erc20, 32 | amount, 33 | msg.sender 34 | ); 35 | 36 | // Transfer the specified amount of ERC20 tokens from the caller to the 37 | // new contract. 38 | IERC20(erc20).transferFrom(msg.sender, address(newPool), amount); 39 | 40 | // Transfer the specified ERC721 tokens from the caller to the new 41 | // contract. 42 | for (uint256 i; i < tokenIds.length; i++) { 43 | IERC721(erc721).transferFrom( 44 | msg.sender, 45 | address(newPool), 46 | tokenIds[i] 47 | ); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/foundry/token/StubERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract StubERC1155 { 5 | event TransferSingle( 6 | address indexed operator, 7 | address indexed from, 8 | address indexed to, 9 | uint256 id, 10 | uint256 amount 11 | ); 12 | 13 | function safeTransferFrom( 14 | address from, 15 | address to, 16 | uint256 tokenId, 17 | uint256 amount, 18 | bytes memory 19 | ) public { 20 | emit TransferSingle(msg.sender, from, to, tokenId, amount); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/foundry/token/StubERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract StubERC20 { 5 | event Transfer(address indexed from, address indexed to, uint256 amount); 6 | 7 | function transferFrom( 8 | address from, 9 | address to, 10 | uint256 amount 11 | ) public virtual returns (bool) { 12 | emit Transfer(from, to, amount); 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/foundry/token/StubERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract StubERC721 { 5 | event Transfer(address indexed from, address indexed to, uint256 tokenId); 6 | 7 | function transferFrom(address from, address to, uint256 tokenId) public { 8 | emit Transfer(from, to, tokenId); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/foundry/utils/ArithmeticUtil.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | library ArithmeticUtil { 5 | ///@dev utility function to avoid overflows when multiplying fuzzed uints with widths <256 6 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 7 | return a * b; 8 | } 9 | 10 | ///@dev utility function to avoid overflows when adding fuzzed uints with widths <256 11 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 12 | return a + b; 13 | } 14 | 15 | ///@dev utility function to avoid overflows when subtracting fuzzed uints with widths <256 16 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return a - b; 18 | } 19 | 20 | ///@dev utility function to avoid overflows when dividing fuzzed uints with widths <256 21 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 22 | return a / b; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/foundry/utils/DifferentialTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { Test } from "forge-std/Test.sol"; 5 | 6 | contract DifferentialTest is Test { 7 | ///@dev error to supply 8 | error RevertWithFailureStatus(bool status); 9 | error DifferentialTestAssertionFailed(); 10 | 11 | // slot where HEVM stores a bool representing whether or not an assertion has failed 12 | bytes32 HEVM_FAILED_SLOT = bytes32("failed"); 13 | 14 | // hash of the bytes surfaced by `revert RevertWithFailureStatus(false)` 15 | bytes32 PASSING_HASH = 16 | keccak256( 17 | abi.encodeWithSelector(RevertWithFailureStatus.selector, false) 18 | ); 19 | 20 | ///@dev reverts after function body with HEVM failure status, which clears all state changes 21 | /// but still surfaces assertion failure status. 22 | modifier stateless() { 23 | _; 24 | revert RevertWithFailureStatus(readHevmFailureSlot()); 25 | } 26 | 27 | ///@dev revert if the supplied bytes do not match the expected "passing" revert bytes 28 | function assertPass(bytes memory reason) internal view { 29 | // hash the reason and compare to the hash of the passing revert bytes 30 | if (keccak256(reason) != PASSING_HASH) { 31 | revert DifferentialTestAssertionFailed(); 32 | } 33 | } 34 | 35 | ///@dev read the failure slot of the HEVM using the vm.load cheatcode 36 | /// Returns true if there was an assertion failure. recorded. 37 | function readHevmFailureSlot() internal view returns (bool) { 38 | return vm.load(address(vm), HEVM_FAILED_SLOT) == bytes32(uint256(1)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/foundry/utils/ERC1155Recipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ERC1155TokenReceiver 6 | } from "@rari-capital/solmate/src/tokens/ERC1155.sol"; 7 | 8 | contract ERC1155Recipient is ERC1155TokenReceiver { 9 | function onERC1155Received( 10 | address, 11 | address, 12 | uint256, 13 | uint256, 14 | bytes calldata 15 | ) public virtual override returns (bytes4) { 16 | return ERC1155TokenReceiver.onERC1155Received.selector; 17 | } 18 | 19 | function onERC1155BatchReceived( 20 | address, 21 | address, 22 | uint256[] calldata, 23 | uint256[] calldata, 24 | bytes calldata 25 | ) external virtual override returns (bytes4) { 26 | return ERC1155TokenReceiver.onERC1155BatchReceived.selector; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/foundry/utils/ERC721Recipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ERC721TokenReceiver 6 | } from "@rari-capital/solmate/src/tokens/ERC721.sol"; 7 | 8 | contract ERC721Recipient is ERC721TokenReceiver { 9 | function onERC721Received( 10 | address, 11 | address, 12 | uint256, 13 | bytes calldata 14 | ) public virtual override returns (bytes4) { 15 | return ERC721TokenReceiver.onERC721Received.selector; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/foundry/utils/ExternalCounter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract ExternalCounter { 5 | uint256 public value; 6 | 7 | function increment() external returns (uint256) { 8 | return value++; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/foundry/utils/PseudoRandom.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract PseudoRandom { 5 | bytes32 seedHash; 6 | 7 | constructor(bytes32 _seedHash) { 8 | seedHash = _seedHash; 9 | } 10 | 11 | function prandUint256() external returns (uint256) { 12 | return uint256(updateSeedHash()); 13 | } 14 | 15 | function prandBytes32() external returns (bytes32) { 16 | return updateSeedHash(); 17 | } 18 | 19 | function updateSeedHash() internal returns (bytes32) { 20 | seedHash = keccak256(abi.encode(seedHash)); 21 | return seedHash; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/foundry/utils/reentrancy/ReentrantEnums.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @dev Enum of functions that set the reentrancy guard 6 | */ 7 | enum EntryPoint { 8 | FulfillBasicOrder, 9 | FulfillBasicOrderEfficient, 10 | FulfillOrder, 11 | FulfillAdvancedOrder, 12 | FulfillAvailableOrders, 13 | FulfillAvailableAdvancedOrders, 14 | MatchOrders, 15 | MatchAdvancedOrders 16 | } 17 | 18 | /** 19 | * @dev Enum of functions that check the reentrancy guard 20 | */ 21 | enum ReentryPoint { 22 | FulfillBasicOrder, 23 | FulfillBasicOrderEfficient, 24 | FulfillOrder, 25 | FulfillAdvancedOrder, 26 | FulfillAvailableOrders, 27 | FulfillAvailableAdvancedOrders, 28 | MatchOrders, 29 | MatchAdvancedOrders, 30 | Cancel, 31 | Validate, 32 | IncrementCounter 33 | } 34 | -------------------------------------------------------------------------------- /test/foundry/utils/reentrancy/ReentrantStructs.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | BasicOrderParameters, 6 | OrderComponents, 7 | Fulfillment, 8 | FulfillmentComponent, 9 | Order, 10 | AdvancedOrder, 11 | CriteriaResolver 12 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 13 | 14 | struct FulfillBasicOrderParameters { 15 | BasicOrderParameters parameters; 16 | } 17 | 18 | struct FulfillOrderParameters { 19 | Order order; 20 | bytes32 fulfillerConduitKey; 21 | } 22 | 23 | struct FulfillAdvancedOrderParameters { 24 | AdvancedOrder order; 25 | CriteriaResolver[] criteriaResolvers; 26 | bytes32 fulfillerConduitKey; 27 | } 28 | 29 | struct FulfillAvailableOrdersParameters { 30 | Order[] orders; 31 | FulfillmentComponent[][] orderFulfillments; 32 | FulfillmentComponent[][] considerationFulfillments; 33 | bytes32 fulfillerConduitKey; 34 | uint256 maximumFulfilled; 35 | } 36 | 37 | struct FulfillAvailableAdvancedOrdersParameters { 38 | AdvancedOrder[] advancedOrders; 39 | CriteriaResolver[] criteriaResolvers; 40 | FulfillmentComponent[][] orderFulfillments; 41 | FulfillmentComponent[][] considerationFulfillments; 42 | bytes32 fulfillerConduitKey; 43 | uint256 maximumFulfilled; 44 | } 45 | 46 | struct MatchOrdersParameters { 47 | Order[] orders; 48 | Fulfillment[] fulfillments; 49 | } 50 | 51 | struct MatchAdvancedOrdersParameters { 52 | AdvancedOrder[] advancedOrders; 53 | CriteriaResolver[] criteriaResolvers; 54 | Fulfillment[] fulfillments; 55 | } 56 | 57 | struct CancelParameters { 58 | OrderComponents[] orders; 59 | } 60 | 61 | struct ValidateParameters { 62 | Order[] orders; 63 | } 64 | 65 | struct ReentrantCallParameters { 66 | FulfillBasicOrderParameters fulfillBasicOrderParameters; 67 | FulfillOrderParameters fulfillOrderParameters; 68 | FulfillAdvancedOrderParameters fulfillAdvancedOrderParameters; 69 | FulfillAvailableOrdersParameters fulfillAvailableOrdersParameters; 70 | FulfillAvailableAdvancedOrdersParameters fulfillAvailableAdvancedOrdersParameters; 71 | MatchOrdersParameters matchOrdersParameters; 72 | MatchAdvancedOrdersParameters matchAdvancedOrdersParameters; 73 | CancelParameters cancelParameters; 74 | ValidateParameters validateParameters; 75 | } 76 | -------------------------------------------------------------------------------- /test/foundry/zone/impl/BadZone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ZoneParameters, 6 | Schema 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 10 | 11 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 12 | 13 | contract BadZone is ERC165, ZoneInterface { 14 | function authorizeOrder( 15 | ZoneParameters calldata 16 | ) public pure returns (bytes4) { 17 | return this.authorizeOrder.selector; 18 | } 19 | 20 | function validateOrder( 21 | ZoneParameters calldata zoneParameters 22 | ) external pure returns (bytes4 validOrderMagicValue) { 23 | if (zoneParameters.consideration[0].identifier == 1) { 24 | return ZoneInterface.validateOrder.selector; 25 | } else { 26 | assembly { 27 | return(0, 0) 28 | } 29 | } 30 | } 31 | 32 | /** 33 | * @dev Returns the metadata for this zone. 34 | */ 35 | function getSeaportMetadata() 36 | external 37 | pure 38 | override 39 | returns ( 40 | string memory name, 41 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 42 | ) 43 | { 44 | schemas = new Schema[](1); 45 | schemas[0].id = 3003; 46 | schemas[0].metadata = new bytes(0); 47 | 48 | return ("BadZone", schemas); 49 | } 50 | 51 | function supportsInterface( 52 | bytes4 interfaceId 53 | ) public view override(ERC165, ZoneInterface) returns (bool) { 54 | return 55 | interfaceId == type(ZoneInterface).interfaceId || 56 | super.supportsInterface(interfaceId); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/foundry/zone/impl/HashCalldataTestZone.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { 5 | Schema, 6 | ZoneParameters 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 10 | 11 | contract HashCalldataTestZone is ZoneInterface { 12 | bytes32 public expectedZoneAuthorizeCalldataHash; 13 | bytes32 public expectedZoneValidateCalldataHash; 14 | 15 | function authorizeOrder( 16 | ZoneParameters calldata zoneParameters 17 | ) public view returns (bytes4) { 18 | // Hash the zone parameters. 19 | bytes32 _expectedZoneHash = bytes32( 20 | keccak256(abi.encode(zoneParameters)) 21 | ); 22 | 23 | if (_expectedZoneHash != expectedZoneAuthorizeCalldataHash) { 24 | revert( 25 | "Zone calldata hash does not match expected zone hash in authorizeOrder" 26 | ); 27 | } 28 | 29 | // Return the authorizeOrder magic value. 30 | return this.authorizeOrder.selector; 31 | } 32 | 33 | /** 34 | * @dev Validates the order with the given `zoneParameters`. Called by 35 | * Consideration whenever any extraData is provided by the caller. 36 | * 37 | * @param zoneParameters The parameters for the order. 38 | * 39 | * @return validOrderMagicValue The validOrder magic value. 40 | */ 41 | function validateOrder( 42 | ZoneParameters calldata zoneParameters 43 | ) external view returns (bytes4 validOrderMagicValue) { 44 | // Hash the zone parameters. 45 | bytes32 _expectedZoneHash = bytes32( 46 | keccak256(abi.encode(zoneParameters)) 47 | ); 48 | 49 | if (_expectedZoneHash != expectedZoneValidateCalldataHash) { 50 | revert( 51 | "Zone calldata hash does not match expected zone hash in validateOrder" 52 | ); 53 | } 54 | 55 | // Return the validOrderMagicValue. 56 | return ZoneInterface.validateOrder.selector; 57 | } 58 | 59 | function setExpectedAuthorizeCalldataHash( 60 | bytes32 _expectedZoneAuthorizeCalldataHash 61 | ) public { 62 | expectedZoneAuthorizeCalldataHash = _expectedZoneAuthorizeCalldataHash; 63 | } 64 | 65 | function setExpectedValidateCalldataHash( 66 | bytes32 _expectedZoneValidateCalldataHash 67 | ) public { 68 | expectedZoneValidateCalldataHash = _expectedZoneValidateCalldataHash; 69 | } 70 | 71 | receive() external payable {} 72 | 73 | function getSeaportMetadata() 74 | external 75 | pure 76 | override(ZoneInterface) 77 | returns (string memory name, Schema[] memory schemas) 78 | { 79 | // Return the metadata. 80 | name = "TestCalldataHashContractOfferer"; 81 | schemas = new Schema[](1); 82 | schemas[0].id = 1337; 83 | schemas[0].metadata = new bytes(0); 84 | } 85 | 86 | /** 87 | * @dev Enable accepting ERC1155 tokens via safeTransfer. 88 | */ 89 | function onERC1155Received( 90 | address, 91 | address, 92 | uint256, 93 | uint256, 94 | bytes calldata 95 | ) external pure returns (bytes4) { 96 | return this.onERC1155Received.selector; 97 | } 98 | 99 | function supportsInterface( 100 | bytes4 interfaceId 101 | ) public view virtual override(ZoneInterface) returns (bool) { 102 | return interfaceId == type(ZoneInterface).interfaceId; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/foundry/zone/impl/TestZone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | ZoneParameters, 6 | Schema 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 10 | 11 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 12 | 13 | contract TestZone is ERC165, ZoneInterface { 14 | function authorizeOrder( 15 | ZoneParameters calldata 16 | ) public pure returns (bytes4) { 17 | return this.authorizeOrder.selector; 18 | } 19 | 20 | // Called by Consideration whenever any extraData is provided by the caller. 21 | function validateOrder( 22 | ZoneParameters calldata 23 | ) external pure returns (bytes4 validOrderMagicValue) { 24 | return ZoneInterface.validateOrder.selector; 25 | } 26 | 27 | /** 28 | * @dev Returns the metadata for this zone. 29 | */ 30 | function getSeaportMetadata() 31 | external 32 | pure 33 | override 34 | returns ( 35 | string memory name, 36 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 37 | ) 38 | { 39 | schemas = new Schema[](1); 40 | schemas[0].id = 3003; 41 | schemas[0].metadata = new bytes(0); 42 | 43 | return ("TestZone", schemas); 44 | } 45 | 46 | function supportsInterface( 47 | bytes4 interfaceId 48 | ) public view override(ERC165, ZoneInterface) returns (bool) { 49 | return 50 | interfaceId == type(ZoneInterface).interfaceId || 51 | super.supportsInterface(interfaceId); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/foundry/zone/impl/VerboseAuthZone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | Schema, 6 | ZoneParameters 7 | } from "seaport-types/src/lib/ConsiderationStructs.sol"; 8 | 9 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 10 | 11 | import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol"; 12 | 13 | contract VerboseAuthZone is ERC165, ZoneInterface { 14 | // Create a mapping of orderHashes to authorized status. 15 | mapping(bytes32 => bool) public orderIsAuthorized; 16 | 17 | bool shouldReturnInvalidMagicValue; 18 | bool shouldRevert; 19 | 20 | event Authorized(bytes32 orderHash); 21 | 22 | event AuthorizeOrderReverted(bytes32 orderHash); 23 | 24 | event AuthorizeOrderNonMagicValue(bytes32 orderHash); 25 | 26 | error OrderNotAuthorized(); 27 | 28 | constructor(bool _shouldReturnInvalidMagicValue, bool _shouldRevert) { 29 | shouldReturnInvalidMagicValue = _shouldReturnInvalidMagicValue; 30 | shouldRevert = _shouldRevert; 31 | } 32 | 33 | function setAuthorizationStatus(bytes32 orderHash, bool status) public { 34 | orderIsAuthorized[orderHash] = status; 35 | } 36 | 37 | function authorizeOrder( 38 | ZoneParameters calldata zoneParameters 39 | ) public returns (bytes4) { 40 | if (!orderIsAuthorized[zoneParameters.orderHash]) { 41 | if (shouldReturnInvalidMagicValue) { 42 | emit AuthorizeOrderNonMagicValue(zoneParameters.orderHash); 43 | 44 | // Return the a value that is not the authorizeOrder magic 45 | // value. 46 | return bytes4(0x12345678); 47 | } 48 | 49 | if (shouldRevert) { 50 | emit AuthorizeOrderReverted(zoneParameters.orderHash); 51 | revert OrderNotAuthorized(); 52 | } 53 | } 54 | 55 | emit Authorized(zoneParameters.orderHash); 56 | 57 | // Return the authorizeOrder magic value. 58 | return this.authorizeOrder.selector; 59 | } 60 | 61 | function validateOrder( 62 | ZoneParameters calldata /* zoneParameters */ 63 | ) external pure returns (bytes4 validOrderMagicValue) { 64 | // Return the validOrderMagicValue. 65 | return ZoneInterface.validateOrder.selector; 66 | } 67 | 68 | function getSeaportMetadata() 69 | external 70 | pure 71 | override 72 | returns ( 73 | string memory name, 74 | Schema[] memory schemas // map to Seaport Improvement Proposal IDs 75 | ) 76 | { 77 | schemas = new Schema[](1); 78 | schemas[0].id = 3003; 79 | schemas[0].metadata = new bytes(0); 80 | 81 | return ("VerboseAuthZone", schemas); 82 | } 83 | 84 | function supportsInterface( 85 | bytes4 interfaceId 86 | ) public view override(ERC165, ZoneInterface) returns (bool) { 87 | return 88 | interfaceId == type(ZoneInterface).interfaceId || 89 | super.supportsInterface(interfaceId); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/getter.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { randomHex } from "./utils/encoding"; 5 | import { faucet } from "./utils/faucet"; 6 | import { seaportFixture } from "./utils/fixtures"; 7 | import { VERSION } from "./utils/helpers"; 8 | 9 | import type { 10 | ConduitControllerInterface, 11 | ConsiderationInterface, 12 | } from "../typechain-types"; 13 | 14 | const { keccak256, toUtf8Bytes } = ethers.utils; 15 | 16 | describe(`Getter tests (Seaport v${VERSION})`, function () { 17 | const { provider } = ethers; 18 | const owner = new ethers.Wallet(randomHex(32), provider); 19 | 20 | let conduitController: ConduitControllerInterface; 21 | let directMarketplaceContract: ConsiderationInterface; 22 | let marketplaceContract: ConsiderationInterface; 23 | 24 | after(async () => { 25 | await network.provider.request({ 26 | method: "hardhat_reset", 27 | }); 28 | }); 29 | 30 | before(async () => { 31 | await faucet(owner.address, provider); 32 | 33 | ({ conduitController, directMarketplaceContract, marketplaceContract } = 34 | await seaportFixture(owner)); 35 | }); 36 | 37 | it("gets correct name", async () => { 38 | const name = await marketplaceContract.name(); 39 | expect(name).to.equal(process.env.REFERENCE ? "Consideration" : "Seaport"); 40 | 41 | const directName = await directMarketplaceContract.name(); 42 | expect(directName).to.equal("Consideration"); 43 | }); 44 | 45 | it("gets correct version, domain separator and conduit controller", async () => { 46 | const name = process.env.REFERENCE ? "Consideration" : "Seaport"; 47 | const { 48 | version, 49 | domainSeparator, 50 | conduitController: controller, 51 | } = await marketplaceContract.information(); 52 | 53 | const typehash = keccak256( 54 | toUtf8Bytes( 55 | "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" 56 | ) 57 | ); 58 | const namehash = keccak256(toUtf8Bytes(name)); 59 | const versionhash = keccak256(toUtf8Bytes(version)); 60 | const { chainId } = await provider.getNetwork(); 61 | const chainIdEncoded = chainId.toString(16).padStart(64, "0"); 62 | const addressEncoded = marketplaceContract.address 63 | .slice(2) 64 | .padStart(64, "0"); 65 | expect(domainSeparator).to.equal( 66 | keccak256( 67 | `0x${typehash.slice(2)}${namehash.slice(2)}${versionhash.slice( 68 | 2 69 | )}${chainIdEncoded}${addressEncoded}` 70 | ) 71 | ); 72 | expect(controller).to.equal(conduitController.address); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/typehashdirectory.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { hexConcat } from "ethers/lib/utils"; 3 | import { ethers } from "hardhat"; 4 | 5 | import { deployContract } from "./utils/contracts"; 6 | import { getBulkOrderTypeHashes } from "./utils/eip712/bulk-orders"; 7 | 8 | // Reference contracts use storage for type hashes, not 9 | // a lookup contract. 10 | if (!process.env.REFERENCE) { 11 | describe("TypehashDirectory", () => { 12 | let address: string; 13 | before(async () => { 14 | address = (await deployContract("TypehashDirectory")).address; 15 | }); 16 | 17 | it("Code is equal to concatenated type hashes for heights 1-24", async () => { 18 | const code = await ethers.provider.getCode(address); 19 | const typeHashes = getBulkOrderTypeHashes(24); 20 | expect(code).to.eq(hexConcat(["0xfe", ...typeHashes])); 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /test/utils/contracts.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | import type { JsonRpcSigner } from "@ethersproject/providers"; 4 | import type { Contract, Wallet } from "ethers"; 5 | 6 | import "dotenv/config"; 7 | 8 | export const deployContract = async ( 9 | name: string, 10 | signer?: JsonRpcSigner | Wallet, 11 | ...args: any[] 12 | ): Promise => { 13 | if (!signer) { 14 | signer = await ethers.provider.getSigner(0); 15 | } 16 | const references = new Map([ 17 | ["Consideration", "ReferenceConsideration"], 18 | ["Conduit", "ReferenceConduit"], 19 | ["ConduitController", "ReferenceConduitController"], 20 | ]); 21 | 22 | const nameWithReference = 23 | process.env.REFERENCE && references.has(name) 24 | ? references.get(name) ?? name 25 | : name; 26 | 27 | const f = await ethers.getContractFactory(nameWithReference, signer); 28 | const c = await f.deploy(...args); 29 | return c as C; 30 | }; 31 | -------------------------------------------------------------------------------- /test/utils/criteria.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | 3 | const { keccak256 } = ethers.utils; 4 | 5 | type BufferElementPositionIndex = { [key: string]: number }; 6 | 7 | export const merkleTree = (tokenIds: ethers.BigNumber[]) => { 8 | const elements = tokenIds 9 | .map((tokenId) => 10 | Buffer.from(tokenId.toHexString().slice(2).padStart(64, "0"), "hex") 11 | ) 12 | .sort(Buffer.compare) 13 | .filter((el, idx, arr) => { 14 | return idx === 0 || !arr[idx - 1].equals(el); 15 | }); 16 | 17 | const bufferElementPositionIndex = elements.reduce( 18 | (memo: BufferElementPositionIndex, el, index) => { 19 | memo["0x" + el.toString("hex")] = index; 20 | return memo; 21 | }, 22 | {} 23 | ); 24 | 25 | // Create layers 26 | const layers = getLayers(elements); 27 | 28 | const root = "0x" + layers[layers.length - 1][0].toString("hex"); 29 | 30 | const proofs = Object.fromEntries( 31 | elements.map((el) => [ 32 | ethers.BigNumber.from(el).toString(), 33 | getHexProof(el, bufferElementPositionIndex, layers), 34 | ]) 35 | ); 36 | 37 | const maxProofLength = Math.max( 38 | ...Object.values(proofs).map((i) => i.length) 39 | ); 40 | 41 | return { 42 | root, 43 | proofs, 44 | maxProofLength, 45 | }; 46 | }; 47 | 48 | const getLayers = (elements: Buffer[]) => { 49 | if (elements.length === 0) { 50 | throw new Error("empty tree"); 51 | } 52 | 53 | const layers = []; 54 | layers.push(elements.map((el) => Buffer.from(keccak256(el).slice(2), "hex"))); 55 | 56 | // Get next layer until we reach the root 57 | while (layers[layers.length - 1].length > 1) { 58 | layers.push(getNextLayer(layers[layers.length - 1])); 59 | } 60 | 61 | return layers; 62 | }; 63 | 64 | const getNextLayer = (elements: Buffer[]) => { 65 | return elements.reduce((layer: Buffer[], el, idx, arr) => { 66 | if (idx % 2 === 0) { 67 | // Hash the current element with its pair element 68 | layer.push(combinedHash(el, arr[idx + 1])); 69 | } 70 | 71 | return layer; 72 | }, []); 73 | }; 74 | 75 | const combinedHash = (first: Buffer, second: Buffer) => { 76 | if (!first) { 77 | return second; 78 | } 79 | if (!second) { 80 | return first; 81 | } 82 | 83 | return Buffer.from( 84 | keccak256(Buffer.concat([first, second].sort(Buffer.compare))).slice(2), 85 | "hex" 86 | ); 87 | }; 88 | 89 | const getHexProof = ( 90 | el: Buffer, 91 | bufferElementPositionIndex: BufferElementPositionIndex, 92 | layers: Buffer[][] 93 | ) => { 94 | let idx = bufferElementPositionIndex["0x" + el.toString("hex")]; 95 | 96 | if (typeof idx !== "number") { 97 | throw new Error("Element does not exist in Merkle tree"); 98 | } 99 | 100 | const proofBuffer = layers.reduce((proof: Buffer[], layer) => { 101 | const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1; 102 | const pairElement = pairIdx < layer.length ? layer[pairIdx] : null; 103 | 104 | if (pairElement) { 105 | proof.push(pairElement); 106 | } 107 | 108 | idx = Math.floor(idx / 2); 109 | 110 | return proof; 111 | }, []); 112 | 113 | return proofBuffer.map((el) => "0x" + el.toString("hex")); 114 | }; 115 | -------------------------------------------------------------------------------- /test/utils/eip712/bulk-orders.ts: -------------------------------------------------------------------------------- 1 | import { _TypedDataEncoder, keccak256, toUtf8Bytes } from "ethers/lib/utils"; 2 | 3 | import { Eip712MerkleTree } from "./Eip712MerkleTree"; 4 | import { DefaultGetter } from "./defaults"; 5 | import { fillArray } from "./utils"; 6 | 7 | import type { OrderComponents } from "../types"; 8 | import type { EIP712TypeDefinitions } from "./defaults"; 9 | 10 | const { bulkOrderType } = require("../../../eip-712-types/bulkOrder.js"); 11 | 12 | function getBulkOrderTypes(height: number): EIP712TypeDefinitions { 13 | const types = { ...bulkOrderType }; 14 | types.BulkOrder = [ 15 | { name: "tree", type: `OrderComponents${`[2]`.repeat(height)}` }, 16 | ]; 17 | return types; 18 | } 19 | 20 | export function getBulkOrderTreeHeight(length: number): number { 21 | return Math.max(Math.ceil(Math.log2(length)), 1); 22 | } 23 | 24 | export function getBulkOrderTree( 25 | orderComponents: OrderComponents[], 26 | startIndex = 0, 27 | height = getBulkOrderTreeHeight(orderComponents.length + startIndex) 28 | ) { 29 | const types = getBulkOrderTypes(height); 30 | const defaultNode = DefaultGetter.from(types, "OrderComponents"); 31 | let elements = [...orderComponents]; 32 | 33 | if (startIndex > 0) { 34 | elements = [ 35 | ...fillArray([] as OrderComponents[], startIndex, defaultNode), 36 | ...orderComponents, 37 | ]; 38 | } 39 | const tree = new Eip712MerkleTree( 40 | types, 41 | "BulkOrder", 42 | "OrderComponents", 43 | elements, 44 | height 45 | ); 46 | return tree; 47 | } 48 | 49 | export function getBulkOrderTypeHash(height: number): string { 50 | const types = getBulkOrderTypes(height); 51 | const encoder = _TypedDataEncoder.from(types); 52 | const typeString = toUtf8Bytes(encoder._types.BulkOrder); 53 | return keccak256(typeString); 54 | } 55 | 56 | export function getBulkOrderTypeHashes(maxHeight: number): string[] { 57 | const typeHashes: string[] = []; 58 | for (let i = 0; i < maxHeight; i++) { 59 | typeHashes.push(getBulkOrderTypeHash(i + 1)); 60 | } 61 | return typeHashes; 62 | } 63 | -------------------------------------------------------------------------------- /test/utils/eip712/defaults.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-dupe-class-members */ 2 | /* eslint-disable no-unused-vars */ 3 | import { Logger } from "@ethersproject/logger"; 4 | import { hexZeroPad } from "ethers/lib/utils"; 5 | 6 | import type { TypedDataField } from "@ethersproject/abstract-signer"; 7 | 8 | const logger = new Logger("defaults"); 9 | 10 | const baseDefaults: Record = { 11 | integer: 0, 12 | address: hexZeroPad("0x", 20), 13 | bool: false, 14 | bytes: "0x", 15 | string: "", 16 | }; 17 | 18 | const isNullish = (value: any): boolean => { 19 | if (value === undefined) return false; 20 | 21 | return ( 22 | value !== undefined && 23 | value !== null && 24 | ((["string", "number"].includes(typeof value) && 25 | BigInt(value) === BigInt(0)) || 26 | (Array.isArray(value) && value.every(isNullish)) || 27 | (typeof value === "object" && Object.values(value).every(isNullish)) || 28 | (typeof value === "boolean" && value === false)) 29 | ); 30 | }; 31 | 32 | function getDefaultForBaseType(type: string): any { 33 | // bytesXX 34 | const [, width] = type.match(/^bytes(\d+)$/) ?? []; 35 | if (width) return hexZeroPad("0x", parseInt(width)); 36 | 37 | if (type.match(/^(u?)int(\d*)$/)) type = "integer"; 38 | 39 | return baseDefaults[type]; 40 | } 41 | 42 | export type EIP712TypeDefinitions = Record; 43 | 44 | type DefaultMap = { 45 | [K in keyof T]: any; 46 | }; 47 | 48 | export class DefaultGetter { 49 | defaultValues: DefaultMap = {} as DefaultMap; 50 | 51 | constructor(protected types: Types) { 52 | for (const name in types) { 53 | const defaultValue = this.getDefaultValue(name); 54 | this.defaultValues[name] = defaultValue; 55 | if (!isNullish(defaultValue)) { 56 | logger.throwError( 57 | `Got non-empty value for type ${name} in default generator: ${defaultValue}` 58 | ); 59 | } 60 | } 61 | } 62 | 63 | static from( 64 | types: Types 65 | ): DefaultMap; 66 | 67 | static from( 68 | types: Types, 69 | type: keyof Types 70 | ): any; 71 | 72 | static from( 73 | types: Types, 74 | type?: keyof Types 75 | ): DefaultMap { 76 | const { defaultValues } = new DefaultGetter(types); 77 | if (type) return defaultValues[type]; 78 | return defaultValues; 79 | } 80 | 81 | getDefaultValue(type: string): any { 82 | if (this.defaultValues[type]) return this.defaultValues[type]; 83 | // Basic type (address, bool, uint256, etc) 84 | const basic = getDefaultForBaseType(type); 85 | if (basic !== undefined) return basic; 86 | 87 | // Array 88 | const match = type.match(/^(.*)(\x5b(\d*)\x5d)$/); 89 | if (match) { 90 | const subtype = match[1]; 91 | const length = parseInt(match[3]); 92 | if (length > 0) { 93 | const baseValue = this.getDefaultValue(subtype); 94 | return Array(length).fill(baseValue); 95 | } 96 | return []; 97 | } 98 | 99 | // Struct 100 | const fields = this.types[type]; 101 | if (fields) { 102 | return fields.reduce( 103 | (obj, { name, type }) => ({ 104 | ...obj, 105 | [name]: this.getDefaultValue(type), 106 | }), 107 | {} 108 | ); 109 | } 110 | 111 | return logger.throwArgumentError(`unknown type: ${type}`, "type", type); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/utils/eip712/utils.ts: -------------------------------------------------------------------------------- 1 | import { hexConcat, hexlify, keccak256 } from "ethers/lib/utils"; 2 | 3 | import type { BytesLike } from "ethers"; 4 | 5 | export const makeArray = (len: number, getValue: (i: number) => T) => 6 | Array(len) 7 | .fill(0) 8 | .map((_, i) => getValue(i)); 9 | 10 | export const chunk = (array: T[], size: number) => { 11 | return makeArray(Math.ceil(array.length / size), (i) => 12 | array.slice(i * size, (i + 1) * size) 13 | ); 14 | }; 15 | 16 | export const bufferToHex = (buf: Buffer) => hexlify(buf); 17 | 18 | export const hexToBuffer = (value: string) => 19 | Buffer.from(value.slice(2), "hex"); 20 | 21 | export const bufferKeccak = (value: BytesLike) => hexToBuffer(keccak256(value)); 22 | 23 | export const hashConcat = (arr: BytesLike[]) => bufferKeccak(hexConcat(arr)); 24 | 25 | export const fillArray = (arr: T[], length: number, value: T) => { 26 | if (length > arr.length) arr.push(...Array(length - arr.length).fill(value)); 27 | return arr; 28 | }; 29 | 30 | export const getRoot = (elements: (Buffer | string)[], hashLeaves = true) => { 31 | if (elements.length === 0) throw new Error("empty tree"); 32 | 33 | const leaves = elements.map((e) => { 34 | const leaf = Buffer.isBuffer(e) ? e : hexToBuffer(e); 35 | return hashLeaves ? bufferKeccak(leaf) : leaf; 36 | }); 37 | 38 | const layers: Buffer[][] = [leaves]; 39 | 40 | // Get next layer until we reach the root 41 | while (layers[layers.length - 1].length > 1) { 42 | layers.push(getNextLayer(layers[layers.length - 1])); 43 | } 44 | 45 | return layers[layers.length - 1][0]; 46 | }; 47 | 48 | export const getNextLayer = (elements: Buffer[]) => { 49 | return chunk(elements, 2).map(hashConcat); 50 | // return elements.reduce((layer: Buffer[], el, idx, arr) => { 51 | // if (idx % 2 === 0) layer.push(hashConcat(el, arr[idx + 1])); 52 | // return layer; 53 | // }, []); 54 | }; 55 | -------------------------------------------------------------------------------- /test/utils/events.ts: -------------------------------------------------------------------------------- 1 | import type { ContractTransaction } from "ethers"; 2 | import type { Interface } from "ethers/lib/utils"; 3 | 4 | type DecodedTransactionEvent = { 5 | eventName: string; 6 | data: { [key: string | number]: string | number | boolean }; 7 | }; 8 | 9 | type EventDecoder = { 10 | eventName: string; 11 | contract: { interface: Interface }; 12 | }; 13 | 14 | export async function decodeEvents( 15 | tx: ContractTransaction, 16 | eventDecoders: EventDecoder[] 17 | ): Promise { 18 | const receipt = await tx.wait(); 19 | const events = receipt.events; 20 | if (events == null) { 21 | return []; 22 | } 23 | 24 | const decodedEvents = events 25 | .map((event) => { 26 | for (const decoder of eventDecoders) { 27 | // Attempt to decode each event as decoder.eventName. 28 | // If the event is not successfully decoded (e.g. if the 29 | // event is not an event with name decoder.eventName), 30 | // the catch will be hit. 31 | try { 32 | const result = decoder.contract.interface.decodeEventLog( 33 | decoder.eventName, 34 | event.data, 35 | event.topics 36 | ); 37 | return { 38 | eventName: decoder.eventName, 39 | data: result, 40 | } as DecodedTransactionEvent; 41 | } catch {} 42 | } 43 | // Event was not decoded by any decoder so return null. 44 | return null; 45 | }) 46 | // Filter out all nulls so that at the end we are left with 47 | // only successfully decoded events. 48 | .filter(Boolean); 49 | return decodedEvents as DecodedTransactionEvent[]; 50 | } 51 | -------------------------------------------------------------------------------- /test/utils/faucet.ts: -------------------------------------------------------------------------------- 1 | import { parseEther } from "@ethersproject/units"; 2 | import { ethers } from "hardhat"; 3 | 4 | import { randomHex } from "./encoding"; 5 | 6 | import type { JsonRpcProvider } from "@ethersproject/providers"; 7 | 8 | const TEN_THOUSAND_ETH = parseEther("10000").toHexString().replace("0x0", "0x"); 9 | 10 | export const faucet = async (address: string, provider: JsonRpcProvider) => { 11 | await provider.send("hardhat_setBalance", [address, TEN_THOUSAND_ETH]); 12 | }; 13 | 14 | export const getWalletWithEther = async () => { 15 | const wallet = new ethers.Wallet(randomHex(32), ethers.provider); 16 | await faucet(wallet.address, ethers.provider); 17 | return wallet; 18 | }; 19 | -------------------------------------------------------------------------------- /test/utils/fixtures/create2.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import hre, { ethers } from "hardhat"; 3 | 4 | import { faucet } from "../faucet"; 5 | 6 | import type { ImmutableCreate2FactoryInterface } from "../../../typechain-types"; 7 | import type { Wallet } from "ethers"; 8 | 9 | const deployConstants = require("../../../constants/constants"); 10 | 11 | export const create2FactoryFixture = async (owner: Wallet) => { 12 | // Deploy keyless create2 deployer 13 | await faucet( 14 | deployConstants.KEYLESS_CREATE2_DEPLOYER_ADDRESS, 15 | ethers.provider 16 | ); 17 | await ethers.provider.sendTransaction( 18 | deployConstants.KEYLESS_CREATE2_DEPLOYMENT_TRANSACTION 19 | ); 20 | let deployedCode = await ethers.provider.getCode( 21 | deployConstants.KEYLESS_CREATE2_ADDRESS 22 | ); 23 | expect(deployedCode).to.equal(deployConstants.KEYLESS_CREATE2_RUNTIME_CODE); 24 | 25 | let { gasLimit } = await ethers.provider.getBlock("latest"); 26 | 27 | if ((hre as any).__SOLIDITY_COVERAGE_RUNNING) { 28 | gasLimit = ethers.BigNumber.from(300_000_000); 29 | } 30 | 31 | // Deploy inefficient deployer through keyless 32 | await owner.sendTransaction({ 33 | to: deployConstants.KEYLESS_CREATE2_ADDRESS, 34 | data: deployConstants.IMMUTABLE_CREATE2_FACTORY_CREATION_CODE, 35 | gasLimit, 36 | }); 37 | deployedCode = await ethers.provider.getCode( 38 | deployConstants.INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS 39 | ); 40 | expect(ethers.utils.keccak256(deployedCode)).to.equal( 41 | deployConstants.IMMUTABLE_CREATE2_FACTORY_RUNTIME_HASH 42 | ); 43 | 44 | const inefficientFactory = await ethers.getContractAt( 45 | "ImmutableCreate2FactoryInterface", 46 | deployConstants.INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS, 47 | owner 48 | ); 49 | 50 | // Deploy effecient deployer through inefficient deployer 51 | await inefficientFactory 52 | .connect(owner) 53 | .safeCreate2( 54 | deployConstants.IMMUTABLE_CREATE2_FACTORY_SALT, 55 | deployConstants.IMMUTABLE_CREATE2_FACTORY_CREATION_CODE, 56 | { 57 | gasLimit, 58 | } 59 | ); 60 | 61 | deployedCode = await ethers.provider.getCode( 62 | deployConstants.IMMUTABLE_CREATE2_FACTORY_ADDRESS 63 | ); 64 | expect(ethers.utils.keccak256(deployedCode)).to.equal( 65 | deployConstants.IMMUTABLE_CREATE2_FACTORY_RUNTIME_HASH 66 | ); 67 | const create2Factory: ImmutableCreate2FactoryInterface = 68 | await ethers.getContractAt( 69 | "ImmutableCreate2FactoryInterface", 70 | deployConstants.IMMUTABLE_CREATE2_FACTORY_ADDRESS, 71 | owner 72 | ); 73 | 74 | return create2Factory; 75 | }; 76 | -------------------------------------------------------------------------------- /test/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | 3 | import { randomBN } from "./encoding"; 4 | 5 | import type { 6 | AdvancedOrder, 7 | CriteriaResolver, 8 | Fulfillment, 9 | Order, 10 | } from "./types"; 11 | 12 | export const VERSION = `1.6${process.env.REFERENCE ? "-reference" : ""}`; 13 | 14 | export const minRandom = (min: ethers.BigNumberish) => randomBN(10).add(min); 15 | 16 | export const getCustomRevertSelector = (customErrorString: string) => 17 | ethers.utils 18 | .keccak256(ethers.utils.toUtf8Bytes(customErrorString)) 19 | .slice(0, 10); 20 | 21 | export const simulateMatchOrders = async ( 22 | marketplaceContract: ethers.Contract, 23 | orders: Order[], 24 | fulfillments: Fulfillment[], 25 | caller: ethers.Wallet, 26 | value: ethers.BigNumberish 27 | ) => 28 | marketplaceContract 29 | .connect(caller) 30 | .callStatic.matchOrders(orders, fulfillments, { 31 | value, 32 | }); 33 | 34 | export const simulateAdvancedMatchOrders = async ( 35 | marketplaceContract: ethers.Contract, 36 | orders: AdvancedOrder[], 37 | criteriaResolvers: CriteriaResolver[], 38 | fulfillments: Fulfillment[], 39 | caller: ethers.Wallet, 40 | value: ethers.BigNumberish, 41 | recipient: string = ethers.constants.AddressZero 42 | ) => 43 | marketplaceContract 44 | .connect(caller) 45 | .callStatic.matchAdvancedOrders( 46 | orders, 47 | criteriaResolvers, 48 | fulfillments, 49 | recipient, 50 | { 51 | value, 52 | } 53 | ); 54 | -------------------------------------------------------------------------------- /test/utils/reports/comment-table.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | const err = chalk.bold.red; 4 | const warn = chalk.hex("#FFA500"); 5 | const info = chalk.blue; 6 | const success = chalk.green; 7 | 8 | export function diffPctString( 9 | newValue: number, 10 | oldValue: number, 11 | warnOnIncrease?: boolean, 12 | diffOnly?: boolean 13 | ): string { 14 | if ([newValue, oldValue].every(isNaN)) { 15 | return warn("null"); 16 | } 17 | const diff = newValue - oldValue; 18 | 19 | if (diff === 0) return info(newValue.toString()); 20 | const pct = +((100 * diff) / oldValue).toFixed(2); 21 | const prefix = pct > 0 ? "+" : ""; 22 | const color = diff > 0 ? (warnOnIncrease ? warn : err) : success; 23 | const value = diffOnly ? diff : newValue; 24 | return `${value} (${color(`${prefix}${pct}%`)})`; 25 | } 26 | // eslint-disable-next-line no-control-regex 27 | const stripANSI = (str: string) => str.replace(/\u001b\[.*?m/g, ""); 28 | 29 | export function getColumnSizesAndAlignments( 30 | rows: string[][], 31 | padding = 0 32 | ): Array<[number, boolean]> { 33 | const sizesAndAlignments: Array<[number, boolean]> = []; 34 | const numColumns = rows[0].length; 35 | for (let i = 0; i < numColumns; i++) { 36 | const entries = rows.map((row) => stripANSI(row[i])); 37 | const maxSize = Math.max(...entries.map((e) => e.length)); 38 | const alignLeft = entries.slice(1).some((e) => !!e.match(/[a-zA-Z]/g)); 39 | sizesAndAlignments.push([maxSize + padding, alignLeft]); 40 | } 41 | return sizesAndAlignments; 42 | } 43 | 44 | const padColumn = ( 45 | col: string, 46 | size: number, 47 | padWith: string, 48 | alignLeft: boolean 49 | ) => { 50 | const padSize = Math.max(0, size - stripANSI(col).length); 51 | const padding = padWith.repeat(padSize); 52 | if (alignLeft) return `${col}${padding}`; 53 | return `${padding}${col}`; 54 | }; 55 | 56 | export const toCommentTable = (rows: string[][]): string[] => { 57 | const sizesAndAlignments = getColumnSizesAndAlignments(rows); 58 | rows.forEach((row) => { 59 | row.forEach((col, c) => { 60 | const [size, alignLeft] = sizesAndAlignments[c]; 61 | row[c] = padColumn(col, size, " ", alignLeft); 62 | }); 63 | }); 64 | 65 | const completeRows = rows.map((row) => `| ${row.join(" | ")} |`); 66 | const rowSeparator = `==${sizesAndAlignments 67 | .map(([size]) => "=".repeat(size)) 68 | .join("===")}==`; 69 | completeRows.splice(1, 0, rowSeparator); 70 | completeRows.unshift(rowSeparator); 71 | completeRows.push(rowSeparator); 72 | return completeRows; 73 | }; 74 | -------------------------------------------------------------------------------- /test/utils/reports/report_parser.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | import { diffPctString, toCommentTable } from "./comment-table"; 5 | 6 | type GasReport = { 7 | contract: string; 8 | method: string; 9 | min: number; 10 | max: number; 11 | avg: number; 12 | calls: number; 13 | }; 14 | 15 | function parseReport(text: string): GasReport[] { 16 | const lines = text 17 | .split("\n") 18 | .slice(6) 19 | .filter((ln) => ln.indexOf("·") !== 0); 20 | const rows = lines 21 | .map((ln) => ln.replace(/\|/g, "").replace(/\s/g, "").split("·")) 22 | .filter((row) => row.length === 7) 23 | .map(([contract, method, min, max, avg, calls]) => ({ 24 | contract, 25 | method, 26 | min: +min, 27 | max: +max, 28 | avg: +avg, 29 | calls: +calls, 30 | })); 31 | return rows; 32 | } 33 | 34 | function parseReportFile(fileName: string, write?: boolean) { 35 | const text = fs.readFileSync(path.join(__dirname, fileName), "utf8"); 36 | const report = parseReport(text); 37 | if (write) { 38 | fs.writeFileSync( 39 | path.join(__dirname, fileName.replace(".md", ".json")), 40 | JSON.stringify(report, null, 2) 41 | ); 42 | } 43 | return report; 44 | } 45 | 46 | export function compareReports(report1: GasReport[], report2: GasReport[]) { 47 | const rows: string[][] = []; 48 | rows.push([`contract`, `method`, `min`, `max`, `avg`]); 49 | report1.forEach((r1, i) => { 50 | if (r1.contract !== "Seaport") return; 51 | const r2 = report2[i]; 52 | if (r1.contract !== r2.contract || r1.method !== r2.method) { 53 | throw new Error("contract and method for comparison do not match"); 54 | } 55 | rows.push([ 56 | r1.contract, 57 | r1.method, 58 | diffPctString(r2.min, r1.min, false, true), 59 | diffPctString(r2.max, r1.max, false, true), 60 | diffPctString(r2.avg, r1.avg, false, true), 61 | ]); 62 | }); 63 | console.log(toCommentTable(rows).join("\n")); 64 | } 65 | 66 | export function compareReportFiles( 67 | name1: string, 68 | name2: string, 69 | write?: boolean 70 | ) { 71 | const report1 = parseReportFile(name1, write); 72 | const report2 = parseReportFile(name2, write); 73 | compareReports(report1, report2); 74 | } 75 | -------------------------------------------------------------------------------- /test/utils/types.ts: -------------------------------------------------------------------------------- 1 | import type { BigNumber } from "ethers"; 2 | 3 | export type AdditionalRecipient = { 4 | amount: BigNumber; 5 | recipient: string; 6 | }; 7 | 8 | export type FulfillmentComponent = { 9 | orderIndex: number; 10 | itemIndex: number; 11 | }; 12 | 13 | export type Fulfillment = { 14 | offerComponents: FulfillmentComponent[]; 15 | considerationComponents: FulfillmentComponent[]; 16 | }; 17 | 18 | export type CriteriaResolver = { 19 | orderIndex: number; 20 | side: 0 | 1; 21 | index: number; 22 | identifier: BigNumber; 23 | criteriaProof: string[]; 24 | }; 25 | 26 | export type BasicOrderParameters = { 27 | considerationToken: string; 28 | considerationIdentifier: BigNumber; 29 | considerationAmount: BigNumber; 30 | offerer: string; 31 | zone: string; 32 | offerToken: string; 33 | offerIdentifier: BigNumber; 34 | offerAmount: BigNumber; 35 | basicOrderType: number; 36 | startTime: string | BigNumber | number; 37 | endTime: string | BigNumber | number; 38 | zoneHash: string; 39 | salt: string; 40 | offererConduitKey: string; 41 | fulfillerConduitKey: string; 42 | totalOriginalAdditionalRecipients: BigNumber; 43 | additionalRecipients: AdditionalRecipient[]; 44 | signature: string; 45 | }; 46 | 47 | export type OfferItem = { 48 | itemType: number; 49 | token: string; 50 | identifierOrCriteria: BigNumber; 51 | startAmount: BigNumber; 52 | endAmount: BigNumber; 53 | }; 54 | export type ConsiderationItem = { 55 | itemType: number; 56 | token: string; 57 | identifierOrCriteria: BigNumber; 58 | startAmount: BigNumber; 59 | endAmount: BigNumber; 60 | recipient: string; 61 | }; 62 | 63 | export type OrderParameters = { 64 | offerer: string; 65 | zone: string; 66 | offer: OfferItem[]; 67 | consideration: ConsiderationItem[]; 68 | orderType: number; 69 | startTime: string | BigNumber | number; 70 | endTime: string | BigNumber | number; 71 | zoneHash: string; 72 | salt: string; 73 | conduitKey: string; 74 | totalOriginalConsiderationItems: string | BigNumber | number; 75 | }; 76 | 77 | export type OrderComponents = Omit< 78 | OrderParameters, 79 | "totalOriginalConsiderationItems" 80 | > & { 81 | counter: BigNumber; 82 | }; 83 | 84 | export type Order = { 85 | parameters: OrderParameters; 86 | signature: string; 87 | }; 88 | 89 | export type AdvancedOrder = { 90 | parameters: OrderParameters; 91 | numerator: string | BigNumber | number; 92 | denominator: string | BigNumber | number; 93 | signature: string; 94 | extraData: string; 95 | }; 96 | 97 | export type BulkOrder = { 98 | tree: Array>>>>>>; 99 | }; 100 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist", 8 | "declaration": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": [ 12 | "./scripts", 13 | "./test", 14 | "./typechain-types", 15 | "./eip-712-types", 16 | "./*.config.ts", 17 | "./docs/prepare-docs.js" 18 | ], 19 | "files": ["./hardhat.config.ts"] 20 | } 21 | --------------------------------------------------------------------------------