├── .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 | 
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 | Function Name |
15 | Signature (1.2) |
16 | Signature (1.1) |
17 |
18 |
19 | fulfillOrder |
20 | 0xb3a34c4c |
21 | 0xb3a34c4c |
22 |
23 |
24 | fulfillAdvancedOrder |
25 | 0xe7acab24 |
26 | 0xe7acab24 |
27 |
28 |
29 | matchOrders |
30 | 0xa8174404 |
31 | 0xa8174404 |
32 |
33 |
34 | matchAdvancedOrders |
35 | 0xf2d12b12 |
36 | 0x55944a42 |
37 |
38 |
39 | fulfillAvailableOrders |
40 | 0xed98a574 |
41 | 0xed98a574 |
42 |
43 |
44 | fulfillAvailableAdvancedOrders |
45 | 0x87201b41 |
46 | 0x87201b41 |
47 |
48 |
49 | fulfillBasicOrder |
50 | 0xfb0f3ee1 |
51 | 0xfb0f3ee1 |
52 |
53 |
54 | fulfillBasicOrder_efficient_6GL6yc |
55 | 0x00000000 |
56 | n/a |
57 |
58 |
59 | cancel |
60 | 0xfd9f1e10 |
61 | 0xfd9f1e10 |
62 |
63 |
64 | validate |
65 | 0x88147732 |
66 | 0x88147732 |
67 |
68 |
69 | incrementCounter |
70 | 0x5b34b966 |
71 | 0x5b34b966 |
72 |
73 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------