diff --git a/.eslintignore b/.eslintignore
index 44358150a..a401ad257 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -5,4 +5,5 @@ cache
constants
coverage
lib/murky
-lib/openzeppelin-contracts
\ No newline at end of file
+lib/openzeppelin-contracts
+order-validator/test
\ No newline at end of file
diff --git a/.gas_reports/bb5d39539354e96cf6e69ad107906a0b6a46ea91.json b/.gas_reports/10d78fe57eb635a6d8a85e34236aa29548a1b52f.json
similarity index 87%
rename from .gas_reports/bb5d39539354e96cf6e69ad107906a0b6a46ea91.json
rename to .gas_reports/10d78fe57eb635a6d8a85e34236aa29548a1b52f.json
index d385bfc4d..5cf34e559 100644
--- a/.gas_reports/bb5d39539354e96cf6e69ad107906a0b6a46ea91.json
+++ b/.gas_reports/10d78fe57eb635a6d8a85e34236aa29548a1b52f.json
@@ -1,5 +1,5 @@
{
- "commitHash": "bb5d39539354e96cf6e69ad107906a0b6a46ea91",
+ "commitHash": "10d78fe57eb635a6d8a85e34236aa29548a1b52f",
"contractReports": {
"Conduit": {
"name": "Conduit",
@@ -7,8 +7,8 @@
{
"method": "execute",
"min": 77435,
- "max": 2179914,
- "avg": 451436,
+ "max": 2232387,
+ "avg": 460181,
"calls": 6
},
{
@@ -21,8 +21,8 @@
{
"method": "executeWithBatch1155",
"min": 97669,
- "max": 361418,
- "avg": 228749,
+ "max": 361430,
+ "avg": 228746,
"calls": 4
},
{
@@ -71,7 +71,7 @@
"method": "updateChannel",
"min": 34454,
"max": 121098,
- "avg": 117293,
+ "avg": 117294,
"calls": 71
}
],
@@ -106,7 +106,7 @@
"method": "registerDigest",
"min": 22239,
"max": 44151,
- "avg": 36843,
+ "avg": 36847,
"calls": 3
},
{
@@ -148,7 +148,7 @@
"method": "cancelOrders",
"min": null,
"max": null,
- "avg": 65327,
+ "avg": 65339,
"calls": 1
}
],
@@ -176,14 +176,14 @@
"method": "assignPauser",
"min": null,
"max": null,
- "avg": 47183,
+ "avg": 47171,
"calls": 1
},
{
"method": "cancelOrders",
"min": null,
"max": null,
- "avg": 73870,
+ "avg": 73894,
"calls": 1
},
{
@@ -195,23 +195,23 @@
},
{
"method": "createZone",
- "min": 1154302,
- "max": 1154314,
- "avg": 1154312,
+ "min": null,
+ "max": null,
+ "avg": 1154314,
"calls": 31
},
{
"method": "executeMatchAdvancedOrders",
"min": null,
"max": null,
- "avg": 289151,
+ "avg": 289127,
"calls": 2
},
{
"method": "executeMatchOrders",
"min": null,
"max": null,
- "avg": 282751,
+ "avg": 282727,
"calls": 2
},
{
@@ -225,7 +225,7 @@
"method": "transferOwnership",
"min": null,
"max": null,
- "avg": 47199,
+ "avg": 47187,
"calls": 2
}
],
@@ -238,8 +238,8 @@
{
"method": "prepare",
"min": 49267,
- "max": 2351690,
- "avg": 1061771,
+ "max": 2351702,
+ "avg": 1061791,
"calls": 26
}
],
@@ -251,37 +251,37 @@
"methods": [
{
"method": "cancel",
- "min": 41226,
- "max": 58374,
- "avg": 54030,
+ "min": 41250,
+ "max": 58422,
+ "avg": 54038,
"calls": 16
},
{
"method": "fulfillAdvancedOrder",
"min": 96288,
"max": 225181,
- "avg": 159350,
+ "avg": 160736,
"calls": 194
},
{
"method": "fulfillAvailableAdvancedOrders",
"min": 149626,
- "max": 260680,
- "avg": 203090,
+ "max": 305236,
+ "avg": 209036,
"calls": 30
},
{
"method": "fulfillAvailableOrders",
"min": 164986,
"max": 215752,
- "avg": 201359,
+ "avg": 201368,
"calls": 21
},
{
"method": "fulfillBasicOrder",
"min": 90639,
"max": 1621627,
- "avg": 598705,
+ "avg": 598707,
"calls": 187
},
{
@@ -295,7 +295,7 @@
"method": "fulfillOrder",
"min": 119409,
"max": 225080,
- "avg": 176770,
+ "avg": 178008,
"calls": 108
},
{
@@ -308,41 +308,41 @@
{
"method": "matchAdvancedOrders",
"min": 179574,
- "max": 300473,
- "avg": 248933,
- "calls": 77
+ "max": 300485,
+ "avg": 249389,
+ "calls": 75
},
{
"method": "matchOrders",
"min": 157522,
"max": 348207,
- "avg": 264552,
- "calls": 151
+ "avg": 264801,
+ "calls": 149
},
{
"method": "validate",
"min": 53201,
- "max": 83910,
- "avg": 73546,
+ "max": 83922,
+ "avg": 73545,
"calls": 29
}
],
- "bytecodeSize": 26114,
- "deployedBytecodeSize": 24389
+ "bytecodeSize": 26128,
+ "deployedBytecodeSize": 24403
},
"SeaportRouter": {
"name": "SeaportRouter",
"methods": [
{
"method": "fulfillAvailableAdvancedOrders",
- "min": 183954,
- "max": 311883,
- "avg": 233586,
+ "min": 183796,
+ "max": 311939,
+ "avg": 233615,
"calls": 6
}
],
- "bytecodeSize": 6345,
- "deployedBytecodeSize": 6158
+ "bytecodeSize": 6417,
+ "deployedBytecodeSize": 6230
},
"TestContractOfferer": {
"name": "TestContractOfferer",
@@ -400,15 +400,15 @@
"method": "mint",
"min": 47235,
"max": 49879,
- "avg": 49421,
- "calls": 236
+ "avg": 49474,
+ "calls": 273
},
{
"method": "setApprovalForAll",
"min": 26102,
"max": 46002,
- "avg": 45380,
- "calls": 448
+ "avg": 45468,
+ "calls": 522
}
],
"bytecodeSize": 4173,
@@ -421,8 +421,8 @@
"method": "approve",
"min": 28881,
"max": 46245,
- "avg": 45780,
- "calls": 338
+ "avg": 45749,
+ "calls": 292
},
{
"method": "blockTransfer",
@@ -433,10 +433,10 @@
},
{
"method": "mint",
- "min": 33982,
+ "min": 33994,
"max": 68458,
- "avg": 67466,
- "calls": 164
+ "avg": 67348,
+ "calls": 141
},
{
"method": "setNoReturnData",
@@ -456,15 +456,15 @@
"method": "mint",
"min": 51492,
"max": 68796,
- "avg": 66042,
- "calls": 315
+ "avg": 65926,
+ "calls": 301
},
{
"method": "setApprovalForAll",
"min": 26195,
"max": 46095,
- "avg": 45578,
- "calls": 540
+ "avg": 45550,
+ "calls": 512
}
],
"bytecodeSize": 5238,
@@ -504,8 +504,8 @@
{
"method": "bulkTransfer",
"min": 77935,
- "max": 1492854,
- "avg": 664509,
+ "max": 1438866,
+ "avg": 632608,
"calls": 3
}
],
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..cb3132aa4
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,19 @@
+name: "Close stale issues"
+on:
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v8
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ 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."
+ 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."
+ days-before-stale: 60
+ days-before-close: 14
+ operations-per-run: 100
+ exempt-pr-labels: "work-in-progress"
+ exempt-issue-labels: "work-in-progress"
diff --git a/.gitignore b/.gitignore
index 0db299cf3..94b5da0e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,8 @@ optimized-out
reference-working
offerers-out
+#foundry script run output files
+broadcast/
.DS_Store
@@ -34,4 +36,10 @@ html
lcov.info
test/utils/eip712/gen.sol
-.gas_reports/*.md
\ No newline at end of file
+.gas_reports/*.md
+
+# fuzz metrics
+metrics.txt
+*-metrics.txt
+
+fuzz_debug.json
diff --git a/.gitmodules b/.gitmodules
index 4fcf68b13..b0ba74548 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,3 @@
-[submodule "lib/forge-std"]
- path = lib/forge-std
- url = https://github.com/foundry-rs/forge-std
-[submodule "lib/ds-test"]
- path = lib/ds-test
- url = https://github.com/dapphub/ds-test
[submodule "lib/murky"]
path = lib/murky
url = https://github.com/dmfxyz/murky
@@ -13,3 +7,17 @@
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
+[submodule "lib/forge-std"]
+ path = lib/forge-std
+ url = https://github.com/foundry-rs/forge-std
+ branch = v1.5.0
+[submodule "lib/solady"]
+ path = lib/solady
+ url = https://github.com/vectorized/solady
+ branch = v0.0.84
+[submodule "lib/solarray"]
+ path = lib/solarray
+ url = https://github.com/emo-eth/solarray
+[submodule "lib/ds-test"]
+ path = lib/ds-test
+ url = https://github.com/dapphub/ds-test
diff --git a/.prettierignore b/.prettierignore
index ab450da55..47a16e0d9 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -12,3 +12,4 @@ lib/murky
lib/openzeppelin-contracts
lib/solmate
+docs/OrderValidator.md
\ No newline at end of file
diff --git a/README.md b/README.md
index 688c06f55..68f4fe71c 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
# Seaport
-Seaport is a new marketplace protocol for safely and efficiently buying and selling NFTs.
+Seaport is a marketplace protocol for safely and efficiently buying and selling NFTs.
## Table of Contents
@@ -20,6 +20,7 @@ Seaport is a new marketplace protocol for safely and efficiently buying and sell
- [Background](#background)
- [Deployments](#deployments)
- [Diagram](#diagram)
+ - [Docs](#docs)
- [Install](#install)
- [Usage](#usage)
- [Foundry Tests](#foundry-tests)
@@ -60,6 +61,10 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
0x00000000000001ad428e4906aE43D8F9852d0dD6 |
+Seaport 1.5 |
+0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC |
+
+
ConduitController |
0x00000000F9490004C11Cef243f5400493c00Ad63 |
@@ -72,14 +77,15 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
Network |
-Seaport 1.1 |
+Seaport 1.5 |
Seaport 1.4 |
+Seaport 1.1 |
ConduitController |
Ethereum |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://etherscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -87,12 +93,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Goerli |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://goerli.etherscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -100,12 +110,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://goerli.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Sepolia |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://sepolia.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://sepolia.etherscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -113,12 +127,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://sepolia.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://sepolia.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Polygon |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://polygonscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://polygonscan.com/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -126,12 +144,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://polygonscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://polygonscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Mumbai |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://mumbai.polygonscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://mumbai.polygonscan.com/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -139,51 +161,33 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
-[0x00000000F9490004C11Cef243f5400493c00Ad63](https://mumbai.polygonscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
-
- |
-Klaytn |
-
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://scope.klaytn.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
-
- |
-
-[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://scope.klaytn.com/address/0x00000000000001ad428e4906aE43D8F9852d0dD6#code)
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://mumbai.polygonscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
|
-[0x00000000F9490004C11Cef243f5400493c00Ad63](https://scope.klaytn.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://mumbai.polygonscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
-Baobab |
+ |
Optimism |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://baobab.scope.klaytn.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://optimistic.etherscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
-[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://baobab.scope.klaytn.com/address/0x00000000000001ad428e4906aE43D8F9852d0dD6#code)
+[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://optimistic.etherscan.io/address/0x00000000000001ad428e4906aE43D8F9852d0dD6#code)
|
-[0x00000000F9490004C11Cef243f5400493c00Ad63](https://baobab.scope.klaytn.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
-
- |
-Optimism |
-
[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://optimistic.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
|
-[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://optimistic.etherscan.io/address/0x00000000000001ad428e4906aE43D8F9852d0dD6#code)
-
- |
-
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://optimistic.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Optimistic Goerli |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli-optimism.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://goerli-optimism.etherscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -191,12 +195,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli-optimism.etherscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://goerli-optimism.etherscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Arbitrum |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://arbiscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -204,12 +212,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://arbiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Arbitrum Goerli |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli.arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://goerli.arbiscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -217,12 +229,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://goerli.arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://goerli.arbiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Arbitrum Nova |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://nova.arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://nova.arbiscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -230,12 +246,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://nova.arbiscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://nova.arbiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Avalanche C-Chain |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://snowtrace.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://snowtrace.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -243,12 +263,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://snowtrace.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://snowtrace.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Avalanche Fuji |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://testnet.snowtrace.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://testnet.snowtrace.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -256,12 +280,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://testnet.snowtrace.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://testnet.snowtrace.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
Gnosis Chain |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://gnosisscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://gnosisscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -269,12 +297,33 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://gnosisscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://gnossiscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+ |
+Chiado |
+
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://blockscout.com/gnosis/chiado/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC/contracts#address-tabs)
+
+ |
+
+Not deployed
+
+ |
+
+Not deployed
+
+ |
+
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://blockscout.com/gnosis/chiado/address/0x00000000F9490004C11Cef243f5400493c00Ad63/contracts#address-tabs)
+
|
BSC |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://bscscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://bscscan.com/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -282,12 +331,16 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://bscscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://bscscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
|
BSC Testnet |
-[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://testnet.bscscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://testnet.bscscan.com/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
|
@@ -295,8 +348,98 @@ See the [documentation](docs/SeaportDocumentation.md), the [interface](contracts
|
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://testnet.bscscan.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
[0x00000000F9490004C11Cef243f5400493c00Ad63](https://testnet.bscscan.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+ |
+Klaytn |
+
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://scope.klaytn.com/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
+
+ |
+
+[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://scope.klaytn.com/address/0x00000000000001ad428e4906aE43D8F9852d0dD6#code)
+
+ |
+
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://scope.klaytn.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://scope.klaytn.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+
+ |
+Baobab |
+
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://baobab.scope.klaytn.com/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
+
+ |
+
+[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://baobab.scope.klaytn.com/address/0x00000000000001ad428e4906aE43D8F9852d0dD6#code)
+
+ |
+
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://baobab.scope.klaytn.com/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://baobab.scope.klaytn.com/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+
+ |
+Moonbeam |
+
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://moonscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
+
+ |
+
+Not deployed
+
+ |
+
+Not deployed
+
+ |
+
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://moonscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+
+ |
+Moonriver |
+
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://moonriver.moonscan.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
+
+ |
+
+Not deployed
+
+ |
+
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://moonriver.moonscan.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://moonriver.moonscan.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+
+ |
+
+Canto |
+
+[0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC](https://evm.explorer.canto.io/address/0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC#code)
+
+ |
+
+[0x00000000000001ad428e4906aE43D8F9852d0dD6](https://evm.explorer.canto.io/address/0x00000000000001ad428e4906aE43D8F9852d0dD6)
+
+ |
+
+[0x00000000006c3852cbEf3e08E8dF289169EdE581](https://evm.explorer.canto.io/address/0x00000000006c3852cbEf3e08E8dF289169EdE581#code)
+
+ |
+
+[0x00000000F9490004C11Cef243f5400493c00Ad63](https://evm.explorer.canto.io/address/0x00000000F9490004C11Cef243f5400493c00Ad63#code)
+
|
@@ -345,12 +488,20 @@ graph TD
For a more thorough flowchart see [Seaport diagram](./diagrams/Seaport.drawio.svg).
+## Docs
+
+- [Seaport Deployment](./docs/Deployment.md)
+- [Seaport Documentation](./docs/SeaportDocumentation.md)
+- [Zone Documentation](./docs/ZoneDocumentation.md)
+- [Function Signatures](./docs/FunctionSignatures.md)
+- [Order Validator](./docs/OrderValidator.md)
+
## Install
To install dependencies and compile contracts:
```bash
-git clone https://github.com/ProjectOpenSea/seaport && cd seaport
+git clone --recurse-submodules https://github.com/ProjectOpenSea/seaport && cd seaport
yarn install
yarn build
```
@@ -387,7 +538,15 @@ yarn profile
### Foundry Tests
-Seaport also includes a suite of fuzzing tests written in solidity with Foundry.
+Seaport also includes a suite of fuzzing tests written in Solidity with Foundry.
+
+Before running these tests, you will need to compile an optimized build by running:
+
+```bash
+FOUNDRY_PROFILE=optimized forge build
+```
+
+This should create an `optimized-out/` directory in your project root.
To run tests with full traces and debugging with source, create an `.env` file with the following line:
@@ -397,7 +556,7 @@ FOUNDRY_PROFILE=debug
You may then run tests with `forge test`, optionally specifying a level of verbosity (anywhere from one to five `v`'s, eg, `-vvv`)
-This will compile tests and contracts without `via-ir` enabled, which is must faster, but will not exactly match the deployed bytecode.
+This will compile tests and contracts without `via-ir` enabled, which is much faster, but will not exactly match the deployed bytecode.
To run tests against the actual bytecode intended to be deployed on networks, you will need to pre-compile the contracts, and remove the `FOUNDRY_PROFILE` variable from your `.env` file. **Note** that informative error traces may not be available, and the Forge debugger will not show the accompanying source code.
@@ -410,10 +569,12 @@ To run Forge coverage tests and open the generated coverage report locally:
```bash
brew install lcov
-SEAPORT_COVERAGE=true forge coverage --report summary --report lcov && genhtml lcov.info -o html --branch
-open html/index.htmlg
+SEAPORT_COVERAGE=true forge coverage --report summary --report lcov && lcov -o lcov.info --remove lcov.info --rc lcov_branch_coverage=1 --rc lcov_function_coverage=1 "test/*" "script/*" && genhtml lcov.info -o html --branch
+open html/index.html
```
+When working on the test suite based around `FuzzEngine.sol`, using `FOUNDRY_PROFILE=moat_debug` will cut compile times roughly in half.
+
**Note** that Forge does not yet ignore specific filepaths when running coverage tests.
For information on Foundry, including installation and testing, see the [Foundry Book](https://book.getfoundry.sh/).
diff --git a/config/.solcover-reference.js b/config/.solcover-reference.js
index 84280eeef..df8fd5c01 100644
--- a/config/.solcover-reference.js
+++ b/config/.solcover-reference.js
@@ -14,6 +14,8 @@ module.exports = {
"interfaces/ConsiderationInterface.sol",
"interfaces/ContractOffererInterface.sol",
"interfaces/EIP1271Interface.sol",
+ "interfaces/ERC165.sol",
+ "interfaces/SeaportInterface.sol",
"interfaces/ZoneInterface.sol",
"lib/ConsiderationBase.sol",
"lib/ConsiderationConstants.sol",
@@ -25,6 +27,8 @@ module.exports = {
"lib/TokenTransferrer.sol",
"test/EIP1271Wallet.sol",
"test/ExcessReturnDataRecipient.sol",
+ "test/ERC1155BatchRecipient.sol",
+ "test/InvalidEthRecipient.sol",
"test/Reenterer.sol",
"test/TestERC1155.sol",
"test/TestERC1155Revert.sol",
@@ -37,8 +41,8 @@ module.exports = {
"test/TestInvalidContractOfferer.sol",
"test/TestInvalidContractOffererRatifyOrder.sol",
"test/TestBadContractOfferer.sol",
- "test/TestZone.sol",
"test/TestPostExecution.sol",
+ "test/TestZone.sol",
"test/TestERC20Panic.sol",
"test/TestERC20Revert.sol",
"test/InvalidERC721Recipient.sol",
@@ -50,6 +54,11 @@ module.exports = {
"test/ConduitMockRevertBytes.sol",
"test/ConduitMockRevertNoReason.sol",
"test/TestTransferValidationZoneOfferer.sol",
+ "zones/PausableZone.sol",
+ "zones/PausableZoneController.sol",
+ "zones/interfaces/PausableZoneControllerInterface.sol",
+ "zones/interfaces/PausableZoneEventsAndErrors.sol",
+ "zones/interfaces/PausableZoneInterface.sol",
"../reference/shim/Shim.sol",
],
};
diff --git a/config/.solcover.js b/config/.solcover.js
index 8cac48297..f8091fe4a 100644
--- a/config/.solcover.js
+++ b/config/.solcover.js
@@ -11,6 +11,7 @@ module.exports = {
"interfaces/ConsiderationInterface.sol",
"interfaces/ContractOffererInterface.sol",
"interfaces/EIP1271Interface.sol",
+ "interfaces/ERC165.sol",
"interfaces/SeaportInterface.sol",
"interfaces/ZoneInterface.sol",
"lib/ConsiderationConstants.sol",
diff --git a/contracts/Seaport.sol b/contracts/Seaport.sol
index 057c498af..634dd1d36 100644
--- a/contracts/Seaport.sol
+++ b/contracts/Seaport.sol
@@ -5,7 +5,7 @@ import { Consideration } from "./lib/Consideration.sol";
/**
* @title Seaport
- * @custom:version 1.4
+ * @custom:version 1.5
* @author 0age (0age.eth)
* @custom:coauthor d1ll0n (d1ll0n.eth)
* @custom:coauthor transmissions11 (t11s.eth)
diff --git a/contracts/helpers/ArrayHelpers.sol b/contracts/helpers/ArrayHelpers.sol
new file mode 100644
index 000000000..008eeaa1c
--- /dev/null
+++ b/contracts/helpers/ArrayHelpers.sol
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import "./PointerLibraries.sol";
+
+/**
+ * @author d1ll0n
+ * @custom:coauthor Most of the natspec is cribbed from the TypeScript
+ * documentation
+ */
+library ArrayHelpers {
+ // Has to be out of place to silence a linter warning
+ function reduceWithArg(
+ MemoryPointer array,
+ /* function (uint256 currentResult, uint256 element, uint256 arg) */
+ /* returns (uint256 newResult) */
+ function(uint256, uint256, MemoryPointer) internal returns (uint256) fn,
+ uint256 initialValue,
+ MemoryPointer arg
+ ) internal returns (uint256 result) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+
+ result = initialValue;
+ while (srcPosition.lt(srcEnd)) {
+ result = fn(result, srcPosition.readUint256(), arg);
+ srcPosition = srcPosition.next();
+ }
+ }
+ }
+
+ function flatten(
+ MemoryPointer array1,
+ MemoryPointer array2
+ ) internal view returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 arrayLength1 = array1.readUint256();
+ uint256 arrayLength2 = array2.readUint256();
+ uint256 array1HeadSize = arrayLength1 * 32;
+ uint256 array2HeadSize = arrayLength2 * 32;
+
+ newArray = malloc(array1HeadSize + array2HeadSize + 32);
+ newArray.write(arrayLength1 + arrayLength2);
+
+ MemoryPointer dst = newArray.next();
+ if (arrayLength1 > 0) {
+ array1.next().copy(dst, array1HeadSize);
+ }
+ if (arrayLength2 > 0) {
+ array2.next().copy(dst.offset(array1HeadSize), array2HeadSize);
+ }
+ }
+ }
+
+ function flattenThree(
+ MemoryPointer array1,
+ MemoryPointer array2,
+ MemoryPointer array3
+ ) internal view returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 arrayLength1 = array1.readUint256();
+ uint256 arrayLength2 = array2.readUint256();
+ uint256 arrayLength3 = array3.readUint256();
+ uint256 array1HeadSize = arrayLength1 * 32;
+ uint256 array2HeadSize = arrayLength2 * 32;
+ uint256 array3HeadSize = arrayLength3 * 32;
+
+ newArray = malloc(
+ array1HeadSize + array2HeadSize + array3HeadSize + 32
+ );
+ newArray.write(arrayLength1 + arrayLength2 + arrayLength3);
+
+ MemoryPointer dst = newArray.next();
+ if (arrayLength1 > 0) {
+ array1.next().copy(dst, array1HeadSize);
+ }
+ if (arrayLength2 > 0) {
+ array2.next().copy(dst.offset(array1HeadSize), array2HeadSize);
+ }
+ if (arrayLength3 > 0) {
+ array3.next().copy(
+ dst.offset(array1HeadSize + array2HeadSize),
+ array3HeadSize
+ );
+ }
+ }
+ }
+
+ // =====================================================================//
+ // map with (element) => (newElement) callback //
+ // =====================================================================//
+
+ /**
+ * @dev map calls a defined callback function on each element of an array
+ * and returns an array that contains the results
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * returns a new value to put in its place in the new array
+ *
+ * @return newArray the new array created with the results from calling
+ * fn with each element
+ */
+ function map(
+ MemoryPointer array,
+ /* function (uint256 value) returns (uint256 newValue) */
+ function(uint256) internal pure returns (uint256) fn
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+ newArray.write(length);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ while (srcPosition.lt(srcEnd)) {
+ dstPosition.write(fn(srcPosition.readUint256()));
+ srcPosition = srcPosition.next();
+ dstPosition = dstPosition.next();
+ }
+ }
+ }
+
+ // =====================================================================//
+ // filterMap with (element) => (newElement) callback //
+ // =====================================================================//
+
+ /**
+ * @dev filterMap calls a defined callback function on each element of an
+ * array and returns an array that contains only the non-zero results
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * returns a new value to put in its place in the new array
+ * or a zero value to indicate that the element should not
+ * be included in the new array
+ *
+ * @return newArray the new array created with the results from calling
+ * fn with each element
+ */
+ function filterMap(
+ MemoryPointer array,
+ /* function (uint256 value) returns (uint256 newValue) */
+ function(MemoryPointer) internal pure returns (MemoryPointer) fn
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ length = 0;
+
+ while (srcPosition.lt(srcEnd)) {
+ MemoryPointer result = fn(srcPosition.readMemoryPointer());
+ if (!result.isNull()) {
+ dstPosition.write(result);
+ dstPosition = dstPosition.next();
+ length += 1;
+ }
+ srcPosition = srcPosition.next();
+ }
+ newArray.write(length);
+ }
+ }
+
+ // =====================================================================//
+ // filterMap with (element, arg) => (newElement) callback //
+ // =====================================================================//
+
+ /**
+ * @dev filterMap calls a defined callback function on each element of an
+ * array and returns an array that contains only the non-zero results
+ *
+ * filterMapWithArg = (arr, callback, arg) => arr.map(
+ * (element) => callback(element, arg)
+ * ).filter(result => result != 0)
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * returns a new value to put in its place in the new array
+ * or a zero value to indicate that the element should not
+ * be included in the new array
+ * @param arg an arbitrary value provided in each call to fn
+ *
+ * @return newArray the new array created with the results from calling
+ * fn with each element
+ */
+ function filterMapWithArg(
+ MemoryPointer array,
+ /* function (MemoryPointer element, MemoryPointer arg) */
+ /* returns (uint256 newValue) */
+ function(MemoryPointer, MemoryPointer)
+ internal
+ pure
+ returns (MemoryPointer) fn,
+ MemoryPointer arg
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ length = 0;
+
+ while (srcPosition.lt(srcEnd)) {
+ MemoryPointer result = fn(srcPosition.readMemoryPointer(), arg);
+ if (!result.isNull()) {
+ dstPosition.write(result);
+ dstPosition = dstPosition.next();
+ length += 1;
+ }
+ srcPosition = srcPosition.next();
+ }
+ newArray.write(length);
+ }
+ }
+
+ // ====================================================================//
+ // filter with (element, arg) => (bool) predicate //
+ // ====================================================================//
+
+ /**
+ * @dev filter calls a defined callback function on each element of an array
+ * and returns an array that contains only the elements which the
+ * callback returned true for
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * returns a boolean that indicates whether the element
+ * should be included in the new array
+ * @param arg an arbitrary value provided in each call to fn
+ *
+ * @return newArray the new array created with the elements which the
+ * callback returned true for
+ */
+ function filterWithArg(
+ MemoryPointer array,
+ /* function (uint256 value, uint256 arg) returns (bool) */
+ function(MemoryPointer, MemoryPointer) internal pure returns (bool) fn,
+ MemoryPointer arg
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ length = 0;
+
+ while (srcPosition.lt(srcEnd)) {
+ MemoryPointer element = srcPosition.readMemoryPointer();
+ if (fn(element, arg)) {
+ dstPosition.write(element);
+ dstPosition = dstPosition.next();
+ length += 1;
+ }
+ srcPosition = srcPosition.next();
+ }
+ newArray.write(length);
+ }
+ }
+
+ // ====================================================================//
+ // filter with (element) => (bool) predicate //
+ // ====================================================================//
+
+ /**
+ * @dev filter calls a defined callback function on each element of an array
+ * and returns an array that contains only the elements which the
+ * callback returned true for
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * returns a boolean that indicates whether the element
+ * should be included in the new array
+ *
+ * @return newArray the new array created with the elements which the
+ * callback returned true for
+ */
+ function filter(
+ MemoryPointer array,
+ /* function (uint256 value) returns (bool) */
+ function(MemoryPointer) internal pure returns (bool) fn
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ length = 0;
+
+ while (srcPosition.lt(srcEnd)) {
+ MemoryPointer element = srcPosition.readMemoryPointer();
+ if (fn(element)) {
+ dstPosition.write(element);
+ dstPosition = dstPosition.next();
+ length += 1;
+ }
+ srcPosition = srcPosition.next();
+ }
+ newArray.write(length);
+ }
+ }
+
+ /**
+ * @dev mapWithIndex calls a defined callback function with each element of
+ * an array and its index and returns an array that contains the
+ * results
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * its index and returns a new value to put in its place
+ * in the new array
+ *
+ * @return newArray the new array created with the results from calling
+ * fn with each element
+ */
+ function mapWithIndex(
+ MemoryPointer array,
+ /* function (uint256 value, uint256 index) returns (uint256 newValue) */
+ function(uint256, uint256) internal pure returns (uint256) fn
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+ newArray.write(length);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ uint256 index;
+ while (srcPosition.lt(srcEnd)) {
+ dstPosition.write(fn(srcPosition.readUint256(), index++));
+ srcPosition = srcPosition.next();
+ dstPosition = dstPosition.next();
+ }
+ }
+ }
+
+ /**
+ * @dev map calls a defined callback function on each element of an array
+ * and returns an array that contains the results
+ *
+ * @param array the array to map
+ * @param fn a function that accepts each element in the array and
+ * the `arg` value provided in the call to map and returns
+ * a new value to put in its place in the new array
+ * @param arg an arbitrary value provided in each call to fn
+ *
+ * @return newArray the new array created with the results from calling
+ * fn with each element
+ */
+ function mapWithArg(
+ MemoryPointer array,
+ /* function (uint256 value, uint256 arg) returns (uint256 newValue) */
+ function(MemoryPointer, MemoryPointer)
+ internal
+ pure
+ returns (MemoryPointer) fn,
+ MemoryPointer arg
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+ newArray.write(length);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ while (srcPosition.lt(srcEnd)) {
+ dstPosition.write(fn(srcPosition.readMemoryPointer(), arg));
+ srcPosition = srcPosition.next();
+ dstPosition = dstPosition.next();
+ }
+ }
+ }
+
+ function mapWithIndex(
+ MemoryPointer array,
+ /* function (uint256 value, uint256 index, uint256 arg) */
+ /* returns (uint256 newValue) */
+ function(uint256, uint256, uint256) internal pure returns (uint256) fn,
+ uint256 arg
+ ) internal pure returns (MemoryPointer newArray) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ newArray = malloc((length + 1) * 32);
+ newArray.write(length);
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ MemoryPointer dstPosition = newArray.next();
+
+ uint256 index;
+ while (srcPosition.lt(srcEnd)) {
+ dstPosition.write(fn(srcPosition.readUint256(), index++, arg));
+ srcPosition = srcPosition.next();
+ dstPosition = dstPosition.next();
+ }
+ }
+ }
+
+ function reduce(
+ MemoryPointer array,
+ /* function (uint256 currentResult, uint256 element) */
+ /* returns (uint256 newResult) */
+ function(uint256, uint256) internal pure returns (uint256) fn,
+ uint256 initialValue
+ ) internal pure returns (uint256 result) {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+
+ result = initialValue;
+ while (srcPosition.lt(srcEnd)) {
+ result = fn(result, srcPosition.readUint256());
+ srcPosition = srcPosition.next();
+ }
+ }
+ }
+
+ // This was the previous home of `reduceWithArg`. It can now be found near
+ // the top of this file.
+
+ function forEach(
+ MemoryPointer array,
+ /* function (MemoryPointer element, MemoryPointer arg) */
+ function(MemoryPointer, MemoryPointer) internal pure fn,
+ MemoryPointer arg
+ ) internal pure {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+
+ while (srcPosition.lt(srcEnd)) {
+ fn(srcPosition.readMemoryPointer(), arg);
+ srcPosition = srcPosition.next();
+ }
+ }
+ }
+
+ function forEach(
+ MemoryPointer array,
+ /* function (MemoryPointer element) */
+ function(MemoryPointer) internal pure fn
+ ) internal pure {
+ unchecked {
+ uint256 length = array.readUint256();
+
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+
+ while (srcPosition.lt(srcEnd)) {
+ fn(srcPosition.readMemoryPointer());
+ srcPosition = srcPosition.next();
+ }
+ }
+ }
+
+ // =====================================================================//
+ // find with function(uint256 element, uint256 arg) predicate //
+ // =====================================================================//
+
+ /**
+ * @dev calls `predicate` once for each element of the array, in ascending
+ * order, until it finds one where predicate returns true. If such an
+ * element is found, find immediately returns that element value.
+ * Otherwise, find returns 0.
+ *
+ * @param array array to search
+ * @param predicate function that checks whether each element meets the
+ * search filter.
+ * @param arg second input to `predicate`
+ *
+ * @return the value of the first element in the array where
+ * predicate is true and 0 otherwise.
+ */
+ function find(
+ MemoryPointer array,
+ function(uint256, uint256) internal pure returns (bool) predicate,
+ uint256 arg
+ ) internal pure returns (uint256) {
+ unchecked {
+ uint256 length = array.readUint256();
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ while (srcPosition.lt(srcEnd)) {
+ uint256 value = srcPosition.readUint256();
+ if (predicate(value, arg)) return value;
+ srcPosition = srcPosition.next();
+ }
+ return 0;
+ }
+ }
+
+ // =====================================================================//
+ // find with function(uint256 element) predicate //
+ // =====================================================================//
+
+ /**
+ * @dev calls `predicate` once for each element of the array, in ascending
+ * order, until it finds one where predicate returns true. If such an
+ * element is found, find immediately returns that element value.
+ * Otherwise, find returns 0.
+ *
+ * @param array array to search
+ * @param predicate function that checks whether each element meets the
+ * search filter.
+ * @param fromIndex index to start search at
+ *
+ * @custom:return the value of the first element in the array where
+ * predicate is trueand 0 otherwise.
+ */
+ function find(
+ MemoryPointer array,
+ function(uint256) internal pure returns (bool) predicate,
+ uint256 fromIndex
+ ) internal pure returns (uint256) {
+ unchecked {
+ uint256 length = array.readUint256();
+ MemoryPointer srcPosition = array.next().offset(fromIndex * 0x20);
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ while (srcPosition.lt(srcEnd)) {
+ uint256 value = srcPosition.readUint256();
+ if (predicate(value)) return value;
+ srcPosition = srcPosition.next();
+ }
+ return 0;
+ }
+ }
+
+ // =====================================================================//
+ // find with function(uint256 element) predicate //
+ // =====================================================================//
+
+ /**
+ * @dev calls `predicate` once for each element of the array, in ascending
+ * order, until it finds one where predicate returns true. If such an
+ * element is found, find immediately returns that element value.
+ * Otherwise, find returns 0.
+ *
+ * @param array array to search
+ * @param predicate function that checks whether each element meets the
+ * search filter.
+ *
+ * @return the value of the first element in the array where
+ * predicate is true and 0 otherwise.
+ */
+ function find(
+ MemoryPointer array,
+ function(uint256) internal pure returns (bool) predicate
+ ) internal pure returns (uint256) {
+ unchecked {
+ uint256 length = array.readUint256();
+ MemoryPointer srcPosition = array.next();
+ MemoryPointer srcEnd = srcPosition.offset(length * 0x20);
+ while (srcPosition.lt(srcEnd)) {
+ uint256 value = srcPosition.readUint256();
+ if (predicate(value)) return value;
+ srcPosition = srcPosition.next();
+ }
+ return 0;
+ }
+ }
+
+ // =====================================================================//
+ // indexOf //
+ // =====================================================================//
+
+ /**
+ * @dev Returns the index of the first occurrence of a value in an array,
+ * or -1 if it is not present.
+ *
+ * @param array array to search
+ * @param searchElement the value to locate in the array.
+ */
+ function indexOf(
+ MemoryPointer array,
+ uint256 searchElement
+ ) internal pure returns (int256 index) {
+ unchecked {
+ int256 length = array.readInt256();
+ MemoryPointer src = array;
+ int256 reachedEnd;
+ while (
+ ((reachedEnd = toInt(index == length)) |
+ toInt((src = src.next()).readUint256() == searchElement)) ==
+ 0
+ ) {
+ index += 1;
+ }
+ return (reachedEnd * -1) | index;
+ }
+ }
+
+ function toInt(bool a) internal pure returns (int256 b) {
+ assembly {
+ b := a
+ }
+ }
+
+ // =====================================================================//
+ // findIndex with one argument //
+ // =====================================================================//
+
+ function findIndexWithArg(
+ MemoryPointer array,
+ function(uint256, uint256) internal pure returns (bool) predicate,
+ uint256 arg
+ ) internal pure returns (int256 index) {
+ unchecked {
+ int256 length = array.readInt256();
+ MemoryPointer src = array;
+ while (index < length) {
+ if (predicate((src = src.next()).readUint256(), arg)) {
+ return index;
+ }
+ index += 1;
+ }
+ return -1;
+ }
+ }
+
+ // =====================================================================//
+ // findIndex from start index //
+ // =====================================================================//
+
+ function findIndexFrom(
+ MemoryPointer array,
+ function(MemoryPointer) internal pure returns (bool) predicate,
+ uint256 fromIndex
+ ) internal pure returns (int256 index) {
+ unchecked {
+ index = int256(fromIndex);
+ int256 length = array.readInt256();
+ MemoryPointer src = array.offset(fromIndex * 0x20);
+ while (index < length) {
+ if (predicate((src = src.next()).readMemoryPointer())) {
+ return index;
+ }
+ index += 1;
+ }
+ return -1;
+ }
+ }
+
+ function countFrom(
+ MemoryPointer array,
+ function(MemoryPointer) internal pure returns (bool) predicate,
+ uint256 fromIndex
+ ) internal pure returns (int256 count) {
+ unchecked {
+ uint256 index = fromIndex;
+ uint256 length = array.readUint256();
+ MemoryPointer src = array.offset(fromIndex * 0x20);
+ while (index < length) {
+ if (predicate((src = src.next()).readMemoryPointer())) {
+ count += 1;
+ }
+ index += 1;
+ }
+ }
+ }
+
+ // =====================================================================//
+ // includes with one argument //
+ // =====================================================================//
+
+ function includes(
+ MemoryPointer array,
+ uint256 value
+ ) internal pure returns (bool) {
+ return indexOf(array, value) != -1;
+ }
+}
diff --git a/contracts/helpers/PointerLibraries.sol b/contracts/helpers/PointerLibraries.sol
index 7f927b564..298d6c032 100644
--- a/contracts/helpers/PointerLibraries.sol
+++ b/contracts/helpers/PointerLibraries.sol
@@ -74,6 +74,12 @@ library CalldataPointerLib {
}
}
+ function isNull(CalldataPointer a) internal pure returns (bool b) {
+ assembly {
+ b := iszero(a)
+ }
+ }
+
/// @dev Resolves an offset stored at `cdPtr + headOffset` to a calldata.
/// pointer `cdPtr` must point to some parent object with a dynamic
/// type's head stored at `cdPtr + headOffset`.
@@ -155,6 +161,12 @@ library ReturndataPointerLib {
}
}
+ function isNull(ReturndataPointer a) internal pure returns (bool b) {
+ assembly {
+ b := iszero(a)
+ }
+ }
+
/// @dev Resolves an offset stored at `rdPtr + headOffset` to a returndata
/// pointer. `rdPtr` must point to some parent object with a dynamic
/// type's head stored at `rdPtr + headOffset`.
@@ -256,6 +268,21 @@ library MemoryPointerLib {
}
}
+ function isNull(MemoryPointer a) internal pure returns (bool b) {
+ assembly {
+ b := iszero(a)
+ }
+ }
+
+ function hash(
+ MemoryPointer ptr,
+ uint256 length
+ ) internal pure returns (bytes32 _hash) {
+ assembly {
+ _hash := keccak256(ptr, length)
+ }
+ }
+
/// @dev Returns the memory pointer one word after `mPtr`.
function next(
MemoryPointer mPtr
@@ -275,7 +302,7 @@ library MemoryPointerLib {
}
}
- /// @dev Resolves a pointer pointer at `mPtr + headOffset` to a memory
+ /// @dev Resolves a pointer at `mPtr + headOffset` to a memory
/// pointer. `mPtr` must point to some parent object with a dynamic
/// type's pointer stored at `mPtr + headOffset`.
function pptr(
@@ -285,7 +312,7 @@ library MemoryPointerLib {
mPtrChild = mPtr.offset(headOffset).readMemoryPointer();
}
- /// @dev Resolves a pointer pointer stored at `mPtr` to a memory pointer.
+ /// @dev Resolves a pointer stored at `mPtr` to a memory pointer.
/// `mPtr` must point to some parent object with a dynamic type as its
/// first member, e.g. `struct { bytes data; }`
function pptr(
diff --git a/contracts/helpers/SeaportRouter.sol b/contracts/helpers/SeaportRouter.sol
index 43b57c393..c02b0e6f6 100644
--- a/contracts/helpers/SeaportRouter.sol
+++ b/contracts/helpers/SeaportRouter.sol
@@ -10,9 +10,9 @@ import { SeaportInterface } from "../interfaces/SeaportInterface.sol";
import { ReentrancyGuard } from "../lib/ReentrancyGuard.sol";
import {
- Execution,
AdvancedOrder,
CriteriaResolver,
+ Execution,
FulfillmentComponent
} from "../lib/ConsiderationStructs.sol";
@@ -24,20 +24,20 @@ import {
* all consideration items across all listings are native tokens.
*/
contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
- /// @dev The allowed v1.1 contract usable through this router.
- address private immutable _SEAPORT_V1_1;
- /// @dev The allowed v1.2 contract usable through this router.
- address private immutable _SEAPORT_V1_2;
+ /// @dev The allowed v1.4 contract usable through this router.
+ address private immutable _SEAPORT_V1_4;
+ /// @dev The allowed v1.5 contract usable through this router.
+ address private immutable _SEAPORT_V1_5;
/**
* @dev Deploy contract with the supported Seaport contracts.
*
- * @param seaportV1point1 The address of the Seaport v1.1 contract.
- * @param seaportV1point2 The address of the Seaport v1.2 contract.
+ * @param seaportV1point4 The address of the Seaport v1.4 contract.
+ * @param seaportV1point5 The address of the Seaport v1.5 contract.
*/
- constructor(address seaportV1point1, address seaportV1point2) {
- _SEAPORT_V1_1 = seaportV1point1;
- _SEAPORT_V1_2 = seaportV1point2;
+ constructor(address seaportV1point4, address seaportV1point5) {
+ _SEAPORT_V1_4 = seaportV1point4;
+ _SEAPORT_V1_5 = seaportV1point5;
}
/**
@@ -95,6 +95,11 @@ contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
maximumFulfilled: fulfillmentsLeft
});
+ // If recipient is not provided assign to msg.sender.
+ if (calldataParams.recipient == address(0)) {
+ calldataParams.recipient = msg.sender;
+ }
+
// Iterate through the provided Seaport contracts.
for (uint256 i = 0; i < params.seaportContracts.length; ) {
// Ensure the provided Seaport contract is allowed.
@@ -114,7 +119,7 @@ contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
// Execute the orders, collecting availableOrders and executions.
// This is wrapped in a try/catch in case a single order is
// executed that is no longer available, leading to a revert
- // with `NoSpecifiedOrdersAvailable()`.
+ // with `NoSpecifiedOrdersAvailable()` that can be ignored.
try
SeaportInterface(params.seaportContracts[i])
.fulfillAvailableAdvancedOrders{
@@ -150,7 +155,33 @@ contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
if (fulfillmentsLeft == 0) {
break;
}
- } catch {}
+ } catch (bytes memory data) {
+ // Set initial value of first four bytes of revert data
+ // to the mask.
+ bytes4 customErrorSelector = bytes4(0xffffffff);
+
+ // Utilize assembly to read first four bytes
+ // (if present) directly.
+ assembly {
+ // Combine original mask with first four bytes of
+ // revert data.
+ customErrorSelector := and(
+ // Data begins after length offset.
+ mload(add(data, 0x20)),
+ customErrorSelector
+ )
+ }
+
+ // Pass through the custom error if the error is
+ // not NoSpecifiedOrdersAvailable()
+ if (
+ customErrorSelector != NoSpecifiedOrdersAvailable.selector
+ ) {
+ assembly {
+ revert(add(data, 32), mload(data))
+ }
+ }
+ }
// Update fulfillments left.
calldataParams.maximumFulfilled = fulfillmentsLeft;
@@ -160,6 +191,11 @@ contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
}
}
+ // Throw an error if no orders were fulfilled.
+ if (fulfillmentsLeft == params.maximumFulfilled) {
+ revert NoSpecifiedOrdersAvailable();
+ }
+
// Return excess ether that may not have been used or was sent back.
if (address(this).balance > 0) {
_returnExcessEther();
@@ -180,8 +216,8 @@ contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
returns (address[] memory seaportContracts)
{
seaportContracts = new address[](2);
- seaportContracts[0] = _SEAPORT_V1_1;
- seaportContracts[1] = _SEAPORT_V1_2;
+ seaportContracts[0] = _SEAPORT_V1_4;
+ seaportContracts[1] = _SEAPORT_V1_5;
}
/**
@@ -189,7 +225,7 @@ contract SeaportRouter is SeaportRouterInterface, ReentrancyGuard {
*/
function _assertSeaportAllowed(address seaport) internal view {
if (
- _cast(seaport == _SEAPORT_V1_1) | _cast(seaport == _SEAPORT_V1_2) ==
+ _cast(seaport == _SEAPORT_V1_4) | _cast(seaport == _SEAPORT_V1_5) ==
0
) {
revert SeaportNotAllowed(seaport);
diff --git a/contracts/helpers/order-validator/SeaportValidator.sol b/contracts/helpers/order-validator/SeaportValidator.sol
new file mode 100644
index 000000000..0b6ab3489
--- /dev/null
+++ b/contracts/helpers/order-validator/SeaportValidator.sol
@@ -0,0 +1,1206 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ItemType } from "../../lib/ConsiderationEnums.sol";
+import {
+ Order,
+ OrderParameters,
+ BasicOrderParameters,
+ OfferItem,
+ ConsiderationItem,
+ Schema,
+ ZoneParameters
+} from "../../lib/ConsiderationStructs.sol";
+import { ConsiderationTypeHashes } from "./lib/ConsiderationTypeHashes.sol";
+import {
+ ConsiderationInterface
+} from "../../interfaces/ConsiderationInterface.sol";
+import {
+ ConduitControllerInterface
+} from "../../interfaces/ConduitControllerInterface.sol";
+import {
+ ContractOffererInterface
+} from "../../interfaces/ContractOffererInterface.sol";
+import { ZoneInterface } from "../../interfaces/ZoneInterface.sol";
+import { GettersAndDerivers } from "../../lib/GettersAndDerivers.sol";
+import { SeaportValidatorInterface } from "./lib/SeaportValidatorInterface.sol";
+import { ZoneInterface } from "../../interfaces/ZoneInterface.sol";
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../../interfaces/AbridgedTokenInterfaces.sol";
+import { IERC165 } from "../../interfaces/IERC165.sol";
+import { IERC2981 } from "../../interfaces/IERC2981.sol";
+import {
+ ErrorsAndWarnings,
+ ErrorsAndWarningsLib
+} from "./lib/ErrorsAndWarnings.sol";
+import { SafeStaticCall } from "./lib/SafeStaticCall.sol";
+import {
+ IssueParser,
+ ValidationConfiguration,
+ TimeIssue,
+ StatusIssue,
+ OfferIssue,
+ ContractOffererIssue,
+ ConsiderationIssue,
+ PrimaryFeeIssue,
+ ERC721Issue,
+ ERC1155Issue,
+ ERC20Issue,
+ NativeIssue,
+ ZoneIssue,
+ ConduitIssue,
+ CreatorFeeIssue,
+ SignatureIssue,
+ GenericIssue,
+ ConsiderationItemConfiguration
+} from "./lib/SeaportValidatorTypes.sol";
+import { Verifiers } from "../../lib/Verifiers.sol";
+import { SeaportValidatorHelper } from "./lib/SeaportValidatorHelper.sol";
+
+/**
+ * @title SeaportValidator
+ * @author OpenSea Protocol Team
+ * @notice SeaportValidator provides advanced validation to seaport orders.
+ */
+contract SeaportValidator is
+ SeaportValidatorInterface,
+ ConsiderationTypeHashes
+{
+ using ErrorsAndWarningsLib for ErrorsAndWarnings;
+ using SafeStaticCall for address;
+ using IssueParser for *;
+
+ /// @notice Cross-chain conduit controller Address
+ ConduitControllerInterface private immutable _conduitController;
+
+ SeaportValidatorHelper private immutable _helper;
+
+ bytes4 public constant ERC20_INTERFACE_ID = 0x36372b07;
+
+ bytes4 public constant ERC721_INTERFACE_ID = 0x80ac58cd;
+
+ bytes4 public constant ERC1155_INTERFACE_ID = 0xd9b67a26;
+
+ bytes4 public constant CONTRACT_OFFERER_INTERFACE_ID = 0x1be900b1;
+
+ bytes4 public constant ZONE_INTERFACE_ID = 0x3839be19;
+
+ constructor(
+ address seaportValidatorHelperAddress,
+ address conduitControllerAddress
+ ) {
+ _helper = SeaportValidatorHelper(seaportValidatorHelperAddress);
+ _conduitController = ConduitControllerInterface(
+ conduitControllerAddress
+ );
+ }
+
+ /**
+ * @notice Conduct a comprehensive validation of the given order.
+ * `isValidOrder` validates simple orders that adhere to a set of rules defined below:
+ * - The order is either a listing or an offer order (one NFT to buy or one NFT to sell).
+ * - The first consideration is the primary consideration.
+ * - The order pays up to two fees in the fungible token currency. First fee is primary fee, second is creator fee.
+ * - In private orders, the last consideration specifies a recipient for the offer item.
+ * - Offer items must be owned and properly approved by the offerer.
+ * - There must be one offer item
+ * - Consideration items must exist.
+ * - The signature must be valid, or the order must be already validated on chain
+ * @param order The order to validate.
+ * @return errorsAndWarnings The errors and warnings found in the order.
+ */
+ function isValidOrder(
+ Order calldata order,
+ address seaportAddress
+ ) external returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ return
+ isValidOrderWithConfiguration(
+ ValidationConfiguration(
+ seaportAddress,
+ address(0),
+ 0,
+ false,
+ false,
+ 30 minutes,
+ 26 weeks
+ ),
+ order
+ );
+ }
+
+ /**
+ * @notice Same as `isValidOrder` but does not modify state.
+ */
+ function isValidOrderReadOnly(
+ Order calldata order,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ return
+ isValidOrderWithConfigurationReadOnly(
+ ValidationConfiguration(
+ seaportAddress,
+ address(0),
+ 0,
+ false,
+ false,
+ 30 minutes,
+ 26 weeks
+ ),
+ order
+ );
+ }
+
+ /**
+ * @notice Same as `isValidOrder` but allows for more configuration related to fee validation.
+ * If `skipStrictValidation` is set order logic validation is not carried out: fees are not
+ * checked and there may be more than one offer item as well as any number of consideration items.
+ */
+ function isValidOrderWithConfiguration(
+ ValidationConfiguration memory validationConfiguration,
+ Order memory order
+ ) public returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Concatenates errorsAndWarnings with the returned errorsAndWarnings
+ errorsAndWarnings.concat(
+ validateTime(
+ order.parameters,
+ validationConfiguration.shortOrderDuration,
+ validationConfiguration.distantOrderExpiration
+ )
+ );
+ errorsAndWarnings.concat(
+ validateOrderStatus(
+ order.parameters,
+ validationConfiguration.seaport
+ )
+ );
+ errorsAndWarnings.concat(
+ validateOfferItems(
+ order.parameters,
+ validationConfiguration.seaport
+ )
+ );
+ errorsAndWarnings.concat(
+ validateConsiderationItems(
+ order.parameters,
+ validationConfiguration.seaport
+ )
+ );
+ errorsAndWarnings.concat(isValidZone(order.parameters));
+ errorsAndWarnings.concat(
+ validateSignature(order, validationConfiguration.seaport)
+ );
+
+ // Skip strict validation if requested
+ if (!validationConfiguration.skipStrictValidation) {
+ errorsAndWarnings.concat(
+ validateStrictLogic(
+ order.parameters,
+ validationConfiguration.primaryFeeRecipient,
+ validationConfiguration.primaryFeeBips,
+ validationConfiguration.checkCreatorFee
+ )
+ );
+ }
+ }
+
+ /**
+ * @notice Same as `isValidOrderWithConfiguration` but doesn't call `validate` on Seaport.
+ * If `skipStrictValidation` is set order logic validation is not carried out: fees are not
+ * checked and there may be more than one offer item as well as any number of consideration items.
+ */
+ function isValidOrderWithConfigurationReadOnly(
+ ValidationConfiguration memory validationConfiguration,
+ Order memory order
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Concatenates errorsAndWarnings with the returned errorsAndWarnings
+ errorsAndWarnings.concat(
+ validateTime(
+ order.parameters,
+ validationConfiguration.shortOrderDuration,
+ validationConfiguration.distantOrderExpiration
+ )
+ );
+ errorsAndWarnings.concat(
+ validateOrderStatus(
+ order.parameters,
+ validationConfiguration.seaport
+ )
+ );
+ errorsAndWarnings.concat(
+ validateOfferItems(
+ order.parameters,
+ validationConfiguration.seaport
+ )
+ );
+ errorsAndWarnings.concat(
+ validateConsiderationItems(
+ order.parameters,
+ validationConfiguration.seaport
+ )
+ );
+ errorsAndWarnings.concat(isValidZone(order.parameters));
+
+ // Skip strict validation if requested
+ if (!validationConfiguration.skipStrictValidation) {
+ errorsAndWarnings.concat(
+ validateStrictLogic(
+ order.parameters,
+ validationConfiguration.primaryFeeRecipient,
+ validationConfiguration.primaryFeeBips,
+ validationConfiguration.checkCreatorFee
+ )
+ );
+ }
+ }
+
+ /**
+ * @notice Strict validation operates under tight assumptions. It validates primary
+ * fee, creator fee, private sale consideration, and overall order format.
+ * @dev Only checks first fee recipient provided by CreatorFeeEngine.
+ * Order of consideration items must be as follows:
+ * 1. Primary consideration
+ * 2. Primary fee
+ * 3. Creator fee
+ * 4. Private sale consideration
+ * @param orderParameters The parameters for the order to validate.
+ * @param primaryFeeRecipient The primary fee recipient. Set to null address for no primary fee.
+ * @param primaryFeeBips The primary fee in BIPs.
+ * @param checkCreatorFee Should check for creator fee. If true, creator fee must be present as
+ * according to creator fee engine. If false, must not have creator fee.
+ * @return errorsAndWarnings The errors and warnings.
+ */
+ function validateStrictLogic(
+ OrderParameters memory orderParameters,
+ address primaryFeeRecipient,
+ uint256 primaryFeeBips,
+ bool checkCreatorFee
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ return
+ _helper.validateStrictLogic(
+ orderParameters,
+ primaryFeeRecipient,
+ primaryFeeBips,
+ checkCreatorFee
+ );
+ }
+
+ /**
+ * @notice Checks if a conduit key is valid.
+ * @param conduitKey The conduit key to check.
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function isValidConduit(
+ bytes32 conduitKey,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ (, errorsAndWarnings) = getApprovalAddress(conduitKey, seaportAddress);
+ }
+
+ /**
+ * @notice Checks if the zone of an order is set and implements the EIP165
+ * zone interface
+ * @dev To validate the zone call for an order, see validateOrderWithZone
+ * @param orderParameters The order parameters to check.
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function isValidZone(
+ OrderParameters memory orderParameters
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // If not restricted, zone isn't checked
+ if (
+ uint8(orderParameters.orderType) < 2 ||
+ uint8(orderParameters.orderType) == 4
+ ) {
+ return errorsAndWarnings;
+ }
+
+ if (orderParameters.zone == address(0)) {
+ // Zone is not set
+ errorsAndWarnings.addError(ZoneIssue.NotSet.parseInt());
+ return errorsAndWarnings;
+ }
+
+ // Warn if zone is an EOA
+ if (address(orderParameters.zone).code.length == 0) {
+ errorsAndWarnings.addWarning(ZoneIssue.EOAZone.parseInt());
+ return errorsAndWarnings;
+ }
+
+ // Check the EIP165 zone interface
+ if (!checkInterface(orderParameters.zone, ZONE_INTERFACE_ID)) {
+ errorsAndWarnings.addWarning(ZoneIssue.InvalidZone.parseInt());
+ return errorsAndWarnings;
+ }
+
+ // Check if the zone implements SIP-5
+ try ZoneInterface(orderParameters.zone).getSeaportMetadata() {} catch {
+ errorsAndWarnings.addWarning(ZoneIssue.InvalidZone.parseInt());
+ }
+ }
+
+ /**
+ * @notice Gets the approval address for the given conduit key
+ * @param conduitKey Conduit key to get approval address for
+ * @param seaportAddress The Seaport address
+ * @return approvalAddress The address to use for approvals
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function getApprovalAddress(
+ bytes32 conduitKey,
+ address seaportAddress
+ )
+ public
+ view
+ returns (address, ErrorsAndWarnings memory errorsAndWarnings)
+ {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Zero conduit key corresponds to seaport
+ if (conduitKey == 0) return (seaportAddress, errorsAndWarnings);
+
+ // Pull conduit info from conduitController
+ (address conduitAddress, bool exists) = _conduitController.getConduit(
+ conduitKey
+ );
+
+ // Conduit does not exist
+ if (!exists) {
+ errorsAndWarnings.addError(ConduitIssue.KeyInvalid.parseInt());
+ conduitAddress = address(0); // Don't return invalid conduit
+ }
+
+ // Approval address does not have Seaport added as a channel
+ if (
+ exists &&
+ !_conduitController.getChannelStatus(conduitAddress, seaportAddress)
+ ) {
+ errorsAndWarnings.addError(
+ ConduitIssue.MissingSeaportChannel.parseInt()
+ );
+ }
+
+ return (conduitAddress, errorsAndWarnings);
+ }
+
+ /**
+ * @notice Validates the signature for the order using the offerer's current counter
+ * @dev Will also check if order is validated on chain.
+ */
+ function validateSignature(
+ Order memory order,
+ address seaportAddress
+ ) public returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ // Pull current counter from seaport
+ uint256 currentCounter = ConsiderationInterface(seaportAddress)
+ .getCounter(order.parameters.offerer);
+
+ return
+ validateSignatureWithCounter(order, currentCounter, seaportAddress);
+ }
+
+ /**
+ * @notice Validates the signature for the order using the given counter
+ * @dev Will also check if order is validated on chain.
+ */
+ function validateSignatureWithCounter(
+ Order memory order,
+ uint256 counter,
+ address seaportAddress
+ ) public returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Typecast Seaport address to ConsiderationInterface
+ ConsiderationInterface seaport = ConsiderationInterface(seaportAddress);
+
+ // Contract orders do not have signatures
+ if (uint8(order.parameters.orderType) == 4) {
+ errorsAndWarnings.addWarning(
+ SignatureIssue.ContractOrder.parseInt()
+ );
+ }
+
+ // Get current counter for context
+ uint256 currentCounter = seaport.getCounter(order.parameters.offerer);
+
+ if (currentCounter > counter) {
+ // Counter strictly increases
+ errorsAndWarnings.addError(SignatureIssue.LowCounter.parseInt());
+ return errorsAndWarnings;
+ } else if (currentCounter < counter) {
+ // Counter is incremented by random large number
+ errorsAndWarnings.addError(SignatureIssue.HighCounter.parseInt());
+ return errorsAndWarnings;
+ }
+
+ bytes32 orderHash = _deriveOrderHash(order.parameters, counter);
+
+ // Check if order is validated on chain
+ (bool isValid, , , ) = seaport.getOrderStatus(orderHash);
+
+ if (isValid) {
+ // Shortcut success, valid on chain
+ return errorsAndWarnings;
+ }
+
+ // Create memory array to pass into validate
+ Order[] memory orderArray = new Order[](1);
+
+ // Store order in array
+ orderArray[0] = order;
+
+ try
+ // Call validate on Seaport
+ seaport.validate(orderArray)
+ returns (bool success) {
+ if (!success) {
+ // Call was unsuccessful, so signature is invalid
+ errorsAndWarnings.addError(SignatureIssue.Invalid.parseInt());
+ }
+ } catch {
+ if (
+ order.parameters.consideration.length !=
+ order.parameters.totalOriginalConsiderationItems
+ ) {
+ // May help diagnose signature issues
+ errorsAndWarnings.addWarning(
+ SignatureIssue.OriginalConsiderationItems.parseInt()
+ );
+ }
+ // Call reverted, so signature is invalid
+ errorsAndWarnings.addError(SignatureIssue.Invalid.parseInt());
+ }
+ }
+
+ /**
+ * @notice Check that a contract offerer implements the EIP165
+ * contract offerer interface
+ * @param contractOfferer The address of the contract offerer
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateContractOfferer(
+ address contractOfferer
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Check the EIP165 contract offerer interface
+ if (!checkInterface(contractOfferer, CONTRACT_OFFERER_INTERFACE_ID)) {
+ errorsAndWarnings.addWarning(
+ ContractOffererIssue.InvalidContractOfferer.parseInt()
+ );
+ }
+
+ // Check if the contract offerer implements SIP-5
+ try
+ ContractOffererInterface(contractOfferer).getSeaportMetadata()
+ {} catch {
+ errorsAndWarnings.addWarning(
+ ContractOffererIssue.InvalidContractOfferer.parseInt()
+ );
+ }
+
+ return errorsAndWarnings;
+ }
+
+ /**
+ * @notice Check the time validity of an order
+ * @param orderParameters The parameters for the order to validate
+ * @param shortOrderDuration The duration of which an order is considered short
+ * @param distantOrderExpiration Distant order expiration delta in seconds.
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateTime(
+ OrderParameters memory orderParameters,
+ uint256 shortOrderDuration,
+ uint256 distantOrderExpiration
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ if (orderParameters.endTime <= orderParameters.startTime) {
+ // Order duration is zero
+ errorsAndWarnings.addError(
+ TimeIssue.EndTimeBeforeStartTime.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+
+ if (orderParameters.endTime < block.timestamp) {
+ // Order is expired
+ errorsAndWarnings.addError(TimeIssue.Expired.parseInt());
+ return errorsAndWarnings;
+ } else if (
+ orderParameters.endTime > block.timestamp + distantOrderExpiration
+ ) {
+ // Order expires in a long time
+ errorsAndWarnings.addWarning(
+ TimeIssue.DistantExpiration.parseInt()
+ );
+ }
+
+ if (orderParameters.startTime > block.timestamp) {
+ // Order is not active
+ errorsAndWarnings.addWarning(TimeIssue.NotActive.parseInt());
+ }
+
+ if (
+ orderParameters.endTime -
+ (
+ orderParameters.startTime > block.timestamp
+ ? orderParameters.startTime
+ : block.timestamp
+ ) <
+ shortOrderDuration
+ ) {
+ // Order has a short duration
+ errorsAndWarnings.addWarning(TimeIssue.ShortOrder.parseInt());
+ }
+ }
+
+ /**
+ * @notice Validate the status of an order
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateOrderStatus(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Typecast Seaport address to ConsiderationInterface
+ ConsiderationInterface seaport = ConsiderationInterface(seaportAddress);
+
+ // Cannot validate status of contract order
+ if (uint8(orderParameters.orderType) == 4) {
+ errorsAndWarnings.addWarning(StatusIssue.ContractOrder.parseInt());
+ }
+
+ // Pull current counter from seaport
+ uint256 currentOffererCounter = seaport.getCounter(
+ orderParameters.offerer
+ );
+ // Derive order hash using orderParameters and currentOffererCounter
+ bytes32 orderHash = _deriveOrderHash(
+ orderParameters,
+ currentOffererCounter
+ );
+ // Get order status from seaport
+ (, bool isCancelled, uint256 totalFilled, uint256 totalSize) = seaport
+ .getOrderStatus(orderHash);
+
+ if (isCancelled) {
+ // Order is cancelled
+ errorsAndWarnings.addError(StatusIssue.Cancelled.parseInt());
+ }
+
+ if (totalSize > 0 && totalFilled == totalSize) {
+ // Order is fully filled
+ errorsAndWarnings.addError(StatusIssue.FullyFilled.parseInt());
+ }
+ }
+
+ /**
+ * @notice Validate all offer items for an order. Ensures that
+ * offerer has sufficient balance and approval for each item.
+ * @dev Amounts are not summed and verified, just the individual amounts.
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateOfferItems(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Iterate over each offer item and validate it
+ for (uint256 i = 0; i < orderParameters.offer.length; i++) {
+ errorsAndWarnings.concat(
+ validateOfferItem(orderParameters, i, seaportAddress)
+ );
+
+ // Check for duplicate offer item
+ OfferItem memory offerItem1 = orderParameters.offer[i];
+
+ for (uint256 j = i + 1; j < orderParameters.offer.length; j++) {
+ // Iterate over each remaining offer item
+ // (previous items already check with this item)
+ OfferItem memory offerItem2 = orderParameters.offer[j];
+
+ // Check if token and id are the same
+ if (
+ offerItem1.token == offerItem2.token &&
+ offerItem1.identifierOrCriteria ==
+ offerItem2.identifierOrCriteria
+ ) {
+ errorsAndWarnings.addError(
+ OfferIssue.DuplicateItem.parseInt()
+ );
+ }
+ }
+ }
+
+ // You must have an offer item
+ if (orderParameters.offer.length == 0) {
+ errorsAndWarnings.addWarning(OfferIssue.ZeroItems.parseInt());
+ }
+
+ // Warning if there is more than one offer item
+ if (orderParameters.offer.length > 1) {
+ errorsAndWarnings.addWarning(OfferIssue.MoreThanOneItem.parseInt());
+ }
+ }
+
+ /**
+ * @notice Validates an offer item
+ * @param orderParameters The parameters for the order to validate
+ * @param offerItemIndex The index of the offerItem in offer array to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOfferItem(
+ OrderParameters memory orderParameters,
+ uint256 offerItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ // First validate the parameters (correct amount, contract, etc)
+ errorsAndWarnings = validateOfferItemParameters(
+ orderParameters,
+ offerItemIndex,
+ seaportAddress
+ );
+ if (errorsAndWarnings.hasErrors()) {
+ // Only validate approvals and balances if parameters are valid
+ return errorsAndWarnings;
+ }
+
+ // Validate approvals and balances for the offer item
+ errorsAndWarnings.concat(
+ validateOfferItemApprovalAndBalance(
+ orderParameters,
+ offerItemIndex,
+ seaportAddress
+ )
+ );
+ }
+
+ /**
+ * @notice Validates the OfferItem parameters. This includes token contract validation
+ * @dev OfferItems with criteria are currently not allowed
+ * @param orderParameters The parameters for the order to validate
+ * @param offerItemIndex The index of the offerItem in offer array to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOfferItemParameters(
+ OrderParameters memory orderParameters,
+ uint256 offerItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Get the offer item at offerItemIndex
+ OfferItem memory offerItem = orderParameters.offer[offerItemIndex];
+
+ // Check if start amount and end amount are zero
+ if (offerItem.startAmount == 0 && offerItem.endAmount == 0) {
+ errorsAndWarnings.addError(OfferIssue.AmountZero.parseInt());
+ return errorsAndWarnings;
+ }
+
+ // Check that amount velocity is not too high.
+ if (
+ offerItem.startAmount != offerItem.endAmount &&
+ orderParameters.endTime > orderParameters.startTime
+ ) {
+ // Assign larger and smaller amount values
+ (uint256 maxAmount, uint256 minAmount) = offerItem.startAmount >
+ offerItem.endAmount
+ ? (offerItem.startAmount, offerItem.endAmount)
+ : (offerItem.endAmount, offerItem.startAmount);
+
+ uint256 amountDelta = maxAmount - minAmount;
+ // delta of time that order exists for
+ uint256 timeDelta = orderParameters.endTime -
+ orderParameters.startTime;
+
+ // Velocity scaled by 1e10 for precision
+ uint256 velocity = (amountDelta * 1e10) / timeDelta;
+ // gives velocity percentage in hundredth of a basis points per second in terms of larger value
+ uint256 velocityPercentage = velocity / (maxAmount * 1e4);
+
+ // 278 * 60 * 30 ~= 500,000
+ if (velocityPercentage > 278) {
+ // Over 50% change per 30 min
+ errorsAndWarnings.addError(
+ OfferIssue.AmountVelocityHigh.parseInt()
+ );
+ }
+ // Over 50% change per 30 min
+ else if (velocityPercentage > 28) {
+ // Over 5% change per 30 min
+ errorsAndWarnings.addWarning(
+ OfferIssue.AmountVelocityHigh.parseInt()
+ );
+ }
+
+ // Check for large amount steps
+ if (minAmount <= 1e15) {
+ errorsAndWarnings.addWarning(
+ OfferIssue.AmountStepLarge.parseInt()
+ );
+ }
+ }
+
+ if (offerItem.itemType == ItemType.ERC721) {
+ // ERC721 type requires amounts to be 1
+ if (offerItem.startAmount != 1 || offerItem.endAmount != 1) {
+ errorsAndWarnings.addError(ERC721Issue.AmountNotOne.parseInt());
+ }
+
+ // Check the EIP165 token interface
+ if (!checkInterface(offerItem.token, ERC721_INTERFACE_ID)) {
+ errorsAndWarnings.addError(ERC721Issue.InvalidToken.parseInt());
+ }
+ } else if (offerItem.itemType == ItemType.ERC721_WITH_CRITERIA) {
+ // Check the EIP165 token interface
+ if (!checkInterface(offerItem.token, ERC721_INTERFACE_ID)) {
+ errorsAndWarnings.addError(ERC721Issue.InvalidToken.parseInt());
+ }
+
+ if (offerItem.startAmount > 1 || offerItem.endAmount > 1) {
+ // Require partial fill enabled. Even orderTypes are full
+ if (uint8(orderParameters.orderType) % 2 == 0) {
+ errorsAndWarnings.addError(
+ ERC721Issue.CriteriaNotPartialFill.parseInt()
+ );
+ }
+ }
+ } else if (
+ offerItem.itemType == ItemType.ERC1155 ||
+ offerItem.itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ // Check the EIP165 token interface
+ if (!checkInterface(offerItem.token, ERC1155_INTERFACE_ID)) {
+ errorsAndWarnings.addError(
+ ERC1155Issue.InvalidToken.parseInt()
+ );
+ }
+ } else if (offerItem.itemType == ItemType.ERC20) {
+ // ERC20 must have `identifierOrCriteria` be zero
+ if (offerItem.identifierOrCriteria != 0) {
+ errorsAndWarnings.addError(
+ ERC20Issue.IdentifierNonZero.parseInt()
+ );
+ }
+
+ // Validate contract, should return an uint256 if its an ERC20
+ if (
+ !offerItem.token.safeStaticCallUint256(
+ abi.encodeWithSelector(
+ ERC20Interface.allowance.selector,
+ seaportAddress,
+ seaportAddress
+ ),
+ 0
+ )
+ ) {
+ errorsAndWarnings.addError(ERC20Issue.InvalidToken.parseInt());
+ }
+ } else {
+ // Must be native
+ // NATIVE must have `token` be zero address
+ if (offerItem.token != address(0)) {
+ errorsAndWarnings.addError(NativeIssue.TokenAddress.parseInt());
+ }
+
+ // NATIVE must have `identifierOrCriteria` be zero
+ if (offerItem.identifierOrCriteria != 0) {
+ errorsAndWarnings.addError(
+ NativeIssue.IdentifierNonZero.parseInt()
+ );
+ }
+ }
+ }
+
+ /**
+ * @notice Validates the OfferItem approvals and balances
+ * @param orderParameters The parameters for the order to validate
+ * @param offerItemIndex The index of the offerItem in offer array to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOfferItemApprovalAndBalance(
+ OrderParameters memory orderParameters,
+ uint256 offerItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ // Note: If multiple items are of the same token, token amounts are not summed for validation
+
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Get the approval address for the given conduit key
+ (
+ address approvalAddress,
+ ErrorsAndWarnings memory ew
+ ) = getApprovalAddress(orderParameters.conduitKey, seaportAddress);
+ errorsAndWarnings.concat(ew);
+
+ if (ew.hasErrors()) {
+ // Approval address is invalid
+ return errorsAndWarnings;
+ }
+
+ // Get the offer item at offerItemIndex
+ OfferItem memory offerItem = orderParameters.offer[offerItemIndex];
+
+ if (offerItem.itemType == ItemType.ERC721) {
+ ERC721Interface token = ERC721Interface(offerItem.token);
+
+ // Check that offerer owns token
+ if (
+ !address(token).safeStaticCallAddress(
+ abi.encodeWithSelector(
+ ERC721Interface.ownerOf.selector,
+ offerItem.identifierOrCriteria
+ ),
+ orderParameters.offerer
+ )
+ ) {
+ errorsAndWarnings.addError(ERC721Issue.NotOwner.parseInt());
+ }
+
+ // Check for approval via `getApproved`
+ if (
+ !address(token).safeStaticCallAddress(
+ abi.encodeWithSelector(
+ ERC721Interface.getApproved.selector,
+ offerItem.identifierOrCriteria
+ ),
+ approvalAddress
+ )
+ ) {
+ // Fallback to `isApprovalForAll`
+ if (
+ !address(token).safeStaticCallBool(
+ abi.encodeWithSelector(
+ ERC721Interface.isApprovedForAll.selector,
+ orderParameters.offerer,
+ approvalAddress
+ ),
+ true
+ )
+ ) {
+ // Not approved
+ errorsAndWarnings.addError(
+ ERC721Issue.NotApproved.parseInt()
+ );
+ }
+ }
+ } else if (offerItem.itemType == ItemType.ERC721_WITH_CRITERIA) {
+ ERC721Interface token = ERC721Interface(offerItem.token);
+
+ // Check for approval
+ if (
+ !address(token).safeStaticCallBool(
+ abi.encodeWithSelector(
+ ERC721Interface.isApprovedForAll.selector,
+ orderParameters.offerer,
+ approvalAddress
+ ),
+ true
+ )
+ ) {
+ // Not approved
+ errorsAndWarnings.addError(ERC721Issue.NotApproved.parseInt());
+ }
+ } else if (offerItem.itemType == ItemType.ERC1155) {
+ ERC1155Interface token = ERC1155Interface(offerItem.token);
+
+ // Check for approval
+ if (
+ !address(token).safeStaticCallBool(
+ abi.encodeWithSelector(
+ ERC1155Interface.isApprovedForAll.selector,
+ orderParameters.offerer,
+ approvalAddress
+ ),
+ true
+ )
+ ) {
+ errorsAndWarnings.addError(ERC1155Issue.NotApproved.parseInt());
+ }
+
+ // Get min required balance (max(startAmount, endAmount))
+ uint256 minBalance = offerItem.startAmount < offerItem.endAmount
+ ? offerItem.startAmount
+ : offerItem.endAmount;
+
+ // Check for sufficient balance
+ if (
+ !address(token).safeStaticCallUint256(
+ abi.encodeWithSelector(
+ ERC1155Interface.balanceOf.selector,
+ orderParameters.offerer,
+ offerItem.identifierOrCriteria
+ ),
+ minBalance
+ )
+ ) {
+ // Insufficient balance
+ errorsAndWarnings.addError(
+ ERC1155Issue.InsufficientBalance.parseInt()
+ );
+ }
+ } else if (offerItem.itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ ERC1155Interface token = ERC1155Interface(offerItem.token);
+
+ // Check for approval
+ if (
+ !address(token).safeStaticCallBool(
+ abi.encodeWithSelector(
+ ERC1155Interface.isApprovedForAll.selector,
+ orderParameters.offerer,
+ approvalAddress
+ ),
+ true
+ )
+ ) {
+ errorsAndWarnings.addError(ERC1155Issue.NotApproved.parseInt());
+ }
+ } else if (offerItem.itemType == ItemType.ERC20) {
+ ERC20Interface token = ERC20Interface(offerItem.token);
+
+ // Get min required balance and approval (max(startAmount, endAmount))
+ uint256 minBalanceAndAllowance = offerItem.startAmount <
+ offerItem.endAmount
+ ? offerItem.startAmount
+ : offerItem.endAmount;
+
+ // Check allowance
+ if (
+ !address(token).safeStaticCallUint256(
+ abi.encodeWithSelector(
+ ERC20Interface.allowance.selector,
+ orderParameters.offerer,
+ approvalAddress
+ ),
+ minBalanceAndAllowance
+ )
+ ) {
+ errorsAndWarnings.addError(
+ ERC20Issue.InsufficientAllowance.parseInt()
+ );
+ }
+
+ // Check balance
+ if (
+ !address(token).safeStaticCallUint256(
+ abi.encodeWithSelector(
+ ERC20Interface.balanceOf.selector,
+ orderParameters.offerer
+ ),
+ minBalanceAndAllowance
+ )
+ ) {
+ errorsAndWarnings.addError(
+ ERC20Issue.InsufficientBalance.parseInt()
+ );
+ }
+ } else {
+ // Must be native
+ // Get min required balance (max(startAmount, endAmount))
+ uint256 minBalance = offerItem.startAmount < offerItem.endAmount
+ ? offerItem.startAmount
+ : offerItem.endAmount;
+
+ // Check for sufficient balance
+ if (orderParameters.offerer.balance < minBalance) {
+ errorsAndWarnings.addError(
+ NativeIssue.InsufficientBalance.parseInt()
+ );
+ }
+
+ // Native items can not be pulled so warn
+ errorsAndWarnings.addWarning(OfferIssue.NativeItem.parseInt());
+ }
+ }
+
+ /**
+ * @notice Validate all consideration items for an order
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItems(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ return
+ _helper.validateConsiderationItems(orderParameters, seaportAddress);
+ }
+
+ /**
+ * @notice Validate a consideration item
+ * @param orderParameters The parameters for the order to validate
+ * @param considerationItemIndex The index of the consideration item to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItem(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ return
+ _helper.validateConsiderationItem(
+ orderParameters,
+ considerationItemIndex,
+ seaportAddress
+ );
+ }
+
+ /**
+ * @notice Validates the parameters of a consideration item including contract validation
+ * @param orderParameters The parameters for the order to validate
+ * @param considerationItemIndex The index of the consideration item to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItemParameters(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ return
+ _helper.validateConsiderationItemParameters(
+ orderParameters,
+ considerationItemIndex,
+ seaportAddress
+ );
+ }
+
+ /**
+ * @notice Validates the zone call for an order
+ * @param orderParameters The order parameters for the order to validate
+ * @param zoneParameters The zone parameters for the order to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOrderWithZone(
+ OrderParameters memory orderParameters,
+ ZoneParameters memory zoneParameters
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Call isValidZone to check if zone is set and implements EIP165
+ errorsAndWarnings.concat(isValidZone(orderParameters));
+
+ // Call zone function `validateOrder` with the supplied ZoneParameters
+ if (
+ !orderParameters.zone.safeStaticCallBytes4(
+ abi.encodeWithSelector(
+ ZoneInterface.validateOrder.selector,
+ zoneParameters
+ ),
+ ZoneInterface.validateOrder.selector
+ )
+ ) {
+ // Call to validateOrder reverted or returned invalid magic value
+ errorsAndWarnings.addWarning(ZoneIssue.RejectedOrder.parseInt());
+ }
+ }
+
+ /**
+ * @notice Safely check that a contract implements an interface
+ * @param token The token address to check
+ * @param interfaceHash The interface hash to check
+ */
+ function checkInterface(
+ address token,
+ bytes4 interfaceHash
+ ) public view returns (bool) {
+ return
+ token.safeStaticCallBool(
+ abi.encodeWithSelector(
+ IERC165.supportsInterface.selector,
+ interfaceHash
+ ),
+ true
+ );
+ }
+
+ function isPaymentToken(ItemType itemType) public pure returns (bool) {
+ return itemType == ItemType.NATIVE || itemType == ItemType.ERC20;
+ }
+
+ /*//////////////////////////////////////////////////////////////
+ Merkle Helpers
+ //////////////////////////////////////////////////////////////*/
+
+ /**
+ * @notice Sorts an array of token ids by the keccak256 hash of the id. Required ordering of ids
+ * for other merkle operations.
+ * @param includedTokens An array of included token ids.
+ * @return sortedTokens The sorted `includedTokens` array.
+ */
+ function sortMerkleTokens(
+ uint256[] memory includedTokens
+ ) public view returns (uint256[] memory sortedTokens) {
+ // Sort token ids by the keccak256 hash of the id
+ return _helper.sortMerkleTokens(includedTokens);
+ }
+
+ /**
+ * @notice Creates a merkle root for includedTokens.
+ * @dev `includedTokens` must be sorting in strictly ascending order according to the keccak256 hash of the value.
+ * @return merkleRoot The merkle root
+ * @return errorsAndWarnings Errors and warnings from the operation
+ */
+ function getMerkleRoot(
+ uint256[] memory includedTokens
+ )
+ public
+ view
+ returns (bytes32 merkleRoot, ErrorsAndWarnings memory errorsAndWarnings)
+ {
+ return _helper.getMerkleRoot(includedTokens);
+ }
+
+ /**
+ * @notice Creates a merkle proof for the the targetIndex contained in includedTokens.
+ * @dev `targetIndex` is referring to the index of an element in `includedTokens`.
+ * `includedTokens` must be sorting in ascending order according to the keccak256 hash of the value.
+ * @return merkleProof The merkle proof
+ * @return errorsAndWarnings Errors and warnings from the operation
+ */
+ function getMerkleProof(
+ uint256[] memory includedTokens,
+ uint256 targetIndex
+ )
+ public
+ view
+ returns (
+ bytes32[] memory merkleProof,
+ ErrorsAndWarnings memory errorsAndWarnings
+ )
+ {
+ return _helper.getMerkleProof(includedTokens, targetIndex);
+ }
+
+ /**
+ * @notice Verifies a merkle proof for the value to prove and given root and proof.
+ * @dev The `valueToProve` is hashed prior to executing the proof verification.
+ * @param merkleRoot The root of the merkle tree
+ * @param merkleProof The merkle proof
+ * @param valueToProve The value to prove
+ * @return whether proof is valid
+ */
+ function verifyMerkleProof(
+ bytes32 merkleRoot,
+ bytes32[] memory merkleProof,
+ uint256 valueToProve
+ ) public view returns (bool) {
+ return _helper.verifyMerkleProof(merkleRoot, merkleProof, valueToProve);
+ }
+}
diff --git a/contracts/helpers/order-validator/lib/ConsiderationTypeHashes.sol b/contracts/helpers/order-validator/lib/ConsiderationTypeHashes.sol
new file mode 100644
index 000000000..3b987d018
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/ConsiderationTypeHashes.sol
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import "../../../lib/ConsiderationStructs.sol";
+
+uint256 constant EIP712_Order_size = 0x180;
+uint256 constant EIP712_OfferItem_size = 0xc0;
+uint256 constant EIP712_ConsiderationItem_size = 0xe0;
+uint256 constant EIP712_DomainSeparator_offset = 0x02;
+uint256 constant EIP712_OrderHash_offset = 0x22;
+uint256 constant EIP712_DigestPayload_size = 0x42;
+uint256 constant EIP_712_PREFIX = (
+ 0x1901000000000000000000000000000000000000000000000000000000000000
+);
+
+contract ConsiderationTypeHashes {
+ bytes32 internal immutable _NAME_HASH;
+ bytes32 internal immutable _VERSION_HASH;
+ bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH;
+ bytes32 internal immutable _OFFER_ITEM_TYPEHASH;
+ bytes32 internal immutable _CONSIDERATION_ITEM_TYPEHASH;
+ bytes32 internal immutable _ORDER_TYPEHASH;
+ bytes32 internal immutable _DOMAIN_SEPARATOR;
+ address internal constant seaportAddress =
+ address(0x00000000000006c7676171937C444f6BDe3D6282);
+
+ constructor() {
+ // Derive hash of the name of the contract.
+ _NAME_HASH = keccak256(bytes("Seaport"));
+
+ // Derive hash of the version string of the contract.
+ _VERSION_HASH = keccak256(bytes("1.2"));
+
+ bytes memory offerItemTypeString = abi.encodePacked(
+ "OfferItem(",
+ "uint8 itemType,",
+ "address token,",
+ "uint256 identifierOrCriteria,",
+ "uint256 startAmount,",
+ "uint256 endAmount",
+ ")"
+ );
+
+ // Construct the ConsiderationItem type string.
+ // prettier-ignore
+ bytes memory considerationItemTypeString = abi.encodePacked(
+ "ConsiderationItem(",
+ "uint8 itemType,",
+ "address token,",
+ "uint256 identifierOrCriteria,",
+ "uint256 startAmount,",
+ "uint256 endAmount,",
+ "address recipient",
+ ")"
+ );
+
+ // Construct the OrderComponents type string, not including the above.
+ // prettier-ignore
+ bytes memory orderComponentsPartialTypeString = abi.encodePacked(
+ "OrderComponents(",
+ "address offerer,",
+ "address zone,",
+ "OfferItem[] offer,",
+ "ConsiderationItem[] consideration,",
+ "uint8 orderType,",
+ "uint256 startTime,",
+ "uint256 endTime,",
+ "bytes32 zoneHash,",
+ "uint256 salt,",
+ "bytes32 conduitKey,",
+ "uint256 counter",
+ ")"
+ );
+ // Derive the OfferItem type hash using the corresponding type string.
+ bytes32 offerItemTypehash = keccak256(offerItemTypeString);
+
+ // Derive ConsiderationItem type hash using corresponding type string.
+ bytes32 considerationItemTypehash = keccak256(
+ considerationItemTypeString
+ );
+
+ // Construct the primary EIP-712 domain type string.
+ // prettier-ignore
+ _EIP_712_DOMAIN_TYPEHASH = keccak256(
+ abi.encodePacked(
+ "EIP712Domain(",
+ "string name,",
+ "string version,",
+ "uint256 chainId,",
+ "address verifyingContract",
+ ")"
+ )
+ );
+
+ _OFFER_ITEM_TYPEHASH = offerItemTypehash;
+ _CONSIDERATION_ITEM_TYPEHASH = considerationItemTypehash;
+
+ // Derive OrderItem type hash via combination of relevant type strings.
+ _ORDER_TYPEHASH = keccak256(
+ abi.encodePacked(
+ orderComponentsPartialTypeString,
+ considerationItemTypeString,
+ offerItemTypeString
+ )
+ );
+
+ _DOMAIN_SEPARATOR = _deriveDomainSeparator();
+ }
+
+ /**
+ * @dev Internal view function to derive the EIP-712 domain separator.
+ *
+ * @return The derived domain separator.
+ */
+ function _deriveDomainSeparator() internal view returns (bytes32) {
+ // prettier-ignore
+ return keccak256(
+ abi.encode(
+ _EIP_712_DOMAIN_TYPEHASH,
+ _NAME_HASH,
+ _VERSION_HASH,
+ block.chainid,
+ seaportAddress
+ )
+ );
+ }
+
+ /**
+ * @dev Internal pure function to efficiently derive an digest to sign for
+ * an order in accordance with EIP-712.
+ *
+ * @param orderHash The order hash.
+ *
+ * @return value The hash.
+ */
+ function _deriveEIP712Digest(
+ bytes32 orderHash
+ ) internal view returns (bytes32 value) {
+ bytes32 domainSeparator = _DOMAIN_SEPARATOR;
+ // Leverage scratch space to perform an efficient hash.
+ assembly {
+ // Place the EIP-712 prefix at the start of scratch space.
+ mstore(0, EIP_712_PREFIX)
+
+ // Place the domain separator in the next region of scratch space.
+ mstore(EIP712_DomainSeparator_offset, domainSeparator)
+
+ // Place the order hash in scratch space, spilling into the first
+ // two bytes of the free memory pointer — this should never be set
+ // as memory cannot be expanded to that size, and will be zeroed out
+ // after the hash is performed.
+ mstore(EIP712_OrderHash_offset, orderHash)
+
+ // Hash the relevant region (65 bytes).
+ value := keccak256(0, EIP712_DigestPayload_size)
+
+ // Clear out the dirtied bits in the memory pointer.
+ mstore(EIP712_OrderHash_offset, 0)
+ }
+ }
+
+ /**
+ * @dev Internal view function to derive the EIP-712 hash for an offer item.
+ *
+ * @param offerItem The offered item to hash.
+ *
+ * @return The hash.
+ */
+ function _hashOfferItem(
+ OfferItem memory offerItem
+ ) internal view returns (bytes32) {
+ return
+ keccak256(
+ abi.encode(
+ _OFFER_ITEM_TYPEHASH,
+ offerItem.itemType,
+ offerItem.token,
+ offerItem.identifierOrCriteria,
+ offerItem.startAmount,
+ offerItem.endAmount
+ )
+ );
+ }
+
+ /**
+ * @dev Internal view function to derive the EIP-712 hash for a consideration item.
+ *
+ * @param considerationItem The consideration item to hash.
+ *
+ * @return The hash.
+ */
+ function _hashConsiderationItem(
+ ConsiderationItem memory considerationItem
+ ) internal view returns (bytes32) {
+ return
+ keccak256(
+ abi.encode(
+ _CONSIDERATION_ITEM_TYPEHASH,
+ considerationItem.itemType,
+ considerationItem.token,
+ considerationItem.identifierOrCriteria,
+ considerationItem.startAmount,
+ considerationItem.endAmount,
+ considerationItem.recipient
+ )
+ );
+ }
+
+ /**
+ * @dev Internal view function to derive the order hash for a given order.
+ * Note that only the original consideration items are included in the
+ * order hash, as additional consideration items may be supplied by the
+ * caller.
+ *
+ * @param orderParameters The parameters of the order to hash.
+ * @param counter The counter of the order to hash.
+ *
+ * @return orderHash The hash.
+ */
+ function _deriveOrderHash(
+ OrderParameters memory orderParameters,
+ uint256 counter
+ ) internal view returns (bytes32 orderHash) {
+ // Designate new memory regions for offer and consideration item hashes.
+ bytes32[] memory offerHashes = new bytes32[](
+ orderParameters.offer.length
+ );
+ bytes32[] memory considerationHashes = new bytes32[](
+ orderParameters.totalOriginalConsiderationItems
+ );
+
+ // Iterate over each offer on the order.
+ for (uint256 i = 0; i < orderParameters.offer.length; ++i) {
+ // Hash the offer and place the result into memory.
+ offerHashes[i] = _hashOfferItem(orderParameters.offer[i]);
+ }
+
+ // Iterate over each consideration on the order.
+ for (
+ uint256 i = 0;
+ i < orderParameters.totalOriginalConsiderationItems;
+ ++i
+ ) {
+ // Hash the consideration and place the result into memory.
+ considerationHashes[i] = _hashConsiderationItem(
+ orderParameters.consideration[i]
+ );
+ }
+
+ // Derive and return the order hash as specified by EIP-712.
+
+ return
+ keccak256(
+ abi.encode(
+ _ORDER_TYPEHASH,
+ orderParameters.offerer,
+ orderParameters.zone,
+ keccak256(abi.encodePacked(offerHashes)),
+ keccak256(abi.encodePacked(considerationHashes)),
+ orderParameters.orderType,
+ orderParameters.startTime,
+ orderParameters.endTime,
+ orderParameters.zoneHash,
+ orderParameters.salt,
+ orderParameters.conduitKey,
+ counter
+ )
+ );
+ }
+}
diff --git a/contracts/helpers/order-validator/lib/ErrorsAndWarnings.sol b/contracts/helpers/order-validator/lib/ErrorsAndWarnings.sol
new file mode 100644
index 000000000..e9934f446
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/ErrorsAndWarnings.sol
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import {
+ ConduitIssue,
+ ConsiderationIssue,
+ ERC20Issue,
+ ERC721Issue,
+ ERC1155Issue,
+ GenericIssue,
+ OfferIssue,
+ SignatureIssue,
+ StatusIssue,
+ TimeIssue,
+ NativeIssue,
+ IssueParser
+} from "./SeaportValidatorTypes.sol";
+
+struct ErrorsAndWarnings {
+ uint16[] errors;
+ uint16[] warnings;
+}
+
+library ErrorsAndWarningsLib {
+ using IssueParser for ConduitIssue;
+ using IssueParser for ConsiderationIssue;
+ using IssueParser for ERC20Issue;
+ using IssueParser for ERC721Issue;
+ using IssueParser for ERC1155Issue;
+ using IssueParser for GenericIssue;
+ using IssueParser for OfferIssue;
+ using IssueParser for SignatureIssue;
+ using IssueParser for StatusIssue;
+ using IssueParser for TimeIssue;
+ using IssueParser for NativeIssue;
+
+ function concat(
+ ErrorsAndWarnings memory ew1,
+ ErrorsAndWarnings memory ew2
+ ) internal pure {
+ ew1.errors = concatMemory(ew1.errors, ew2.errors);
+ ew1.warnings = concatMemory(ew1.warnings, ew2.warnings);
+ }
+
+ function empty() internal pure returns (ErrorsAndWarnings memory) {
+ return ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+ }
+
+ function addError(
+ uint16 err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ ErrorsAndWarnings memory ew = ErrorsAndWarnings(
+ new uint16[](0),
+ new uint16[](0)
+ );
+ ew.errors = pushMemory(ew.errors, err);
+ return ew;
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ uint16 err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ ew.errors = pushMemory(ew.errors, err);
+ return ew;
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ GenericIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ ERC20Issue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ ERC721Issue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ ERC1155Issue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ ConsiderationIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ OfferIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ SignatureIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ TimeIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ ConduitIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addError(
+ ErrorsAndWarnings memory ew,
+ StatusIssue err
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addError(ew, err.parseInt());
+ }
+
+ function addWarning(
+ uint16 warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ ErrorsAndWarnings memory ew = ErrorsAndWarnings(
+ new uint16[](0),
+ new uint16[](0)
+ );
+ ew.warnings = pushMemory(ew.warnings, warn);
+ return ew;
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ uint16 warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ ew.warnings = pushMemory(ew.warnings, warn);
+ return ew;
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ GenericIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ ERC20Issue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ ERC721Issue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ ERC1155Issue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ OfferIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ ConsiderationIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ SignatureIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ TimeIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ ConduitIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ StatusIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function addWarning(
+ ErrorsAndWarnings memory ew,
+ NativeIssue warn
+ ) internal pure returns (ErrorsAndWarnings memory) {
+ return addWarning(ew, warn.parseInt());
+ }
+
+ function hasErrors(
+ ErrorsAndWarnings memory ew
+ ) internal pure returns (bool) {
+ return ew.errors.length != 0;
+ }
+
+ function hasWarnings(
+ ErrorsAndWarnings memory ew
+ ) internal pure returns (bool) {
+ return ew.warnings.length != 0;
+ }
+
+ // Helper Functions
+ function concatMemory(
+ uint16[] memory array1,
+ uint16[] memory array2
+ ) private pure returns (uint16[] memory) {
+ if (array1.length == 0) {
+ return array2;
+ } else if (array2.length == 0) {
+ return array1;
+ }
+
+ uint16[] memory returnValue = new uint16[](
+ array1.length + array2.length
+ );
+
+ for (uint256 i = 0; i < array1.length; i++) {
+ returnValue[i] = array1[i];
+ }
+ for (uint256 i = 0; i < array2.length; i++) {
+ returnValue[i + array1.length] = array2[i];
+ }
+
+ return returnValue;
+ }
+
+ function pushMemory(
+ uint16[] memory uint16Array,
+ uint16 newValue
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory returnValue = new uint16[](uint16Array.length + 1);
+
+ for (uint256 i = 0; i < uint16Array.length; i++) {
+ returnValue[i] = uint16Array[i];
+ }
+ returnValue[uint16Array.length] = newValue;
+
+ return returnValue;
+ }
+}
diff --git a/contracts/helpers/order-validator/lib/Murky.sol b/contracts/helpers/order-validator/lib/Murky.sol
new file mode 100644
index 000000000..d49675a3c
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/Murky.sol
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import {
+ ErrorsAndWarnings,
+ ErrorsAndWarningsLib
+} from "./ErrorsAndWarnings.sol";
+
+import { IssueParser, MerkleIssue } from "./SeaportValidatorTypes.sol";
+
+contract Murky {
+ using ErrorsAndWarningsLib for ErrorsAndWarnings;
+ using IssueParser for MerkleIssue;
+
+ bool internal constant HASH_ODD_WITH_ZERO = false;
+
+ function _verifyProof(
+ bytes32 root,
+ bytes32[] memory proof,
+ bytes32 valueToProve
+ ) internal pure returns (bool) {
+ // proof length must be less than max array size
+ bytes32 rollingHash = valueToProve;
+ uint256 length = proof.length;
+ unchecked {
+ for (uint256 i = 0; i < length; ++i) {
+ rollingHash = _hashLeafPairs(rollingHash, proof[i]);
+ }
+ }
+ return root == rollingHash;
+ }
+
+ /********************
+ * HASHING FUNCTION *
+ ********************/
+
+ /// ascending sort and concat prior to hashing
+ function _hashLeafPairs(bytes32 left, bytes32 right)
+ internal
+ pure
+ returns (bytes32 _hash)
+ {
+ assembly {
+ switch lt(left, right)
+ case 0 {
+ mstore(0x0, right)
+ mstore(0x20, left)
+ }
+ default {
+ mstore(0x0, left)
+ mstore(0x20, right)
+ }
+ _hash := keccak256(0x0, 0x40)
+ }
+ }
+
+ /********************
+ * PROOF GENERATION *
+ ********************/
+
+ function _getRoot(uint256[] memory data)
+ internal
+ pure
+ returns (bytes32 result, ErrorsAndWarnings memory errorsAndWarnings)
+ {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ if (data.length < 2) {
+ errorsAndWarnings.addError(MerkleIssue.SingleLeaf.parseInt());
+ return (0, errorsAndWarnings);
+ }
+
+ bool hashOddWithZero = HASH_ODD_WITH_ZERO;
+
+ if (!_processInput(data)) {
+ errorsAndWarnings.addError(MerkleIssue.Unsorted.parseInt());
+ return (0, errorsAndWarnings);
+ }
+
+ assembly {
+ function hashLeafPairs(left, right) -> _hash {
+ switch lt(left, right)
+ case 0 {
+ mstore(0x0, right)
+ mstore(0x20, left)
+ }
+ default {
+ mstore(0x0, left)
+ mstore(0x20, right)
+ }
+ _hash := keccak256(0x0, 0x40)
+ }
+ function hashLevel(_data, length, _hashOddWithZero) -> newLength {
+ // we will be modifying data in-place, so set result pointer to data pointer
+ let _result := _data
+ // get length of original data array
+ // let length := mload(_data)
+ // bool to track if we need to hash the last element of an odd-length array with zero
+ let oddLength
+
+ // if length is odd, we need to hash the last element with zero
+ switch and(length, 1)
+ case 1 {
+ // if length is odd, add 1 so division by 2 will round up
+ newLength := add(1, div(length, 2))
+ oddLength := 1
+ }
+ default {
+ newLength := div(length, 2)
+ }
+ // todo: necessary?
+ // mstore(_data, newLength)
+ let resultIndexPointer := add(0x20, _data)
+ let dataIndexPointer := resultIndexPointer
+
+ // stop iterating over for loop at length-1
+ let stopIteration := add(_data, mul(length, 0x20))
+ // write result array in-place over data array
+ for {
+
+ } lt(dataIndexPointer, stopIteration) {
+
+ } {
+ // get next two elements from data, hash them together
+ let data1 := mload(dataIndexPointer)
+ let data2 := mload(add(dataIndexPointer, 0x20))
+ let hashedPair := hashLeafPairs(data1, data2)
+ // overwrite an element of data array with
+ mstore(resultIndexPointer, hashedPair)
+ // increment result pointer by 1 slot
+ resultIndexPointer := add(0x20, resultIndexPointer)
+ // increment data pointer by 2 slot
+ dataIndexPointer := add(0x40, dataIndexPointer)
+ }
+ // we did not yet hash last index if odd-length
+ if oddLength {
+ let data1 := mload(dataIndexPointer)
+ let nextValue
+ switch _hashOddWithZero
+ case 0 {
+ nextValue := data1
+ }
+ default {
+ nextValue := hashLeafPairs(data1, 0)
+ }
+ mstore(resultIndexPointer, nextValue)
+ }
+ }
+
+ let dataLength := mload(data)
+ for {
+
+ } gt(dataLength, 1) {
+
+ } {
+ dataLength := hashLevel(data, dataLength, hashOddWithZero)
+ }
+ result := mload(add(0x20, data))
+ }
+ }
+
+ function _getProof(uint256[] memory data, uint256 node)
+ internal
+ pure
+ returns (
+ bytes32[] memory result,
+ ErrorsAndWarnings memory errorsAndWarnings
+ )
+ {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ if (data.length < 2) {
+ errorsAndWarnings.addError(MerkleIssue.SingleLeaf.parseInt());
+ return (new bytes32[](0), errorsAndWarnings);
+ }
+
+ bool hashOddWithZero = HASH_ODD_WITH_ZERO;
+
+ if (!_processInput(data)) {
+ errorsAndWarnings.addError(MerkleIssue.Unsorted.parseInt());
+ return (new bytes32[](0), errorsAndWarnings);
+ }
+
+ // The size of the proof is equal to the ceiling of log2(numLeaves)
+ // Two overflow risks: node, pos
+ // node: max array size is 2**256-1. Largest index in the array will be 1 less than that. Also,
+ // for dynamic arrays, size is limited to 2**64-1
+ // pos: pos is bounded by log2(data.length), which should be less than type(uint256).max
+ assembly {
+ function hashLeafPairs(left, right) -> _hash {
+ switch lt(left, right)
+ case 0 {
+ mstore(0x0, right)
+ mstore(0x20, left)
+ }
+ default {
+ mstore(0x0, left)
+ mstore(0x20, right)
+ }
+ _hash := keccak256(0x0, 0x40)
+ }
+ function hashLevel(_data, length, _hashOddWithZero) -> newLength {
+ // we will be modifying data in-place, so set result pointer to data pointer
+ let _result := _data
+ // get length of original data array
+ // let length := mload(_data)
+ // bool to track if we need to hash the last element of an odd-length array with zero
+ let oddLength
+
+ // if length is odd, we'll need to hash the last element with zero
+ switch and(length, 1)
+ case 1 {
+ // if length is odd, add 1 so division by 2 will round up
+ newLength := add(1, div(length, 2))
+ oddLength := 1
+ }
+ default {
+ newLength := div(length, 2)
+ }
+ // todo: necessary?
+ // mstore(_data, newLength)
+ let resultIndexPointer := add(0x20, _data)
+ let dataIndexPointer := resultIndexPointer
+
+ // stop iterating over for loop at length-1
+ let stopIteration := add(_data, mul(length, 0x20))
+ // write result array in-place over data array
+ for {
+
+ } lt(dataIndexPointer, stopIteration) {
+
+ } {
+ // get next two elements from data, hash them together
+ let data1 := mload(dataIndexPointer)
+ let data2 := mload(add(dataIndexPointer, 0x20))
+ let hashedPair := hashLeafPairs(data1, data2)
+ // overwrite an element of data array with
+ mstore(resultIndexPointer, hashedPair)
+ // increment result pointer by 1 slot
+ resultIndexPointer := add(0x20, resultIndexPointer)
+ // increment data pointer by 2 slot
+ dataIndexPointer := add(0x40, dataIndexPointer)
+ }
+ // we did not yet hash last index if odd-length
+ if oddLength {
+ let data1 := mload(dataIndexPointer)
+ let nextValue
+ switch _hashOddWithZero
+ case 0 {
+ nextValue := data1
+ }
+ default {
+ nextValue := hashLeafPairs(data1, 0)
+ }
+ mstore(resultIndexPointer, nextValue)
+ }
+ }
+
+ // set result pointer to free memory
+ result := mload(0x40)
+ // get pointer to first index of result
+ let resultIndexPtr := add(0x20, result)
+ // declare so we can use later
+ let newLength
+ // put length of data onto stack
+ let dataLength := mload(data)
+ for {
+ // repeat until only one element is left
+ } gt(dataLength, 1) {
+
+ } {
+ // bool if node is odd
+ let oddNodeIndex := and(node, 1)
+ // bool if node is last
+ let lastNodeIndex := eq(dataLength, add(1, node))
+ // store both bools in one value so we can switch on it
+ let switchVal := or(shl(1, lastNodeIndex), oddNodeIndex)
+ switch switchVal
+ // 00 - neither odd nor last
+ case 0 {
+ // store data[node+1] at result[i]
+ // get pointer to result[node+1] by adding 2 to node and multiplying by 0x20
+ // to account for the fact that result points to array length, not first index
+ mstore(
+ resultIndexPtr,
+ mload(add(data, mul(0x20, add(2, node))))
+ )
+ }
+ // 10 - node is last
+ case 2 {
+ // store 0 at result[i]
+ mstore(resultIndexPtr, 0)
+ }
+ // 01 or 11 - node is odd (and possibly also last)
+ default {
+ // store data[node-1] at result[i]
+ mstore(resultIndexPtr, mload(add(data, mul(0x20, node))))
+ }
+ // increment result index
+ resultIndexPtr := add(0x20, resultIndexPtr)
+
+ // get new node index
+ node := div(node, 2)
+ // keep track of how long result array is
+ newLength := add(1, newLength)
+ // compute the next hash level, overwriting data, and get the new length
+ dataLength := hashLevel(data, dataLength, hashOddWithZero)
+ }
+ // store length of result array at pointer
+ mstore(result, newLength)
+ // set free mem pointer to word after end of result array
+ mstore(0x40, resultIndexPtr)
+ }
+ }
+
+ /**
+ * Hashes each element of the input array in place using keccak256
+ */
+ function _processInput(uint256[] memory data)
+ private
+ pure
+ returns (bool sorted)
+ {
+ sorted = true;
+
+ // Hash inputs with keccak256
+ for (uint256 i = 0; i < data.length; ++i) {
+ assembly {
+ mstore(
+ add(data, mul(0x20, add(1, i))),
+ keccak256(add(data, mul(0x20, add(1, i))), 0x20)
+ )
+ // for every element after the first, hashed value must be greater than the last one
+ if and(
+ gt(i, 0),
+ iszero(
+ gt(
+ mload(add(data, mul(0x20, add(1, i)))),
+ mload(add(data, mul(0x20, add(1, sub(i, 1)))))
+ )
+ )
+ ) {
+ sorted := 0 // Elements not ordered by hash
+ }
+ }
+ }
+ }
+
+ // Sort uint256 in order of the keccak256 hashes
+ struct HashAndIntTuple {
+ uint256 num;
+ bytes32 hash;
+ }
+
+ function _sortUint256ByHash(uint256[] memory values)
+ internal
+ pure
+ returns (uint256[] memory sortedValues)
+ {
+ HashAndIntTuple[] memory toSort = new HashAndIntTuple[](values.length);
+ for (uint256 i = 0; i < values.length; i++) {
+ toSort[i] = HashAndIntTuple(
+ values[i],
+ keccak256(abi.encode(values[i]))
+ );
+ }
+
+ _quickSort(toSort, 0, int256(toSort.length - 1));
+
+ sortedValues = new uint256[](values.length);
+ for (uint256 i = 0; i < values.length; i++) {
+ sortedValues[i] = toSort[i].num;
+ }
+ }
+
+ function _quickSort(
+ HashAndIntTuple[] memory arr,
+ int256 left,
+ int256 right
+ ) internal pure {
+ int256 i = left;
+ int256 j = right;
+ if (i == j) return;
+ bytes32 pivot = arr[uint256(left + (right - left) / 2)].hash;
+ while (i <= j) {
+ while (arr[uint256(i)].hash < pivot) i++;
+ while (pivot < arr[uint256(j)].hash) j--;
+ if (i <= j) {
+ (arr[uint256(i)], arr[uint256(j)]) = (
+ arr[uint256(j)],
+ arr[uint256(i)]
+ );
+ i++;
+ j--;
+ }
+ }
+ if (left < j) _quickSort(arr, left, j);
+ if (i < right) _quickSort(arr, i, right);
+ }
+}
diff --git a/contracts/helpers/order-validator/lib/SafeStaticCall.sol b/contracts/helpers/order-validator/lib/SafeStaticCall.sol
new file mode 100644
index 000000000..1963e01ad
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/SafeStaticCall.sol
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+library SafeStaticCall {
+ function safeStaticCallBool(
+ address target,
+ bytes memory callData,
+ bool expectedReturn
+ ) internal view returns (bool) {
+ (bool success, bytes memory res) = target.staticcall(callData);
+ if (!success) return false;
+ if (res.length != 32) return false;
+
+ if (
+ bytes32(res) &
+ 0x0000000000000000000000000000000000000000000000000000000000000001 !=
+ bytes32(res)
+ ) {
+ return false;
+ }
+
+ return expectedReturn ? res[31] == 0x01 : res[31] == 0;
+ }
+
+ function safeStaticCallAddress(
+ address target,
+ bytes memory callData,
+ address expectedReturn
+ ) internal view returns (bool) {
+ (bool success, bytes memory res) = target.staticcall(callData);
+ if (!success) return false;
+ if (res.length != 32) return false;
+
+ if (
+ bytes32(res) &
+ 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF !=
+ bytes32(res)
+ ) {
+ // Ensure only 20 bytes used
+ return false;
+ }
+
+ return abi.decode(res, (address)) == expectedReturn;
+ }
+
+ function safeStaticCallUint256(
+ address target,
+ bytes memory callData,
+ uint256 minExpectedReturn
+ ) internal view returns (bool) {
+ (bool success, bytes memory res) = target.staticcall(callData);
+ if (!success) return false;
+ if (res.length != 32) return false;
+
+ return abi.decode(res, (uint256)) >= minExpectedReturn;
+ }
+
+ function safeStaticCallBytes4(
+ address target,
+ bytes memory callData,
+ bytes4 expectedReturn
+ ) internal view returns (bool) {
+ (bool success, bytes memory res) = target.staticcall(callData);
+ if (!success) return false;
+ if (res.length != 32) return false;
+ if (
+ bytes32(res) &
+ 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 !=
+ bytes32(res)
+ ) {
+ // Ensure only 4 bytes used
+ return false;
+ }
+
+ return abi.decode(res, (bytes4)) == expectedReturn;
+ }
+}
diff --git a/contracts/helpers/order-validator/lib/SeaportValidatorHelper.sol b/contracts/helpers/order-validator/lib/SeaportValidatorHelper.sol
new file mode 100644
index 000000000..e597cb453
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/SeaportValidatorHelper.sol
@@ -0,0 +1,958 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ItemType } from "../../../lib/ConsiderationEnums.sol";
+import {
+ Order,
+ OrderParameters,
+ BasicOrderParameters,
+ OfferItem,
+ ConsiderationItem,
+ Schema,
+ ZoneParameters
+} from "../../../lib/ConsiderationStructs.sol";
+import { ConsiderationTypeHashes } from "../lib/ConsiderationTypeHashes.sol";
+import {
+ ConsiderationInterface
+} from "../../../interfaces/ConsiderationInterface.sol";
+import {
+ ConduitControllerInterface
+} from "../../../interfaces/ConduitControllerInterface.sol";
+import {
+ ContractOffererInterface
+} from "../../../interfaces/ContractOffererInterface.sol";
+import { ZoneInterface } from "../../../interfaces/ZoneInterface.sol";
+import { GettersAndDerivers } from "../../../lib/GettersAndDerivers.sol";
+import {
+ SeaportValidatorInterface
+} from "../lib/SeaportValidatorInterface.sol";
+import { ZoneInterface } from "../../../interfaces/ZoneInterface.sol";
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../../../interfaces/AbridgedTokenInterfaces.sol";
+import { IERC165 } from "../../../interfaces/IERC165.sol";
+import { IERC2981 } from "../../../interfaces/IERC2981.sol";
+import {
+ ErrorsAndWarnings,
+ ErrorsAndWarningsLib
+} from "../lib/ErrorsAndWarnings.sol";
+import { SafeStaticCall } from "../lib/SafeStaticCall.sol";
+import { Murky } from "../lib/Murky.sol";
+import {
+ IssueParser,
+ ValidationConfiguration,
+ TimeIssue,
+ StatusIssue,
+ OfferIssue,
+ ContractOffererIssue,
+ ConsiderationIssue,
+ PrimaryFeeIssue,
+ ERC721Issue,
+ ERC1155Issue,
+ ERC20Issue,
+ NativeIssue,
+ ZoneIssue,
+ ConduitIssue,
+ CreatorFeeIssue,
+ SignatureIssue,
+ GenericIssue,
+ ConsiderationItemConfiguration
+} from "../lib/SeaportValidatorTypes.sol";
+import { Verifiers } from "../../../lib/Verifiers.sol";
+
+/**
+ * @title SeaportValidator
+ * @author OpenSea Protocol Team
+ * @notice SeaportValidatorHelper assists in advanced validation to seaport orders.
+ */
+contract SeaportValidatorHelper is Murky {
+ using ErrorsAndWarningsLib for ErrorsAndWarnings;
+ using SafeStaticCall for address;
+ using IssueParser for *;
+
+ /// @notice Ethereum creator fee engine address
+ CreatorFeeEngineInterface public immutable creatorFeeEngine;
+
+ bytes4 public constant ERC20_INTERFACE_ID = 0x36372b07;
+
+ bytes4 public constant ERC721_INTERFACE_ID = 0x80ac58cd;
+
+ bytes4 public constant ERC1155_INTERFACE_ID = 0xd9b67a26;
+
+ constructor() {
+ address creatorFeeEngineAddress;
+ if (block.chainid == 1 || block.chainid == 31337) {
+ creatorFeeEngineAddress = 0x0385603ab55642cb4Dd5De3aE9e306809991804f;
+ } else if (block.chainid == 3) {
+ // Ropsten
+ creatorFeeEngineAddress = 0xFf5A6F7f36764aAD301B7C9E85A5277614Df5E26;
+ } else if (block.chainid == 4) {
+ // Rinkeby
+ creatorFeeEngineAddress = 0x8d17687ea9a6bb6efA24ec11DcFab01661b2ddcd;
+ } else if (block.chainid == 5) {
+ // Goerli
+ creatorFeeEngineAddress = 0xe7c9Cb6D966f76f3B5142167088927Bf34966a1f;
+ } else if (block.chainid == 42) {
+ // Kovan
+ creatorFeeEngineAddress = 0x54D88324cBedfFe1e62c9A59eBb310A11C295198;
+ } else if (block.chainid == 137) {
+ // Polygon
+ creatorFeeEngineAddress = 0x28EdFcF0Be7E86b07493466e7631a213bDe8eEF2;
+ } else if (block.chainid == 80001) {
+ // Mumbai
+ creatorFeeEngineAddress = 0x0a01E11887f727D1b1Cd81251eeEE9BEE4262D07;
+ } else {
+ // No creator fee engine for this chain
+ creatorFeeEngineAddress = address(0);
+ }
+
+ creatorFeeEngine = CreatorFeeEngineInterface(creatorFeeEngineAddress);
+ }
+
+ /**
+ * @notice Strict validation operates under tight assumptions. It validates primary
+ * fee, creator fee, private sale consideration, and overall order format.
+ * @dev Only checks first fee recipient provided by CreatorFeeEngine.
+ * Order of consideration items must be as follows:
+ * 1. Primary consideration
+ * 2. Primary fee
+ * 3. Creator fee
+ * 4. Private sale consideration
+ * @param orderParameters The parameters for the order to validate.
+ * @param primaryFeeRecipient The primary fee recipient. Set to null address for no primary fee.
+ * @param primaryFeeBips The primary fee in BIPs.
+ * @param checkCreatorFee Should check for creator fee. If true, creator fee must be present as
+ * according to creator fee engine. If false, must not have creator fee.
+ * @return errorsAndWarnings The errors and warnings.
+ */
+ function validateStrictLogic(
+ OrderParameters memory orderParameters,
+ address primaryFeeRecipient,
+ uint256 primaryFeeBips,
+ bool checkCreatorFee
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Check that order matches the required format (listing or offer)
+ {
+ bool canCheckFee = true;
+ // Single offer item and at least one consideration
+ if (
+ orderParameters.offer.length != 1 ||
+ orderParameters.consideration.length == 0
+ ) {
+ // Not listing or offer, can't check fees
+ canCheckFee = false;
+ } else if (
+ // Can't have both items be fungible
+ isPaymentToken(orderParameters.offer[0].itemType) &&
+ isPaymentToken(orderParameters.consideration[0].itemType)
+ ) {
+ // Not listing or offer, can't check fees
+ canCheckFee = false;
+ } else if (
+ // Can't have both items be non-fungible
+ !isPaymentToken(orderParameters.offer[0].itemType) &&
+ !isPaymentToken(orderParameters.consideration[0].itemType)
+ ) {
+ // Not listing or offer, can't check fees
+ canCheckFee = false;
+ }
+ if (!canCheckFee) {
+ // Does not match required format
+ errorsAndWarnings.addError(
+ GenericIssue.InvalidOrderFormat.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+ }
+
+ // Validate secondary consideration items (fees)
+ (
+ uint256 tertiaryConsiderationIndex,
+ ErrorsAndWarnings memory errorsAndWarningsLocal
+ ) = _validateSecondaryConsiderationItems(
+ orderParameters,
+ ConsiderationItemConfiguration({
+ primaryFeeRecipient: primaryFeeRecipient,
+ primaryFeeBips: primaryFeeBips,
+ checkCreatorFee: checkCreatorFee
+ })
+ );
+
+ errorsAndWarnings.concat(errorsAndWarningsLocal);
+
+ // Validate tertiary consideration items if not 0 (0 indicates error).
+ // Only if no prior errors
+ if (tertiaryConsiderationIndex != 0) {
+ errorsAndWarnings.concat(
+ _validateTertiaryConsiderationItems(
+ orderParameters,
+ tertiaryConsiderationIndex
+ )
+ );
+ }
+ }
+
+ /**
+ * @notice Validate all consideration items for an order
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItems(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // You must have a consideration item
+ if (orderParameters.consideration.length == 0) {
+ errorsAndWarnings.addWarning(
+ ConsiderationIssue.ZeroItems.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+
+ // Declare a boolean to check if offerer is receiving at least
+ // one consideration item
+ bool offererReceivingAtLeastOneItem = false;
+
+ // Iterate over each consideration item
+ for (uint256 i = 0; i < orderParameters.consideration.length; i++) {
+ // Validate consideration item
+ errorsAndWarnings.concat(
+ validateConsiderationItem(orderParameters, i, seaportAddress)
+ );
+
+ ConsiderationItem memory considerationItem1 = orderParameters
+ .consideration[i];
+
+ // Check if the offerer is the recipient
+ if (!offererReceivingAtLeastOneItem) {
+ if (considerationItem1.recipient == orderParameters.offerer) {
+ offererReceivingAtLeastOneItem = true;
+ }
+ }
+
+ // Check for duplicate consideration items
+ for (
+ uint256 j = i + 1;
+ j < orderParameters.consideration.length;
+ j++
+ ) {
+ // Iterate over each remaining consideration item
+ // (previous items already check with this item)
+ ConsiderationItem memory considerationItem2 = orderParameters
+ .consideration[j];
+
+ // Check if itemType, token, id, and recipient are the same
+ if (
+ considerationItem2.itemType ==
+ considerationItem1.itemType &&
+ considerationItem2.token == considerationItem1.token &&
+ considerationItem2.identifierOrCriteria ==
+ considerationItem1.identifierOrCriteria &&
+ considerationItem2.recipient == considerationItem1.recipient
+ ) {
+ errorsAndWarnings.addWarning(
+ // Duplicate consideration item, warning
+ ConsiderationIssue.DuplicateItem.parseInt()
+ );
+ }
+ }
+ }
+
+ if (!offererReceivingAtLeastOneItem) {
+ // Offerer is not receiving at least one consideration item
+ errorsAndWarnings.addWarning(
+ ConsiderationIssue.OffererNotReceivingAtLeastOneItem.parseInt()
+ );
+ }
+ }
+
+ /**
+ * @notice Validate a consideration item
+ * @param orderParameters The parameters for the order to validate
+ * @param considerationItemIndex The index of the consideration item to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItem(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ // Validate the consideration item at considerationItemIndex
+ errorsAndWarnings.concat(
+ validateConsiderationItemParameters(
+ orderParameters,
+ considerationItemIndex,
+ seaportAddress
+ )
+ );
+ }
+
+ /**
+ * @notice Validates the parameters of a consideration item including contract validation
+ * @param orderParameters The parameters for the order to validate
+ * @param considerationItemIndex The index of the consideration item to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItemParameters(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex,
+ address seaportAddress
+ ) public view returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ ConsiderationItem memory considerationItem = orderParameters
+ .consideration[considerationItemIndex];
+
+ // Check if startAmount and endAmount are zero
+ if (
+ considerationItem.startAmount == 0 &&
+ considerationItem.endAmount == 0
+ ) {
+ errorsAndWarnings.addError(
+ ConsiderationIssue.AmountZero.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+
+ // Check if the recipient is the null address
+ if (considerationItem.recipient == address(0)) {
+ errorsAndWarnings.addError(
+ ConsiderationIssue.NullRecipient.parseInt()
+ );
+ }
+
+ if (
+ considerationItem.startAmount != considerationItem.endAmount &&
+ orderParameters.endTime > orderParameters.startTime
+ ) {
+ // Check that amount velocity is not too high.
+ // Assign larger and smaller amount values
+ (uint256 maxAmount, uint256 minAmount) = considerationItem
+ .startAmount > considerationItem.endAmount
+ ? (considerationItem.startAmount, considerationItem.endAmount)
+ : (considerationItem.endAmount, considerationItem.startAmount);
+
+ uint256 amountDelta = maxAmount - minAmount;
+ // delta of time that order exists for
+ uint256 timeDelta = orderParameters.endTime -
+ orderParameters.startTime;
+
+ // Velocity scaled by 1e10 for precision
+ uint256 velocity = (amountDelta * 1e10) / timeDelta;
+ // gives velocity percentage in hundredth of a basis points per second in terms of larger value
+ uint256 velocityPercentage = velocity / (maxAmount * 1e4);
+
+ // 278 * 60 * 30 ~= 500,000
+ if (velocityPercentage > 278) {
+ // Over 50% change per 30 min
+ errorsAndWarnings.addError(
+ ConsiderationIssue.AmountVelocityHigh.parseInt()
+ );
+ }
+ // 28 * 60 * 30 ~= 50,000
+ else if (velocityPercentage > 28) {
+ // Over 5% change per 30 min
+ errorsAndWarnings.addWarning(
+ ConsiderationIssue.AmountVelocityHigh.parseInt()
+ );
+ }
+
+ // Check for large amount steps
+ if (minAmount <= 1e15) {
+ errorsAndWarnings.addWarning(
+ ConsiderationIssue.AmountStepLarge.parseInt()
+ );
+ }
+ }
+
+ if (considerationItem.itemType == ItemType.ERC721) {
+ // ERC721 type requires amounts to be 1
+ if (
+ considerationItem.startAmount != 1 ||
+ considerationItem.endAmount != 1
+ ) {
+ errorsAndWarnings.addError(ERC721Issue.AmountNotOne.parseInt());
+ }
+
+ // Check EIP165 interface
+ if (!checkInterface(considerationItem.token, ERC721_INTERFACE_ID)) {
+ errorsAndWarnings.addError(ERC721Issue.InvalidToken.parseInt());
+ return errorsAndWarnings;
+ }
+
+ // Check that token exists
+ if (
+ !considerationItem.token.safeStaticCallUint256(
+ abi.encodeWithSelector(
+ ERC721Interface.ownerOf.selector,
+ considerationItem.identifierOrCriteria
+ ),
+ 1
+ )
+ ) {
+ // Token does not exist
+ errorsAndWarnings.addError(
+ ERC721Issue.IdentifierDNE.parseInt()
+ );
+ }
+ } else if (
+ considerationItem.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ // Check EIP165 interface
+ if (!checkInterface(considerationItem.token, ERC721_INTERFACE_ID)) {
+ // Does not implement required interface
+ errorsAndWarnings.addError(ERC721Issue.InvalidToken.parseInt());
+ }
+ } else if (
+ considerationItem.itemType == ItemType.ERC1155 ||
+ considerationItem.itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ // Check EIP165 interface
+ if (
+ !checkInterface(considerationItem.token, ERC1155_INTERFACE_ID)
+ ) {
+ // Does not implement required interface
+ errorsAndWarnings.addError(
+ ERC1155Issue.InvalidToken.parseInt()
+ );
+ }
+ } else if (considerationItem.itemType == ItemType.ERC20) {
+ // ERC20 must have `identifierOrCriteria` be zero
+ if (considerationItem.identifierOrCriteria != 0) {
+ errorsAndWarnings.addError(
+ ERC20Issue.IdentifierNonZero.parseInt()
+ );
+ }
+
+ // Check that it is an ERC20 token. ERC20 will return a uint256
+ if (
+ !considerationItem.token.safeStaticCallUint256(
+ abi.encodeWithSelector(
+ ERC20Interface.allowance.selector,
+ seaportAddress,
+ seaportAddress
+ ),
+ 0
+ )
+ ) {
+ // Not an ERC20 token
+ errorsAndWarnings.addError(ERC20Issue.InvalidToken.parseInt());
+ }
+ } else {
+ // Must be native
+ // NATIVE must have `token` be zero address
+ if (considerationItem.token != address(0)) {
+ errorsAndWarnings.addError(NativeIssue.TokenAddress.parseInt());
+ }
+ // NATIVE must have `identifierOrCriteria` be zero
+ if (considerationItem.identifierOrCriteria != 0) {
+ errorsAndWarnings.addError(
+ NativeIssue.IdentifierNonZero.parseInt()
+ );
+ }
+ }
+ }
+
+ function _validateSecondaryConsiderationItems(
+ OrderParameters memory orderParameters,
+ ConsiderationItemConfiguration memory config
+ )
+ internal
+ view
+ returns (
+ uint256 /* tertiaryConsiderationIndex */,
+ ErrorsAndWarnings memory /* errorsAndWarnings */
+ )
+ {
+ ErrorsAndWarnings memory errorsAndWarnings = ErrorsAndWarnings(
+ new uint16[](0),
+ new uint16[](0)
+ );
+
+ // Consideration item to hold expected creator fee info
+ ConsiderationItem memory creatorFeeConsideration;
+
+ bool primaryFeePresent;
+
+ {
+ // non-fungible item address
+ address itemAddress;
+ // non-fungible item identifier
+ uint256 itemIdentifier;
+ // fungible item start amount
+ uint256 transactionAmountStart;
+ // fungible item end amount
+ uint256 transactionAmountEnd;
+
+ if (isPaymentToken(orderParameters.offer[0].itemType)) {
+ // Offer is an offer. Offer item is fungible and used for fees
+ creatorFeeConsideration.itemType = orderParameters
+ .offer[0]
+ .itemType;
+ creatorFeeConsideration.token = orderParameters.offer[0].token;
+ transactionAmountStart = orderParameters.offer[0].startAmount;
+ transactionAmountEnd = orderParameters.offer[0].endAmount;
+
+ // Set non-fungible information for calculating creator fee
+ itemAddress = orderParameters.consideration[0].token;
+ itemIdentifier = orderParameters
+ .consideration[0]
+ .identifierOrCriteria;
+ } else {
+ // Offer is an offer. Consideration item is fungible and used for fees
+ creatorFeeConsideration.itemType = orderParameters
+ .consideration[0]
+ .itemType;
+ creatorFeeConsideration.token = orderParameters
+ .consideration[0]
+ .token;
+ transactionAmountStart = orderParameters
+ .consideration[0]
+ .startAmount;
+ transactionAmountEnd = orderParameters
+ .consideration[0]
+ .endAmount;
+
+ // Set non-fungible information for calculating creator fees
+ itemAddress = orderParameters.offer[0].token;
+ itemIdentifier = orderParameters.offer[0].identifierOrCriteria;
+ }
+
+ // Store flag if primary fee is present
+ primaryFeePresent = false;
+ {
+ // Calculate primary fee start and end amounts
+ uint256 primaryFeeStartAmount = (transactionAmountStart *
+ config.primaryFeeBips) / 10000;
+ uint256 primaryFeeEndAmount = (transactionAmountEnd *
+ config.primaryFeeBips) / 10000;
+
+ // Check if primary fee check is desired. Skip if calculated amount is zero.
+ if (
+ config.primaryFeeRecipient != address(0) &&
+ (primaryFeeStartAmount > 0 || primaryFeeEndAmount > 0)
+ ) {
+ // Ensure primary fee is present
+ if (orderParameters.consideration.length < 2) {
+ errorsAndWarnings.addError(
+ PrimaryFeeIssue.Missing.parseInt()
+ );
+ return (0, errorsAndWarnings);
+ }
+ primaryFeePresent = true;
+
+ ConsiderationItem memory primaryFeeItem = orderParameters
+ .consideration[1];
+
+ // Check item type
+ if (
+ primaryFeeItem.itemType !=
+ creatorFeeConsideration.itemType
+ ) {
+ errorsAndWarnings.addError(
+ PrimaryFeeIssue.ItemType.parseInt()
+ );
+ return (0, errorsAndWarnings);
+ }
+ // Check token
+ if (primaryFeeItem.token != creatorFeeConsideration.token) {
+ errorsAndWarnings.addError(
+ PrimaryFeeIssue.Token.parseInt()
+ );
+ }
+ // Check start amount
+ if (primaryFeeItem.startAmount < primaryFeeStartAmount) {
+ errorsAndWarnings.addError(
+ PrimaryFeeIssue.StartAmount.parseInt()
+ );
+ }
+ // Check end amount
+ if (primaryFeeItem.endAmount < primaryFeeEndAmount) {
+ errorsAndWarnings.addError(
+ PrimaryFeeIssue.EndAmount.parseInt()
+ );
+ }
+ // Check recipient
+ if (
+ primaryFeeItem.recipient != config.primaryFeeRecipient
+ ) {
+ errorsAndWarnings.addError(
+ PrimaryFeeIssue.Recipient.parseInt()
+ );
+ }
+ }
+ }
+
+ // Check creator fee
+ (
+ creatorFeeConsideration.recipient,
+ creatorFeeConsideration.startAmount,
+ creatorFeeConsideration.endAmount
+ ) = getCreatorFeeInfo(
+ itemAddress,
+ itemIdentifier,
+ transactionAmountStart,
+ transactionAmountEnd
+ );
+ }
+
+ // Flag indicating if creator fee is present in considerations
+ bool creatorFeePresent = false;
+
+ // Determine if should check for creator fee
+ if (
+ creatorFeeConsideration.recipient != address(0) &&
+ config.checkCreatorFee &&
+ (creatorFeeConsideration.startAmount > 0 ||
+ creatorFeeConsideration.endAmount > 0)
+ ) {
+ // Calculate index of creator fee consideration item
+ uint16 creatorFeeConsiderationIndex = primaryFeePresent ? 2 : 1; // 2 if primary fee, ow 1
+
+ // Check that creator fee consideration item exists
+ if (
+ orderParameters.consideration.length - 1 <
+ creatorFeeConsiderationIndex
+ ) {
+ errorsAndWarnings.addError(CreatorFeeIssue.Missing.parseInt());
+ return (0, errorsAndWarnings);
+ }
+
+ ConsiderationItem memory creatorFeeItem = orderParameters
+ .consideration[creatorFeeConsiderationIndex];
+
+ creatorFeePresent = true;
+
+ // Check type
+ if (creatorFeeItem.itemType != creatorFeeConsideration.itemType) {
+ errorsAndWarnings.addError(CreatorFeeIssue.ItemType.parseInt());
+ return (0, errorsAndWarnings);
+ }
+ // Check token
+ if (creatorFeeItem.token != creatorFeeConsideration.token) {
+ errorsAndWarnings.addError(CreatorFeeIssue.Token.parseInt());
+ }
+ // Check start amount
+ if (
+ creatorFeeItem.startAmount < creatorFeeConsideration.startAmount
+ ) {
+ errorsAndWarnings.addError(
+ CreatorFeeIssue.StartAmount.parseInt()
+ );
+ }
+ // Check end amount
+ if (creatorFeeItem.endAmount < creatorFeeConsideration.endAmount) {
+ errorsAndWarnings.addError(
+ CreatorFeeIssue.EndAmount.parseInt()
+ );
+ }
+ // Check recipient
+ if (creatorFeeItem.recipient != creatorFeeConsideration.recipient) {
+ errorsAndWarnings.addError(
+ CreatorFeeIssue.Recipient.parseInt()
+ );
+ }
+ }
+
+ // Calculate index of first tertiary consideration item
+ uint256 tertiaryConsiderationIndex = 1 +
+ (primaryFeePresent ? 1 : 0) +
+ (creatorFeePresent ? 1 : 0);
+
+ return (tertiaryConsiderationIndex, errorsAndWarnings);
+ }
+
+ /**
+ * @notice Internal function for validating all consideration items after the fee items.
+ * Only additional acceptable consideration is private sale.
+ */
+ function _validateTertiaryConsiderationItems(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex
+ ) internal pure returns (ErrorsAndWarnings memory errorsAndWarnings) {
+ errorsAndWarnings = ErrorsAndWarnings(new uint16[](0), new uint16[](0));
+
+ if (orderParameters.consideration.length <= considerationItemIndex) {
+ // No more consideration items
+ return errorsAndWarnings;
+ }
+
+ ConsiderationItem memory privateSaleConsideration = orderParameters
+ .consideration[considerationItemIndex];
+
+ // Check if offer is payment token. Private sale not possible if so.
+ if (isPaymentToken(orderParameters.offer[0].itemType)) {
+ errorsAndWarnings.addError(
+ ConsiderationIssue.ExtraItems.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+
+ // Check if private sale to self
+ if (privateSaleConsideration.recipient == orderParameters.offerer) {
+ errorsAndWarnings.addError(
+ ConsiderationIssue.PrivateSaleToSelf.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+
+ // Ensure that private sale parameters match offer item.
+ if (
+ privateSaleConsideration.itemType !=
+ orderParameters.offer[0].itemType ||
+ privateSaleConsideration.token != orderParameters.offer[0].token ||
+ orderParameters.offer[0].startAmount !=
+ privateSaleConsideration.startAmount ||
+ orderParameters.offer[0].endAmount !=
+ privateSaleConsideration.endAmount ||
+ orderParameters.offer[0].identifierOrCriteria !=
+ privateSaleConsideration.identifierOrCriteria
+ ) {
+ // Invalid private sale, say extra consideration item
+ errorsAndWarnings.addError(
+ ConsiderationIssue.ExtraItems.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+
+ errorsAndWarnings.addWarning(ConsiderationIssue.PrivateSale.parseInt());
+
+ // Should not be any additional consideration items
+ if (orderParameters.consideration.length - 1 > considerationItemIndex) {
+ // Extra consideration items
+ errorsAndWarnings.addError(
+ ConsiderationIssue.ExtraItems.parseInt()
+ );
+ return errorsAndWarnings;
+ }
+ }
+
+ /**
+ * @notice Fetches the on chain creator fees.
+ * @dev Uses the creatorFeeEngine when available, otherwise fallback to `IERC2981`.
+ * @param token The token address
+ * @param tokenId The token identifier
+ * @param transactionAmountStart The transaction start amount
+ * @param transactionAmountEnd The transaction end amount
+ * @return recipient creator fee recipient
+ * @return creatorFeeAmountStart creator fee start amount
+ * @return creatorFeeAmountEnd creator fee end amount
+ */
+ function getCreatorFeeInfo(
+ address token,
+ uint256 tokenId,
+ uint256 transactionAmountStart,
+ uint256 transactionAmountEnd
+ )
+ public
+ view
+ returns (
+ address payable recipient,
+ uint256 creatorFeeAmountStart,
+ uint256 creatorFeeAmountEnd
+ )
+ {
+ // Check if creator fee engine is on this chain
+ if (address(creatorFeeEngine) != address(0)) {
+ // Creator fee engine may revert if no creator fees are present.
+ try
+ creatorFeeEngine.getRoyaltyView(
+ token,
+ tokenId,
+ transactionAmountStart
+ )
+ returns (
+ address payable[] memory creatorFeeRecipients,
+ uint256[] memory creatorFeeAmountsStart
+ ) {
+ if (creatorFeeRecipients.length != 0) {
+ // Use first recipient and amount
+ recipient = creatorFeeRecipients[0];
+ creatorFeeAmountStart = creatorFeeAmountsStart[0];
+ }
+ } catch {
+ // Creator fee not found
+ }
+
+ // If fees found for start amount, check end amount
+ if (recipient != address(0)) {
+ // Creator fee engine may revert if no creator fees are present.
+ try
+ creatorFeeEngine.getRoyaltyView(
+ token,
+ tokenId,
+ transactionAmountEnd
+ )
+ returns (
+ address payable[] memory,
+ uint256[] memory creatorFeeAmountsEnd
+ ) {
+ creatorFeeAmountEnd = creatorFeeAmountsEnd[0];
+ } catch {}
+ }
+ } else {
+ // Fallback to ERC2981
+ {
+ // Static call to token using ERC2981
+ (bool success, bytes memory res) = token.staticcall(
+ abi.encodeWithSelector(
+ IERC2981.royaltyInfo.selector,
+ tokenId,
+ transactionAmountStart
+ )
+ );
+ // Check if call succeeded
+ if (success) {
+ // Ensure 64 bytes returned
+ if (res.length == 64) {
+ // Decode result and assign recipient and start amount
+ (recipient, creatorFeeAmountStart) = abi.decode(
+ res,
+ (address, uint256)
+ );
+ }
+ }
+ }
+
+ // Only check end amount if start amount found
+ if (recipient != address(0)) {
+ // Static call to token using ERC2981
+ (bool success, bytes memory res) = token.staticcall(
+ abi.encodeWithSelector(
+ IERC2981.royaltyInfo.selector,
+ tokenId,
+ transactionAmountEnd
+ )
+ );
+ // Check if call succeeded
+ if (success) {
+ // Ensure 64 bytes returned
+ if (res.length == 64) {
+ // Decode result and assign end amount
+ (, creatorFeeAmountEnd) = abi.decode(
+ res,
+ (address, uint256)
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @notice Safely check that a contract implements an interface
+ * @param token The token address to check
+ * @param interfaceHash The interface hash to check
+ */
+ function checkInterface(
+ address token,
+ bytes4 interfaceHash
+ ) public view returns (bool) {
+ return
+ token.safeStaticCallBool(
+ abi.encodeWithSelector(
+ IERC165.supportsInterface.selector,
+ interfaceHash
+ ),
+ true
+ );
+ }
+
+ /*//////////////////////////////////////////////////////////////
+ Merkle Helpers
+ //////////////////////////////////////////////////////////////*/
+
+ /**
+ * @notice Sorts an array of token ids by the keccak256 hash of the id. Required ordering of ids
+ * for other merkle operations.
+ * @param includedTokens An array of included token ids.
+ * @return sortedTokens The sorted `includedTokens` array.
+ */
+ function sortMerkleTokens(
+ uint256[] memory includedTokens
+ ) public pure returns (uint256[] memory sortedTokens) {
+ // Sort token ids by the keccak256 hash of the id
+ return _sortUint256ByHash(includedTokens);
+ }
+
+ /**
+ * @notice Creates a merkle root for includedTokens.
+ * @dev `includedTokens` must be sorting in strictly ascending order according to the keccak256 hash of the value.
+ * @return merkleRoot The merkle root
+ * @return errorsAndWarnings Errors and warnings from the operation
+ */
+ function getMerkleRoot(
+ uint256[] memory includedTokens
+ )
+ public
+ pure
+ returns (bytes32 merkleRoot, ErrorsAndWarnings memory errorsAndWarnings)
+ {
+ (merkleRoot, errorsAndWarnings) = _getRoot(includedTokens);
+ }
+
+ /**
+ * @notice Creates a merkle proof for the the targetIndex contained in includedTokens.
+ * @dev `targetIndex` is referring to the index of an element in `includedTokens`.
+ * `includedTokens` must be sorting in ascending order according to the keccak256 hash of the value.
+ * @return merkleProof The merkle proof
+ * @return errorsAndWarnings Errors and warnings from the operation
+ */
+ function getMerkleProof(
+ uint256[] memory includedTokens,
+ uint256 targetIndex
+ )
+ public
+ pure
+ returns (
+ bytes32[] memory merkleProof,
+ ErrorsAndWarnings memory errorsAndWarnings
+ )
+ {
+ (merkleProof, errorsAndWarnings) = _getProof(
+ includedTokens,
+ targetIndex
+ );
+ }
+
+ /**
+ * @notice Verifies a merkle proof for the value to prove and given root and proof.
+ * @dev The `valueToProve` is hashed prior to executing the proof verification.
+ * @param merkleRoot The root of the merkle tree
+ * @param merkleProof The merkle proof
+ * @param valueToProve The value to prove
+ * @return whether proof is valid
+ */
+ function verifyMerkleProof(
+ bytes32 merkleRoot,
+ bytes32[] memory merkleProof,
+ uint256 valueToProve
+ ) public pure returns (bool) {
+ bytes32 hashedValue = keccak256(abi.encode(valueToProve));
+
+ return _verifyProof(merkleRoot, merkleProof, hashedValue);
+ }
+
+ function isPaymentToken(ItemType itemType) public pure returns (bool) {
+ return itemType == ItemType.NATIVE || itemType == ItemType.ERC20;
+ }
+}
+
+interface CreatorFeeEngineInterface {
+ function getRoyaltyView(
+ address tokenAddress,
+ uint256 tokenId,
+ uint256 value
+ )
+ external
+ view
+ returns (address payable[] memory recipients, uint256[] memory amounts);
+}
diff --git a/contracts/helpers/order-validator/lib/SeaportValidatorInterface.sol b/contracts/helpers/order-validator/lib/SeaportValidatorInterface.sol
new file mode 100644
index 000000000..5dd5925a6
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/SeaportValidatorInterface.sol
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import { ItemType } from "../../../lib/ConsiderationEnums.sol";
+import {
+ Order,
+ OrderParameters,
+ ZoneParameters
+} from "../../../lib/ConsiderationStructs.sol";
+import { ErrorsAndWarnings } from "./ErrorsAndWarnings.sol";
+import { ValidationConfiguration } from "./SeaportValidatorTypes.sol";
+
+/**
+ * @title SeaportValidator
+ * @notice SeaportValidator validates simple orders that adhere to a set of rules defined below:
+ * - The order is either a listing or an offer order (one NFT to buy or one NFT to sell).
+ * - The first consideration is the primary consideration.
+ * - The order pays up to two fees in the fungible token currency. First fee is primary fee, second is creator fee.
+ * - In private orders, the last consideration specifies a recipient for the offer item.
+ * - Offer items must be owned and properly approved by the offerer.
+ * - Consideration items must exist.
+ */
+interface SeaportValidatorInterface {
+ /**
+ * @notice Conduct a comprehensive validation of the given order.
+ * @param order The order to validate.
+ * @return errorsAndWarnings The errors and warnings found in the order.
+ */
+ function isValidOrder(
+ Order calldata order,
+ address seaportAddress
+ ) external returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Same as `isValidOrder` but allows for more configuration related to fee validation.
+ */
+ function isValidOrderWithConfiguration(
+ ValidationConfiguration memory validationConfiguration,
+ Order memory order
+ ) external returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Same as `isValidOrderWithConfiguration` but doesn't call `validate` on Seaport.
+ */
+ function isValidOrderWithConfigurationReadOnly(
+ ValidationConfiguration memory validationConfiguration,
+ Order memory order
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Checks if a conduit key is valid.
+ * @param conduitKey The conduit key to check.
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function isValidConduit(
+ bytes32 conduitKey,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ // TODO: Need to add support for order with extra data
+ /**
+ * @notice Checks that the zone of an order implements the required interface
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function isValidZone(
+ OrderParameters memory orderParameters
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ function validateSignature(
+ Order memory order,
+ address seaportAddress
+ ) external returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ function validateSignatureWithCounter(
+ Order memory order,
+ uint256 counter,
+ address seaportAddress
+ ) external returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Check the time validity of an order
+ * @param orderParameters The parameters for the order to validate
+ * @param shortOrderDuration The duration of which an order is considered short
+ * @param distantOrderExpiration Distant order expiration delta in seconds.
+ * @return errorsAndWarnings The Issues and warnings
+ */
+ function validateTime(
+ OrderParameters memory orderParameters,
+ uint256 shortOrderDuration,
+ uint256 distantOrderExpiration
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validate the status of an order
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateOrderStatus(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validate all offer items for an order
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateOfferItems(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validate all consideration items for an order
+ * @param orderParameters The parameters for the order to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItems(
+ OrderParameters memory orderParameters,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Strict validation operates under tight assumptions. It validates primary
+ * fee, creator fee, private sale consideration, and overall order format.
+ * @dev Only checks first fee recipient provided by CreatorFeeRegistry.
+ * Order of consideration items must be as follows:
+ * 1. Primary consideration
+ * 2. Primary fee
+ * 3. Creator Fee
+ * 4. Private sale consideration
+ * @param orderParameters The parameters for the order to validate.
+ * @param primaryFeeRecipient The primary fee recipient. Set to null address for no primary fee.
+ * @param primaryFeeBips The primary fee in BIPs.
+ * @param checkCreatorFee Should check for creator fee. If true, creator fee must be present as
+ * according to creator fee engine. If false, must not have creator fee.
+ * @return errorsAndWarnings The errors and warnings.
+ */
+ function validateStrictLogic(
+ OrderParameters memory orderParameters,
+ address primaryFeeRecipient,
+ uint256 primaryFeeBips,
+ bool checkCreatorFee
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validate a consideration item
+ * @param orderParameters The parameters for the order to validate
+ * @param considerationItemIndex The index of the consideration item to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItem(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validates the parameters of a consideration item including contract validation
+ * @param orderParameters The parameters for the order to validate
+ * @param considerationItemIndex The index of the consideration item to validate
+ * @return errorsAndWarnings The errors and warnings
+ */
+ function validateConsiderationItemParameters(
+ OrderParameters memory orderParameters,
+ uint256 considerationItemIndex,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validates an offer item
+ * @param orderParameters The parameters for the order to validate
+ * @param offerItemIndex The index of the offerItem in offer array to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOfferItem(
+ OrderParameters memory orderParameters,
+ uint256 offerItemIndex,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validates the OfferItem parameters. This includes token contract validation
+ * @dev OfferItems with criteria are currently not allowed
+ * @param orderParameters The parameters for the order to validate
+ * @param offerItemIndex The index of the offerItem in offer array to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOfferItemParameters(
+ OrderParameters memory orderParameters,
+ uint256 offerItemIndex,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Validates the OfferItem approvals and balances
+ * @param orderParameters The parameters for the order to validate
+ * @param offerItemIndex The index of the offerItem in offer array to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOfferItemApprovalAndBalance(
+ OrderParameters memory orderParameters,
+ uint256 offerItemIndex,
+ address seaportAddress
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Calls validateOrder on the order's zone with the given zoneParameters
+ * @param orderParameters The parameters for the order to validate
+ * @param zoneParameters The parameters for the zone to validate
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function validateOrderWithZone(
+ OrderParameters memory orderParameters,
+ ZoneParameters memory zoneParameters
+ ) external view returns (ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Gets the approval address for the given conduit key
+ * @param conduitKey Conduit key to get approval address for
+ * @return errorsAndWarnings An ErrorsAndWarnings structs with results
+ */
+ function getApprovalAddress(
+ bytes32 conduitKey,
+ address seaportAddress
+ )
+ external
+ view
+ returns (address, ErrorsAndWarnings memory errorsAndWarnings);
+
+ /**
+ * @notice Safely check that a contract implements an interface
+ * @param token The token address to check
+ * @param interfaceHash The interface hash to check
+ */
+ function checkInterface(
+ address token,
+ bytes4 interfaceHash
+ ) external view returns (bool);
+
+ function isPaymentToken(ItemType itemType) external pure returns (bool);
+
+ /*//////////////////////////////////////////////////////////////
+ Merkle Helpers
+ //////////////////////////////////////////////////////////////*/
+
+ /**
+ * @notice Sorts an array of token ids by the keccak256 hash of the id. Required ordering of ids
+ * for other merkle operations.
+ * @param includedTokens An array of included token ids.
+ * @return sortedTokens The sorted `includedTokens` array.
+ */
+ function sortMerkleTokens(
+ uint256[] memory includedTokens
+ ) external view returns (uint256[] memory sortedTokens);
+
+ /**
+ * @notice Creates a merkle root for includedTokens.
+ * @dev `includedTokens` must be sorting in strictly ascending order according to the keccak256 hash of the value.
+ * @return merkleRoot The merkle root
+ * @return errorsAndWarnings Errors and warnings from the operation
+ */
+ function getMerkleRoot(
+ uint256[] memory includedTokens
+ )
+ external
+ view
+ returns (
+ bytes32 merkleRoot,
+ ErrorsAndWarnings memory errorsAndWarnings
+ );
+
+ /**
+ * @notice Creates a merkle proof for the the targetIndex contained in includedTokens.
+ * @dev `targetIndex` is referring to the index of an element in `includedTokens`.
+ * `includedTokens` must be sorting in ascending order according to the keccak256 hash of the value.
+ * @return merkleProof The merkle proof
+ * @return errorsAndWarnings Errors and warnings from the operation
+ */
+ function getMerkleProof(
+ uint256[] memory includedTokens,
+ uint256 targetIndex
+ )
+ external
+ view
+ returns (
+ bytes32[] memory merkleProof,
+ ErrorsAndWarnings memory errorsAndWarnings
+ );
+
+ function verifyMerkleProof(
+ bytes32 merkleRoot,
+ bytes32[] memory merkleProof,
+ uint256 valueToProve
+ ) external view returns (bool);
+}
diff --git a/contracts/helpers/order-validator/lib/SeaportValidatorTypes.sol b/contracts/helpers/order-validator/lib/SeaportValidatorTypes.sol
new file mode 100644
index 000000000..065e8933a
--- /dev/null
+++ b/contracts/helpers/order-validator/lib/SeaportValidatorTypes.sol
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+struct ValidationConfiguration {
+ /// @notice The seaport address.
+ address seaport;
+ /// @notice Recipient for primary fee payments.
+ address primaryFeeRecipient;
+ /// @notice Bips for primary fee payments.
+ uint256 primaryFeeBips;
+ /// @notice Should creator fees be checked?
+ bool checkCreatorFee;
+ /// @notice Should strict validation be skipped?
+ bool skipStrictValidation;
+ /// @notice Short order duration in seconds
+ uint256 shortOrderDuration;
+ /// @notice Distant order expiration delta in seconds. Warning if order expires in longer than this.
+ uint256 distantOrderExpiration;
+}
+
+struct ConsiderationItemConfiguration {
+ address primaryFeeRecipient;
+ uint256 primaryFeeBips;
+ bool checkCreatorFee;
+}
+
+enum GenericIssue {
+ InvalidOrderFormat // 100
+}
+
+enum ERC20Issue {
+ IdentifierNonZero, // 200
+ InvalidToken, // 201
+ InsufficientAllowance, // 202
+ InsufficientBalance // 203
+}
+
+enum ERC721Issue {
+ AmountNotOne, // 300
+ InvalidToken, // 301
+ IdentifierDNE, // 302
+ NotOwner, // 303
+ NotApproved, // 304
+ CriteriaNotPartialFill // 305
+}
+
+enum ERC1155Issue {
+ InvalidToken, // 400
+ NotApproved, // 401
+ InsufficientBalance // 402
+}
+
+enum ConsiderationIssue {
+ AmountZero, // 500
+ NullRecipient, // 501
+ ExtraItems, // 502
+ PrivateSaleToSelf, // 503
+ ZeroItems, // 504
+ DuplicateItem, // 505
+ OffererNotReceivingAtLeastOneItem, // 506
+ PrivateSale, // 507
+ AmountVelocityHigh, // 508
+ AmountStepLarge // 509
+}
+
+enum OfferIssue {
+ ZeroItems, // 600
+ AmountZero, // 601
+ MoreThanOneItem, // 602
+ NativeItem, // 603
+ DuplicateItem, // 604
+ AmountVelocityHigh, // 605
+ AmountStepLarge // 606
+}
+
+enum PrimaryFeeIssue {
+ Missing, // 700
+ ItemType, // 701
+ Token, // 702
+ StartAmount, // 703
+ EndAmount, // 704
+ Recipient // 705
+}
+
+enum StatusIssue {
+ Cancelled, // 800
+ FullyFilled, // 801
+ ContractOrder // 802
+}
+
+enum TimeIssue {
+ EndTimeBeforeStartTime, // 900
+ Expired, // 901
+ DistantExpiration, // 902
+ NotActive, // 903
+ ShortOrder // 904
+}
+
+enum ConduitIssue {
+ KeyInvalid, // 1000
+ MissingSeaportChannel // 1001
+}
+
+enum SignatureIssue {
+ Invalid, // 1100
+ ContractOrder, // 1101
+ LowCounter, // 1102
+ HighCounter, // 1103
+ OriginalConsiderationItems // 1104
+}
+
+enum CreatorFeeIssue {
+ Missing, // 1200
+ ItemType, // 1201
+ Token, // 1202
+ StartAmount, // 1203
+ EndAmount, // 1204
+ Recipient // 1205
+}
+
+enum NativeIssue {
+ TokenAddress, // 1300
+ IdentifierNonZero, // 1301
+ InsufficientBalance // 1302
+}
+
+enum ZoneIssue {
+ InvalidZone, // 1400
+ RejectedOrder, // 1401
+ NotSet, // 1402
+ EOAZone // 1403
+}
+
+enum MerkleIssue {
+ SingleLeaf, // 1500
+ Unsorted // 1501
+}
+
+enum ContractOffererIssue {
+ InvalidContractOfferer // 1600
+}
+
+/**
+ * @title IssueParser - parse issues into integers
+ * @notice Implements a `parseInt` function for each issue type.
+ * offsets the enum value to place within the issue range.
+ */
+library IssueParser {
+ function parseInt(GenericIssue err) internal pure returns (uint16) {
+ return uint16(err) + 100;
+ }
+
+ function parseInt(ERC20Issue err) internal pure returns (uint16) {
+ return uint16(err) + 200;
+ }
+
+ function parseInt(ERC721Issue err) internal pure returns (uint16) {
+ return uint16(err) + 300;
+ }
+
+ function parseInt(ERC1155Issue err) internal pure returns (uint16) {
+ return uint16(err) + 400;
+ }
+
+ function parseInt(ConsiderationIssue err) internal pure returns (uint16) {
+ return uint16(err) + 500;
+ }
+
+ function parseInt(OfferIssue err) internal pure returns (uint16) {
+ return uint16(err) + 600;
+ }
+
+ function parseInt(PrimaryFeeIssue err) internal pure returns (uint16) {
+ return uint16(err) + 700;
+ }
+
+ function parseInt(StatusIssue err) internal pure returns (uint16) {
+ return uint16(err) + 800;
+ }
+
+ function parseInt(TimeIssue err) internal pure returns (uint16) {
+ return uint16(err) + 900;
+ }
+
+ function parseInt(ConduitIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1000;
+ }
+
+ function parseInt(SignatureIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1100;
+ }
+
+ function parseInt(CreatorFeeIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1200;
+ }
+
+ function parseInt(NativeIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1300;
+ }
+
+ function parseInt(ZoneIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1400;
+ }
+
+ function parseInt(MerkleIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1500;
+ }
+
+ function parseInt(ContractOffererIssue err) internal pure returns (uint16) {
+ return uint16(err) + 1600;
+ }
+}
+
+library IssueStringHelpers {
+ function toString(GenericIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == GenericIssue.InvalidOrderFormat) {
+ code = "InvalidOrderFormat";
+ }
+ return string.concat("GenericIssue: ", code);
+ }
+
+ function toString(ERC20Issue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == ERC20Issue.IdentifierNonZero) {
+ code = "IdentifierNonZero";
+ } else if (id == ERC20Issue.InvalidToken) {
+ code = "InvalidToken";
+ } else if (id == ERC20Issue.InsufficientAllowance) {
+ code = "InsufficientAllowance";
+ } else if (id == ERC20Issue.InsufficientBalance) {
+ code = "InsufficientBalance";
+ }
+ return string.concat("ERC20Issue: ", code);
+ }
+
+ function toString(ERC721Issue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == ERC721Issue.AmountNotOne) {
+ code = "AmountNotOne";
+ } else if (id == ERC721Issue.InvalidToken) {
+ code = "InvalidToken";
+ } else if (id == ERC721Issue.IdentifierDNE) {
+ code = "IdentifierDNE";
+ } else if (id == ERC721Issue.NotOwner) {
+ code = "NotOwner";
+ } else if (id == ERC721Issue.NotApproved) {
+ code = "NotApproved";
+ } else if (id == ERC721Issue.CriteriaNotPartialFill) {
+ code = "CriteriaNotPartialFill";
+ }
+ return string.concat("ERC721Issue: ", code);
+ }
+
+ function toString(ERC1155Issue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == ERC1155Issue.InvalidToken) {
+ code = "InvalidToken";
+ } else if (id == ERC1155Issue.NotApproved) {
+ code = "NotApproved";
+ } else if (id == ERC1155Issue.InsufficientBalance) {
+ code = "InsufficientBalance";
+ }
+ return string.concat("ERC1155Issue: ", code);
+ }
+
+ function toString(
+ ConsiderationIssue id
+ ) internal pure returns (string memory) {
+ string memory code;
+ if (id == ConsiderationIssue.AmountZero) {
+ code = "AmountZero";
+ } else if (id == ConsiderationIssue.NullRecipient) {
+ code = "NullRecipient";
+ } else if (id == ConsiderationIssue.ExtraItems) {
+ code = "ExtraItems";
+ } else if (id == ConsiderationIssue.PrivateSaleToSelf) {
+ code = "PrivateSaleToSelf";
+ } else if (id == ConsiderationIssue.ZeroItems) {
+ code = "ZeroItems";
+ } else if (id == ConsiderationIssue.DuplicateItem) {
+ code = "DuplicateItem";
+ } else if (id == ConsiderationIssue.OffererNotReceivingAtLeastOneItem) {
+ code = "OffererNotReceivingAtLeastOneItem";
+ } else if (id == ConsiderationIssue.PrivateSale) {
+ code = "PrivateSale";
+ } else if (id == ConsiderationIssue.AmountVelocityHigh) {
+ code = "AmountVelocityHigh";
+ } else if (id == ConsiderationIssue.AmountStepLarge) {
+ code = "AmountStepLarge";
+ }
+ return string.concat("ConsiderationIssue: ", code);
+ }
+
+ function toString(OfferIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == OfferIssue.ZeroItems) {
+ code = "ZeroItems";
+ } else if (id == OfferIssue.AmountZero) {
+ code = "AmountZero";
+ } else if (id == OfferIssue.MoreThanOneItem) {
+ code = "MoreThanOneItem";
+ } else if (id == OfferIssue.NativeItem) {
+ code = "NativeItem";
+ } else if (id == OfferIssue.DuplicateItem) {
+ code = "DuplicateItem";
+ } else if (id == OfferIssue.AmountVelocityHigh) {
+ code = "AmountVelocityHigh";
+ } else if (id == OfferIssue.AmountStepLarge) {
+ code = "AmountStepLarge";
+ }
+ return string.concat("OfferIssue: ", code);
+ }
+
+ function toString(
+ PrimaryFeeIssue id
+ ) internal pure returns (string memory) {
+ string memory code;
+ if (id == PrimaryFeeIssue.Missing) {
+ code = "Missing";
+ } else if (id == PrimaryFeeIssue.ItemType) {
+ code = "ItemType";
+ } else if (id == PrimaryFeeIssue.Token) {
+ code = "Token";
+ } else if (id == PrimaryFeeIssue.StartAmount) {
+ code = "StartAmount";
+ } else if (id == PrimaryFeeIssue.EndAmount) {
+ code = "EndAmount";
+ } else if (id == PrimaryFeeIssue.Recipient) {
+ code = "Recipient";
+ }
+ return string.concat("PrimaryFeeIssue: ", code);
+ }
+
+ function toString(StatusIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == StatusIssue.Cancelled) {
+ code = "Cancelled";
+ } else if (id == StatusIssue.FullyFilled) {
+ code = "FullyFilled";
+ } else if (id == StatusIssue.ContractOrder) {
+ code = "ContractOrder";
+ }
+ return string.concat("StatusIssue: ", code);
+ }
+
+ function toString(TimeIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == TimeIssue.EndTimeBeforeStartTime) {
+ code = "EndTimeBeforeStartTime";
+ } else if (id == TimeIssue.Expired) {
+ code = "Expired";
+ } else if (id == TimeIssue.DistantExpiration) {
+ code = "DistantExpiration";
+ } else if (id == TimeIssue.NotActive) {
+ code = "NotActive";
+ } else if (id == TimeIssue.ShortOrder) {
+ code = "ShortOrder";
+ }
+ return string.concat("TimeIssue: ", code);
+ }
+
+ function toString(ConduitIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == ConduitIssue.KeyInvalid) {
+ code = "KeyInvalid";
+ } else if (id == ConduitIssue.MissingSeaportChannel) {
+ code = "MissingSeaportChannel";
+ }
+ return string.concat("ConduitIssue: ", code);
+ }
+
+ function toString(SignatureIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == SignatureIssue.Invalid) {
+ code = "Invalid";
+ } else if (id == SignatureIssue.ContractOrder) {
+ code = "ContractOrder";
+ } else if (id == SignatureIssue.LowCounter) {
+ code = "LowCounter";
+ } else if (id == SignatureIssue.HighCounter) {
+ code = "HighCounter";
+ } else if (id == SignatureIssue.OriginalConsiderationItems) {
+ code = "OriginalConsiderationItems";
+ }
+ return string.concat("SignatureIssue: ", code);
+ }
+
+ function toString(
+ CreatorFeeIssue id
+ ) internal pure returns (string memory) {
+ string memory code;
+ if (id == CreatorFeeIssue.Missing) {
+ code = "Missing";
+ } else if (id == CreatorFeeIssue.ItemType) {
+ code = "ItemType";
+ } else if (id == CreatorFeeIssue.Token) {
+ code = "Token";
+ } else if (id == CreatorFeeIssue.StartAmount) {
+ code = "StartAmount";
+ } else if (id == CreatorFeeIssue.EndAmount) {
+ code = "EndAmount";
+ } else if (id == CreatorFeeIssue.Recipient) {
+ code = "Recipient";
+ }
+ return string.concat("CreatorFeeIssue: ", code);
+ }
+
+ function toString(NativeIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == NativeIssue.TokenAddress) {
+ code = "TokenAddress";
+ } else if (id == NativeIssue.IdentifierNonZero) {
+ code = "IdentifierNonZero";
+ } else if (id == NativeIssue.InsufficientBalance) {
+ code = "InsufficientBalance";
+ }
+ return string.concat("NativeIssue: ", code);
+ }
+
+ function toString(ZoneIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == ZoneIssue.InvalidZone) {
+ code = "InvalidZone";
+ } else if (id == ZoneIssue.RejectedOrder) {
+ code = "RejectedOrder";
+ } else if (id == ZoneIssue.NotSet) {
+ code = "NotSet";
+ } else if (id == ZoneIssue.EOAZone) {
+ code = "EOAZone";
+ }
+ return string.concat("ZoneIssue: ", code);
+ }
+
+ function toString(MerkleIssue id) internal pure returns (string memory) {
+ string memory code;
+ if (id == MerkleIssue.SingleLeaf) {
+ code = "SingleLeaf";
+ } else if (id == MerkleIssue.Unsorted) {
+ code = "Unsorted";
+ }
+ return string.concat("MerkleIssue: ", code);
+ }
+
+ function toString(
+ ContractOffererIssue id
+ ) internal pure returns (string memory) {
+ string memory code;
+ if (id == ContractOffererIssue.InvalidContractOfferer) {
+ code = "InvalidContractOfferer";
+ }
+ return string.concat("ContractOffererIssue: ", code);
+ }
+
+ function toIssueString(
+ uint16 issueCode
+ ) internal pure returns (string memory issueString) {
+ uint16 issue = (issueCode / 100) * 100;
+ uint8 id = uint8(issueCode % 100);
+ if (issue == 100) {
+ return toString(GenericIssue(id));
+ } else if (issue == 200) {
+ return toString(ERC20Issue(id));
+ } else if (issue == 300) {
+ return toString(ERC721Issue(id));
+ } else if (issue == 400) {
+ return toString(ERC1155Issue(id));
+ } else if (issue == 500) {
+ return toString(ConsiderationIssue(id));
+ } else if (issue == 600) {
+ return toString(OfferIssue(id));
+ } else if (issue == 700) {
+ return toString(PrimaryFeeIssue(id));
+ } else if (issue == 800) {
+ return toString(StatusIssue(id));
+ } else if (issue == 900) {
+ return toString(TimeIssue(id));
+ } else if (issue == 1000) {
+ return toString(ConduitIssue(id));
+ } else if (issue == 1100) {
+ return toString(SignatureIssue(id));
+ } else if (issue == 1200) {
+ return toString(CreatorFeeIssue(id));
+ } else if (issue == 1300) {
+ return toString(NativeIssue(id));
+ } else if (issue == 1400) {
+ return toString(ZoneIssue(id));
+ } else if (issue == 1500) {
+ return toString(MerkleIssue(id));
+ } else if (issue == 1600) {
+ return toString(ContractOffererIssue(id));
+ } else {
+ revert("IssueStringHelpers: Unknown issue code");
+ }
+ }
+
+ function toIssueString(
+ uint16[] memory issueCodes
+ ) internal pure returns (string memory issueString) {
+ for (uint256 i; i < issueCodes.length; i++) {
+ issueString = string.concat(
+ issueString,
+ "\n ",
+ toIssueString(issueCodes[i])
+ );
+ }
+ }
+}
diff --git a/contracts/helpers/sol/ConduitControllerInterface.sol b/contracts/helpers/sol/ConduitControllerInterface.sol
new file mode 100644
index 000000000..b9f1c4b41
--- /dev/null
+++ b/contracts/helpers/sol/ConduitControllerInterface.sol
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ ConduitControllerInterface
+} from "../../interfaces/ConduitControllerInterface.sol";
diff --git a/contracts/helpers/sol/ConduitInterface.sol b/contracts/helpers/sol/ConduitInterface.sol
new file mode 100644
index 000000000..5b634ad5a
--- /dev/null
+++ b/contracts/helpers/sol/ConduitInterface.sol
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ConduitInterface } from "../../interfaces/ConduitInterface.sol";
diff --git a/contracts/helpers/sol/ContractOffererInterface.sol b/contracts/helpers/sol/ContractOffererInterface.sol
new file mode 100644
index 000000000..42b47e1c2
--- /dev/null
+++ b/contracts/helpers/sol/ContractOffererInterface.sol
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ ContractOffererInterface
+} from "../../interfaces/ContractOffererInterface.sol";
diff --git a/contracts/helpers/sol/ErrorSpaceEnums.sol b/contracts/helpers/sol/ErrorSpaceEnums.sol
new file mode 100644
index 000000000..cecd2bb41
--- /dev/null
+++ b/contracts/helpers/sol/ErrorSpaceEnums.sol
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+enum ErrorType {
+ CORE_CONSIDERATION_ERROR,
+ TOKEN_TRANSFER_ERROR,
+ SIGNATURE_VERIFICATION_ERROR,
+ REENTRANCY_ERROR,
+ CONDUIT_ERROR,
+ ZONE_ERROR,
+ AMOUNT_DERIVATION_ERROR,
+ TRANSFER_HELPER_ERROR,
+ CRITERIA_RESOLUTION_ERROR,
+ FULFILLMENT_APPLICATION_ERROR,
+ PAUSABLE_ZONE_ERROR,
+ ZONE_INTERACTION_ERROR
+}
+
+enum CoreConsiderationErrors {
+ BAD_FRACTION,
+ CANNOT_CANCEL_ORDER,
+ CONSIDERATION_LENGTH_NOT_EQUAL_TO_TOTAL_ORIGINAL,
+ CONSIDERATION_NOT_MET,
+ INSUFFICIENT_NATIVE_TOKENS_SUPPLIED,
+ INVALID_BASIC_ORDER_PARAMETER_ENCODING,
+ INVALID_CALL_TO_CONDUIT,
+ INVALID_MSG_VALUE,
+ INVALID_NATIVE_OFFER_ITEM,
+ INVALID_TIME,
+ MISMATCHED_OFFER_AND_CONSIDERATION_COMPONENTS,
+ MISSING_ORIGINAL_CONSIDERATION_ITEMS,
+ NO_SPECIFIED_ORDERS_AVAILABLE,
+ ORDER_ALREADY_FILLED,
+ ORDER_IS_CANCELLED,
+ ORDER_PARTIALLY_FILLED,
+ PARTIAL_FILLS_NOT_ENABLED_FOR_ORDER
+}
+
+enum TokenTransferrerErrors {
+ INVALID_ERC721_TRANSFER_AMOUNT,
+ MISSING_ITEM_AMOUNT,
+ UNUSED_ITEM_PARAMETERS,
+ TOKEN_TRANSFER_GENERIC_FAILURE,
+ ERC1155_BATCH_TRANSFER_GENERIC_FAILURE,
+ BAD_RETURN_VALUE_FROM_ERC20_ON_TRANSFER,
+ NO_CONTRACT,
+ INVALID1155_BATCH_TRANSFER_ENCODING
+}
+
+enum SignatureVerificationErrors {
+ BAD_SIGNATURE_V,
+ INVALID_SIGNER,
+ INVALID_SIGNATURE,
+ BAD_CONTRACT_SIGNATURE
+}
+
+enum ReentrancyErrors {
+ NO_REENTRANT_CALLS
+}
+
+enum AmountDerivationErrors {
+ INEXACT_FRACTION
+}
+
+enum TransferHelperErrors {
+ INVALID_ITEM_TYPE,
+ INVALID_ERC721_TRANSFER_AMOUNT,
+ INVALID_ERC721_RECIPIENT,
+ ERC721_RECEIVER_ERROR_REVERT_BYTES,
+ ERC721_RECEIVER_ERROR_REVERT_STRING,
+ INVALID_ERC20_IDENTIFIER,
+ RECIPIENT_CANNOT_BE_ZERO_ADDRESS,
+ INVALID_CONDUIT,
+ CONDUIT_ERROR_REVERT_STRING,
+ CONDUIT_ERROR_REVERT_BYTES
+}
+
+enum CriteriaResolutionErrors {
+ ORDER_CRITERIA_RESOLVER_OUT_OF_RANGE,
+ UNRESOLVED_OFFER_CRITERIA,
+ UNRESOLVED_CONSIDERATION_CRITERIA,
+ OFFER_CRITERIA_RESOLVER_OUT_OF_RANGE,
+ CONSIDERATION_CRITERIA_RESOLVER_OUT_OF_RANGE,
+ CRITERIA_NOT_ENABLED_FOR_ITEM,
+ INVALID_PROOF
+}
+
+enum FulfillmentApplicationErrors {
+ MISSING_FULFILLMENT_COMPONENT_ON_AGGREGATION,
+ OFFER_AND_CONSIDERATION_REQUIRED_ON_FULFILLMENT,
+ MISMATCHED_FULFILLMENT_OFFER_AND_CONSIDERATION_COMPONENTS,
+ INVALID_FULFILLMENT_COMPONENT_DATA
+}
+
+enum PausableZoneErrors {
+ INVALID_PAUSER,
+ INVALID_OPERATOR,
+ INVALID_CONTROLLER,
+ ZONE_ALREADY_EXISTS,
+ CALLER_IS_NOT_OWNER,
+ CALLER_IS_NOT_OPERATOR,
+ OWNER_CAN_NOT_BE_SET_AS_ZERO,
+ PAUSER_CAN_NOT_BE_SET_AS_ZERO,
+ CALLER_IS_NOT_POTENTIAL_OWNER
+}
+
+enum ZoneInteractionErrors {
+ INVALID_RESTRICTED_ORDER,
+ INVALID_CONTRACT_ORDER
+}
diff --git a/contracts/helpers/sol/SeaportEnums.sol b/contracts/helpers/sol/SeaportEnums.sol
new file mode 100644
index 000000000..ed84184b2
--- /dev/null
+++ b/contracts/helpers/sol/SeaportEnums.sol
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import "../../lib/ConsiderationEnums.sol";
diff --git a/contracts/helpers/sol/SeaportInterface.sol b/contracts/helpers/sol/SeaportInterface.sol
new file mode 100644
index 000000000..04eb51311
--- /dev/null
+++ b/contracts/helpers/sol/SeaportInterface.sol
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ ConsiderationInterface as SeaportInterface
+} from "../../interfaces/ConsiderationInterface.sol";
diff --git a/contracts/helpers/sol/SeaportSol.sol b/contracts/helpers/sol/SeaportSol.sol
new file mode 100644
index 000000000..49c79ef4c
--- /dev/null
+++ b/contracts/helpers/sol/SeaportSol.sol
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import "./SeaportStructs.sol";
+import "./SeaportEnums.sol";
+import "./lib/SeaportStructLib.sol";
+import "./lib/SeaportEnumsLib.sol";
+import "./fulfillments/lib/Structs.sol";
+import { SeaportArrays } from "./lib/SeaportArrays.sol";
+import { SeaportInterface } from "./SeaportInterface.sol";
+import {
+ ConsiderationInterface
+} from "../../interfaces/ConsiderationInterface.sol";
+import { ConduitInterface } from "./ConduitInterface.sol";
+import { ConduitControllerInterface } from "./ConduitControllerInterface.sol";
+import { ZoneInterface } from "./ZoneInterface.sol";
+import { ContractOffererInterface } from "./ContractOffererInterface.sol";
+import { Solarray } from "./Solarray.sol";
+import {
+ FulfillAvailableHelper
+} from "./fulfillments/available/FulfillAvailableHelper.sol";
+import {
+ MatchFulfillmentHelper
+} from "./fulfillments/match/MatchFulfillmentHelper.sol";
+import {
+ MatchComponent,
+ MatchComponentType
+} from "./lib/types/MatchComponentType.sol";
diff --git a/contracts/helpers/sol/SeaportStructs.sol b/contracts/helpers/sol/SeaportStructs.sol
new file mode 100644
index 000000000..505e04960
--- /dev/null
+++ b/contracts/helpers/sol/SeaportStructs.sol
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import "../../lib/ConsiderationStructs.sol";
diff --git a/contracts/helpers/sol/Solarray.sol b/contracts/helpers/sol/Solarray.sol
new file mode 100644
index 000000000..827bbf4c3
--- /dev/null
+++ b/contracts/helpers/sol/Solarray.sol
@@ -0,0 +1,2098 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.6.2 <0.9.0;
+
+library Solarray {
+ function uint8s(uint8 a) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint8s(uint8 a, uint8 b) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint8s(
+ uint8 a,
+ uint8 b,
+ uint8 c
+ ) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint8s(
+ uint8 a,
+ uint8 b,
+ uint8 c,
+ uint8 d
+ ) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint8s(
+ uint8 a,
+ uint8 b,
+ uint8 c,
+ uint8 d,
+ uint8 e
+ ) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint8s(
+ uint8 a,
+ uint8 b,
+ uint8 c,
+ uint8 d,
+ uint8 e,
+ uint8 f
+ ) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint8s(
+ uint8 a,
+ uint8 b,
+ uint8 c,
+ uint8 d,
+ uint8 e,
+ uint8 f,
+ uint8 g
+ ) internal pure returns (uint8[] memory) {
+ uint8[] memory arr = new uint8[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uint16s(uint16 a) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint16s(
+ uint16 a,
+ uint16 b
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint16s(
+ uint16 a,
+ uint16 b,
+ uint16 c
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint16s(
+ uint16 a,
+ uint16 b,
+ uint16 c,
+ uint16 d
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint16s(
+ uint16 a,
+ uint16 b,
+ uint16 c,
+ uint16 d,
+ uint16 e
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint16s(
+ uint16 a,
+ uint16 b,
+ uint16 c,
+ uint16 d,
+ uint16 e,
+ uint16 f
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint16s(
+ uint16 a,
+ uint16 b,
+ uint16 c,
+ uint16 d,
+ uint16 e,
+ uint16 f,
+ uint16 g
+ ) internal pure returns (uint16[] memory) {
+ uint16[] memory arr = new uint16[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uint32s(uint32 a) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint32s(
+ uint32 a,
+ uint32 b
+ ) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint32s(
+ uint32 a,
+ uint32 b,
+ uint32 c
+ ) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint32s(
+ uint32 a,
+ uint32 b,
+ uint32 c,
+ uint32 d
+ ) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint32s(
+ uint32 a,
+ uint32 b,
+ uint32 c,
+ uint32 d,
+ uint32 e
+ ) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint32s(
+ uint32 a,
+ uint32 b,
+ uint32 c,
+ uint32 d,
+ uint32 e,
+ uint32 f
+ ) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint32s(
+ uint32 a,
+ uint32 b,
+ uint32 c,
+ uint32 d,
+ uint32 e,
+ uint32 f,
+ uint32 g
+ ) internal pure returns (uint32[] memory) {
+ uint32[] memory arr = new uint32[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uint40s(uint40 a) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint40s(
+ uint40 a,
+ uint40 b
+ ) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint40s(
+ uint40 a,
+ uint40 b,
+ uint40 c
+ ) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint40s(
+ uint40 a,
+ uint40 b,
+ uint40 c,
+ uint40 d
+ ) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint40s(
+ uint40 a,
+ uint40 b,
+ uint40 c,
+ uint40 d,
+ uint40 e
+ ) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint40s(
+ uint40 a,
+ uint40 b,
+ uint40 c,
+ uint40 d,
+ uint40 e,
+ uint40 f
+ ) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint40s(
+ uint40 a,
+ uint40 b,
+ uint40 c,
+ uint40 d,
+ uint40 e,
+ uint40 f,
+ uint40 g
+ ) internal pure returns (uint40[] memory) {
+ uint40[] memory arr = new uint40[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uint64s(uint64 a) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint64s(
+ uint64 a,
+ uint64 b
+ ) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint64s(
+ uint64 a,
+ uint64 b,
+ uint64 c
+ ) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint64s(
+ uint64 a,
+ uint64 b,
+ uint64 c,
+ uint64 d
+ ) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint64s(
+ uint64 a,
+ uint64 b,
+ uint64 c,
+ uint64 d,
+ uint64 e
+ ) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint64s(
+ uint64 a,
+ uint64 b,
+ uint64 c,
+ uint64 d,
+ uint64 e,
+ uint64 f
+ ) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint64s(
+ uint64 a,
+ uint64 b,
+ uint64 c,
+ uint64 d,
+ uint64 e,
+ uint64 f,
+ uint64 g
+ ) internal pure returns (uint64[] memory) {
+ uint64[] memory arr = new uint64[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uint128s(uint128 a) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint128s(
+ uint128 a,
+ uint128 b
+ ) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint128s(
+ uint128 a,
+ uint128 b,
+ uint128 c
+ ) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint128s(
+ uint128 a,
+ uint128 b,
+ uint128 c,
+ uint128 d
+ ) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint128s(
+ uint128 a,
+ uint128 b,
+ uint128 c,
+ uint128 d,
+ uint128 e
+ ) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint128s(
+ uint128 a,
+ uint128 b,
+ uint128 c,
+ uint128 d,
+ uint128 e,
+ uint128 f
+ ) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint128s(
+ uint128 a,
+ uint128 b,
+ uint128 c,
+ uint128 d,
+ uint128 e,
+ uint128 f,
+ uint128 g
+ ) internal pure returns (uint128[] memory) {
+ uint128[] memory arr = new uint128[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uint256s(uint256 a) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uint256s(
+ uint256 a,
+ uint256 b
+ ) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uint256s(
+ uint256 a,
+ uint256 b,
+ uint256 c
+ ) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uint256s(
+ uint256 a,
+ uint256 b,
+ uint256 c,
+ uint256 d
+ ) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uint256s(
+ uint256 a,
+ uint256 b,
+ uint256 c,
+ uint256 d,
+ uint256 e
+ ) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uint256s(
+ uint256 a,
+ uint256 b,
+ uint256 c,
+ uint256 d,
+ uint256 e,
+ uint256 f
+ ) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uint256s(
+ uint256 a,
+ uint256 b,
+ uint256 c,
+ uint256 d,
+ uint256 e,
+ uint256 f,
+ uint256 g
+ ) internal pure returns (uint256[] memory) {
+ uint256[] memory arr = new uint256[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function int8s(int8 a) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function int8s(int8 a, int8 b) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function int8s(
+ int8 a,
+ int8 b,
+ int8 c
+ ) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function int8s(
+ int8 a,
+ int8 b,
+ int8 c,
+ int8 d
+ ) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function int8s(
+ int8 a,
+ int8 b,
+ int8 c,
+ int8 d,
+ int8 e
+ ) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function int8s(
+ int8 a,
+ int8 b,
+ int8 c,
+ int8 d,
+ int8 e,
+ int8 f
+ ) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function int8s(
+ int8 a,
+ int8 b,
+ int8 c,
+ int8 d,
+ int8 e,
+ int8 f,
+ int8 g
+ ) internal pure returns (int8[] memory) {
+ int8[] memory arr = new int8[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function int16s(int16 a) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function int16s(int16 a, int16 b) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function int16s(
+ int16 a,
+ int16 b,
+ int16 c
+ ) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function int16s(
+ int16 a,
+ int16 b,
+ int16 c,
+ int16 d
+ ) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function int16s(
+ int16 a,
+ int16 b,
+ int16 c,
+ int16 d,
+ int16 e
+ ) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function int16s(
+ int16 a,
+ int16 b,
+ int16 c,
+ int16 d,
+ int16 e,
+ int16 f
+ ) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function int16s(
+ int16 a,
+ int16 b,
+ int16 c,
+ int16 d,
+ int16 e,
+ int16 f,
+ int16 g
+ ) internal pure returns (int16[] memory) {
+ int16[] memory arr = new int16[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function int32s(int32 a) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function int32s(int32 a, int32 b) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function int32s(
+ int32 a,
+ int32 b,
+ int32 c
+ ) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function int32s(
+ int32 a,
+ int32 b,
+ int32 c,
+ int32 d
+ ) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function int32s(
+ int32 a,
+ int32 b,
+ int32 c,
+ int32 d,
+ int32 e
+ ) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function int32s(
+ int32 a,
+ int32 b,
+ int32 c,
+ int32 d,
+ int32 e,
+ int32 f
+ ) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function int32s(
+ int32 a,
+ int32 b,
+ int32 c,
+ int32 d,
+ int32 e,
+ int32 f,
+ int32 g
+ ) internal pure returns (int32[] memory) {
+ int32[] memory arr = new int32[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function int64s(int64 a) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function int64s(int64 a, int64 b) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function int64s(
+ int64 a,
+ int64 b,
+ int64 c
+ ) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function int64s(
+ int64 a,
+ int64 b,
+ int64 c,
+ int64 d
+ ) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function int64s(
+ int64 a,
+ int64 b,
+ int64 c,
+ int64 d,
+ int64 e
+ ) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function int64s(
+ int64 a,
+ int64 b,
+ int64 c,
+ int64 d,
+ int64 e,
+ int64 f
+ ) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function int64s(
+ int64 a,
+ int64 b,
+ int64 c,
+ int64 d,
+ int64 e,
+ int64 f,
+ int64 g
+ ) internal pure returns (int64[] memory) {
+ int64[] memory arr = new int64[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function int128s(int128 a) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function int128s(
+ int128 a,
+ int128 b
+ ) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function int128s(
+ int128 a,
+ int128 b,
+ int128 c
+ ) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function int128s(
+ int128 a,
+ int128 b,
+ int128 c,
+ int128 d
+ ) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function int128s(
+ int128 a,
+ int128 b,
+ int128 c,
+ int128 d,
+ int128 e
+ ) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function int128s(
+ int128 a,
+ int128 b,
+ int128 c,
+ int128 d,
+ int128 e,
+ int128 f
+ ) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function int128s(
+ int128 a,
+ int128 b,
+ int128 c,
+ int128 d,
+ int128 e,
+ int128 f,
+ int128 g
+ ) internal pure returns (int128[] memory) {
+ int128[] memory arr = new int128[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function int256s(int256 a) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function int256s(
+ int256 a,
+ int256 b
+ ) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function int256s(
+ int256 a,
+ int256 b,
+ int256 c
+ ) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function int256s(
+ int256 a,
+ int256 b,
+ int256 c,
+ int256 d
+ ) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function int256s(
+ int256 a,
+ int256 b,
+ int256 c,
+ int256 d,
+ int256 e
+ ) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function int256s(
+ int256 a,
+ int256 b,
+ int256 c,
+ int256 d,
+ int256 e,
+ int256 f
+ ) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function int256s(
+ int256 a,
+ int256 b,
+ int256 c,
+ int256 d,
+ int256 e,
+ int256 f,
+ int256 g
+ ) internal pure returns (int256[] memory) {
+ int256[] memory arr = new int256[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bytes1s(bytes1 a) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bytes1s(
+ bytes1 a,
+ bytes1 b
+ ) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bytes1s(
+ bytes1 a,
+ bytes1 b,
+ bytes1 c
+ ) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bytes1s(
+ bytes1 a,
+ bytes1 b,
+ bytes1 c,
+ bytes1 d
+ ) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bytes1s(
+ bytes1 a,
+ bytes1 b,
+ bytes1 c,
+ bytes1 d,
+ bytes1 e
+ ) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bytes1s(
+ bytes1 a,
+ bytes1 b,
+ bytes1 c,
+ bytes1 d,
+ bytes1 e,
+ bytes1 f
+ ) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bytes1s(
+ bytes1 a,
+ bytes1 b,
+ bytes1 c,
+ bytes1 d,
+ bytes1 e,
+ bytes1 f,
+ bytes1 g
+ ) internal pure returns (bytes1[] memory) {
+ bytes1[] memory arr = new bytes1[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bytes8s(bytes8 a) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bytes8s(
+ bytes8 a,
+ bytes8 b
+ ) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bytes8s(
+ bytes8 a,
+ bytes8 b,
+ bytes8 c
+ ) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bytes8s(
+ bytes8 a,
+ bytes8 b,
+ bytes8 c,
+ bytes8 d
+ ) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bytes8s(
+ bytes8 a,
+ bytes8 b,
+ bytes8 c,
+ bytes8 d,
+ bytes8 e
+ ) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bytes8s(
+ bytes8 a,
+ bytes8 b,
+ bytes8 c,
+ bytes8 d,
+ bytes8 e,
+ bytes8 f
+ ) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bytes8s(
+ bytes8 a,
+ bytes8 b,
+ bytes8 c,
+ bytes8 d,
+ bytes8 e,
+ bytes8 f,
+ bytes8 g
+ ) internal pure returns (bytes8[] memory) {
+ bytes8[] memory arr = new bytes8[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bytes16s(bytes16 a) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bytes16s(
+ bytes16 a,
+ bytes16 b
+ ) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bytes16s(
+ bytes16 a,
+ bytes16 b,
+ bytes16 c
+ ) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bytes16s(
+ bytes16 a,
+ bytes16 b,
+ bytes16 c,
+ bytes16 d
+ ) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bytes16s(
+ bytes16 a,
+ bytes16 b,
+ bytes16 c,
+ bytes16 d,
+ bytes16 e
+ ) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bytes16s(
+ bytes16 a,
+ bytes16 b,
+ bytes16 c,
+ bytes16 d,
+ bytes16 e,
+ bytes16 f
+ ) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bytes16s(
+ bytes16 a,
+ bytes16 b,
+ bytes16 c,
+ bytes16 d,
+ bytes16 e,
+ bytes16 f,
+ bytes16 g
+ ) internal pure returns (bytes16[] memory) {
+ bytes16[] memory arr = new bytes16[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bytes20s(bytes20 a) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bytes20s(
+ bytes20 a,
+ bytes20 b
+ ) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bytes20s(
+ bytes20 a,
+ bytes20 b,
+ bytes20 c
+ ) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bytes20s(
+ bytes20 a,
+ bytes20 b,
+ bytes20 c,
+ bytes20 d
+ ) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bytes20s(
+ bytes20 a,
+ bytes20 b,
+ bytes20 c,
+ bytes20 d,
+ bytes20 e
+ ) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bytes20s(
+ bytes20 a,
+ bytes20 b,
+ bytes20 c,
+ bytes20 d,
+ bytes20 e,
+ bytes20 f
+ ) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bytes20s(
+ bytes20 a,
+ bytes20 b,
+ bytes20 c,
+ bytes20 d,
+ bytes20 e,
+ bytes20 f,
+ bytes20 g
+ ) internal pure returns (bytes20[] memory) {
+ bytes20[] memory arr = new bytes20[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bytes32s(bytes32 a) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bytes32s(
+ bytes32 a,
+ bytes32 b
+ ) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bytes32s(
+ bytes32 a,
+ bytes32 b,
+ bytes32 c
+ ) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bytes32s(
+ bytes32 a,
+ bytes32 b,
+ bytes32 c,
+ bytes32 d
+ ) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bytes32s(
+ bytes32 a,
+ bytes32 b,
+ bytes32 c,
+ bytes32 d,
+ bytes32 e
+ ) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bytes32s(
+ bytes32 a,
+ bytes32 b,
+ bytes32 c,
+ bytes32 d,
+ bytes32 e,
+ bytes32 f
+ ) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bytes32s(
+ bytes32 a,
+ bytes32 b,
+ bytes32 c,
+ bytes32 d,
+ bytes32 e,
+ bytes32 f,
+ bytes32 g
+ ) internal pure returns (bytes32[] memory) {
+ bytes32[] memory arr = new bytes32[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bytess(bytes memory a) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bytess(
+ bytes memory a,
+ bytes memory b
+ ) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bytess(
+ bytes memory a,
+ bytes memory b,
+ bytes memory c
+ ) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bytess(
+ bytes memory a,
+ bytes memory b,
+ bytes memory c,
+ bytes memory d
+ ) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bytess(
+ bytes memory a,
+ bytes memory b,
+ bytes memory c,
+ bytes memory d,
+ bytes memory e
+ ) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bytess(
+ bytes memory a,
+ bytes memory b,
+ bytes memory c,
+ bytes memory d,
+ bytes memory e,
+ bytes memory f
+ ) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bytess(
+ bytes memory a,
+ bytes memory b,
+ bytes memory c,
+ bytes memory d,
+ bytes memory e,
+ bytes memory f,
+ bytes memory g
+ ) internal pure returns (bytes[] memory) {
+ bytes[] memory arr = new bytes[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function addresses(address a) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function addresses(
+ address a,
+ address b
+ ) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function addresses(
+ address a,
+ address b,
+ address c
+ ) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function addresses(
+ address a,
+ address b,
+ address c,
+ address d
+ ) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function addresses(
+ address a,
+ address b,
+ address c,
+ address d,
+ address e
+ ) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function addresses(
+ address a,
+ address b,
+ address c,
+ address d,
+ address e,
+ address f
+ ) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function addresses(
+ address a,
+ address b,
+ address c,
+ address d,
+ address e,
+ address f,
+ address g
+ ) internal pure returns (address[] memory) {
+ address[] memory arr = new address[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function bools(bool a) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function bools(bool a, bool b) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function bools(
+ bool a,
+ bool b,
+ bool c
+ ) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function bools(
+ bool a,
+ bool b,
+ bool c,
+ bool d
+ ) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function bools(
+ bool a,
+ bool b,
+ bool c,
+ bool d,
+ bool e
+ ) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function bools(
+ bool a,
+ bool b,
+ bool c,
+ bool d,
+ bool e,
+ bool f
+ ) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function bools(
+ bool a,
+ bool b,
+ bool c,
+ bool d,
+ bool e,
+ bool f,
+ bool g
+ ) internal pure returns (bool[] memory) {
+ bool[] memory arr = new bool[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function strings(string memory a) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function strings(
+ string memory a,
+ string memory b
+ ) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function strings(
+ string memory a,
+ string memory b,
+ string memory c
+ ) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function strings(
+ string memory a,
+ string memory b,
+ string memory c,
+ string memory d
+ ) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function strings(
+ string memory a,
+ string memory b,
+ string memory c,
+ string memory d,
+ string memory e
+ ) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function strings(
+ string memory a,
+ string memory b,
+ string memory c,
+ string memory d,
+ string memory e,
+ string memory f
+ ) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function strings(
+ string memory a,
+ string memory b,
+ string memory c,
+ string memory d,
+ string memory e,
+ string memory f,
+ string memory g
+ ) internal pure returns (string[] memory) {
+ string[] memory arr = new string[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+}
diff --git a/contracts/helpers/sol/SpaceEnums.sol b/contracts/helpers/sol/SpaceEnums.sol
new file mode 100644
index 000000000..b84f0102b
--- /dev/null
+++ b/contracts/helpers/sol/SpaceEnums.sol
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+enum Method {
+ BASIC,
+ FULFILL,
+ MATCH,
+ FULFILL_AVAILABLE,
+ FULFILL_ADVANCED,
+ MATCH_ADVANCED,
+ FULFILL_AVAILABLE_ADVANCED,
+ CANCEL,
+ VALIDATE,
+ INCREMENT_COUNTER,
+ GET_ORDER_HASH,
+ GET_ORDER_STATUS,
+ GET_COUNTER,
+ INFORMATION,
+ NAME
+}
+
+enum OrderStatusEnum {
+ AVAILABLE, // not validated or fulfilled; implicitly validated via signature except when match is called
+ VALIDATED, // validated on-chain
+ PARTIAL, // partially fulfilled
+ FULFILLED, // completely fulfilled
+ CANCELLED_EXPLICIT, // explicit cancellation
+ CANCELLED_COUNTER, // canceled via counter increment (reverts due to invalid sig)
+ REVERT // fulfilling reverts
+}
+
+enum BroadOrderType {
+ FULL,
+ PARTIAL,
+ CONTRACT
+}
+
+// !BroadOrderType.CONTRACT <- Zone
+enum Zone {
+ NONE,
+ PASS,
+ FAIL
+}
+
+// !BroadOrderType.CONTRACT <- OrderAuth
+enum NumOrders {
+ ONE,
+ MULTIPLE
+}
+
+// NumOrders.MULTIPLE && any(OrderFulfillment.PARTIAL) <- OrderComposition
+enum OrdersComposition {
+ HOMOGENOUS,
+ HETEROGENOUS
+}
+
+// OrderStatus.REVERT && !Zone.FAIL && !Offerer.CONTRACT_OFFERER <- OrderStatusRevertReason
+enum OrderStatusRevertReason {
+ NATIVE, // receive hook fails
+ ERC1155 // receive hook fails
+}
+
+// ItemType.ERC20/ERC1155/ERC721 <- TokenIndex
+// (test contracts have 3 of each token deployed; can mix and match)
+enum TokenIndex {
+ ONE,
+ TWO,
+ THREE
+}
+
+enum Criteria {
+ MERKLE, // non-zero criteria
+ WILDCARD // criteria zero
+}
+
+// Criteria.WILDCARD/MERKLE <- CriteriaResolved
+enum CriteriaResolved {
+ VALID, // correctly resolved
+ INVALID, // incorrectly resolved
+ UNAVAILABLE // resolved but not owned/approved
+}
+
+enum Amount {
+ FIXED,
+ ASCENDING,
+ DESCENDING
+}
+
+enum AmountDegree {
+ // ZERO, ?
+ SMALL,
+ MEDIUM,
+ LARGE,
+ WUMBO
+}
+
+enum FulfillmentRecipient {
+ ZERO,
+ ALICE,
+ BOB,
+ EVE
+}
+
+// ConsiderationItem.* / ReceivedItem.* / Method.*ADVANCED <- Recipient
+enum Recipient {
+ // ZERO,?
+ OFFERER,
+ RECIPIENT,
+ DILLON,
+ EVE,
+ FRANK
+ // INVALID
+}
+
+enum RecipientDirty {
+ CLEAN,
+ DIRTY
+}
+
+enum Caller {
+ TEST_CONTRACT,
+ ALICE,
+ BOB,
+ CAROL,
+ DILLON,
+ EVE,
+ FRANK
+}
+
+// disregarded in the self_ad_hoc case
+enum Offerer {
+ TEST_CONTRACT,
+ ALICE, // consider EOA space enum
+ BOB,
+ CONTRACT_OFFERER,
+ EIP1271
+}
+
+// debatable if needed but we do need to test zonehash permutations
+enum ZoneHash {
+ NONE,
+ VALID,
+ INVALID
+}
+
+// Offerer.CONTRACT_OFFERER <- ContractOfferer
+enum ContractOfferer {
+ PASS,
+ FAIL_GENERATE,
+ FAIL_MIN_RECEIVED,
+ FAIL_MAX_SPENT,
+ FAIL_RATIFY
+}
+
+// ContractOfferer.PASS <- ContractOffererPassGenerated
+enum ContractOffererPassGenerated {
+ IDENTITY,
+ MIN_RECEIVED,
+ MAX_SPENT,
+ BOTH
+}
+
+// ContractOffererPassGenerated.MIN_RECEIVED/BOTH <- ContractOffererPassMinReceived
+enum ContractOffererPassMinReceived {
+ MORE_MIN_RECEIVED,
+ EXTRA_MIN_RECEIVED,
+ BOTH
+}
+
+// ContractOffererPassGenerated.MAX_SPENT/BOTH <- ContractOffererPassMaxSpent
+enum ContractOffererPassMaxSpent {
+ LESS_MAX_SPENT,
+ TRUNCATED_MAX_SPENT,
+ BOTH
+}
+
+enum ContractOffererFailGenerated {
+ MIN_RECEIVED,
+ MAX_SPENT,
+ BOTH
+}
+
+// ContractOffererFailGenerated.MIN_RECEIVED/BOTH <- ContractOffererFailMinReceived
+enum ContractOffererFailMinReceived {
+ LESS_MIN_RECEIVED,
+ TRUNCATED_MIN_RECEIVED,
+ BOTH
+}
+
+// ContractOffererFailGenerated.MAX_SPENT/BOTH <- ContractOffererFailMaxSpent
+enum ContractOffererFailMaxSpent {
+ MORE_MAX_SPENT,
+ EXTRA_MAX_SPENT,
+ BOTH
+}
+
+enum Time {
+ // valid granularity important for ascending/descending
+ STARTS_IN_FUTURE,
+ EXACT_START, // order is live
+ ONGOING,
+ EXACT_END, // order is expired
+ EXPIRED
+}
+
+// Method.MATCH* <- MatchValidation
+enum MatchValidation {
+ SELF_AD_HOC,
+ SIGNATURE
+}
+
+enum SignatureMethod {
+ EOA,
+ VALIDATE,
+ EIP1271,
+ CONTRACT,
+ SELF_AD_HOC
+}
+
+// Offerer.EOA <- EOASignature
+enum EOASignature {
+ STANDARD,
+ EIP2098,
+ BULK,
+ BULK2098
+}
+
+// Offerer.EIP1271 <- EIP1271Signature
+enum EIP1271Signature {
+ EIP1271,
+ EIP1271_BULKLIKE
+}
+
+// Validation.SIGNATURE <- SignatureValidity
+enum SignatureValidity {
+ VALID,
+ INVALID
+}
+
+// EOASignature.BULK/BULK2098+EIP1271Signature.EIP1271_BULKLIKE <- BulkSignatureSize
+enum BulkSignatureSize {
+ ONE,
+ TWO,
+ THREE,
+ FOUR,
+ FIVE,
+ SIX,
+ SEVEN,
+ EIGHT,
+ NINE,
+ TEN,
+ ELEVEN,
+ TWELVE,
+ THIRTEEN,
+ FOURTEEN,
+ FIFTEEN,
+ SIXTEEN,
+ SEVENTEEN,
+ EIGHTEEN,
+ NINETEEN,
+ TWENTY,
+ TWENTYONE,
+ TWENTYTWO,
+ TWENTYTHREE,
+ TWENTYFOUR
+}
+
+enum Salt {
+ VALID,
+ DUPLICATE // ?
+}
+
+enum ConduitChoice {
+ NONE,
+ ONE,
+ TWO
+}
+
+enum Counter {
+ VALID,
+ INVALID
+}
+
+// Counter.INVALID <- CounterValue
+enum CounterValue {
+ LESS,
+ GREATER
+}
+
+enum TotalOriginalConsiderationItems {
+ LESS,
+ EQUAL,
+ GREATER
+}
+
+// Method.*Advanced <- Fraction
+enum Fraction {
+ INVALID,
+ VALID
+}
+
+// Fraction.VALID <- ValidFraction
+enum ValidFraction {
+ SMALL,
+ HALF,
+ LARGE,
+ WHOLE
+}
+
+enum InvalidFraction {
+ INVALID, // order is a full order
+ UNEVEN, // cannot divide into amount
+ IRREGULAR // N/M where N > M
+}
+
+enum CriteriaProofs {
+ LESS, // too few proofs
+ EQUAL, // sufficient number of proofs
+ GREATER // extra proofs
+}
+
+// Method.FULFILL_AVAILABLE* <= FulfillAvailableFulfillments
+enum FulfillAvailableFulfillments {
+ NAIVE, // one component per offer + consideration item, ie, unaggregated
+ AGGREGATED,
+ INVALID
+}
+
+// FulfillAvailableFulfillments.INVALID <- InvalidFulfillAvailableFulfillments
+enum InvalidFulfillAvailableFulfillments {
+ OFFER,
+ CONSIDERATION,
+ BOTH
+}
+
+// InvalidFulfillAvailableFulfillments.* <- InvalidFulfillAvailableFulfillmentsCondition
+enum InvalidFulfillAvailableFulfillmentsCondition {
+ INVALID, // ie not correctly constructed, duplicates, etc
+ LESS, // ie missing fulfillments
+ GREATER // ie including duplicates
+}
+
+// FulfillAvailableFulfillments.AGGREGATED <- AggregatedFulfillAvailableFulfillments
+enum AggregatedFulfillAvailableFulfillments {
+ OFFER,
+ CONSIDERATION,
+ BOTH
+}
+
+enum BasicOrderCategory {
+ NONE,
+ LISTING,
+ BID
+}
+
+enum Tips {
+ NONE,
+ TIPS
+}
+
+enum UnavailableReason {
+ AVAILABLE,
+ EXPIRED,
+ STARTS_IN_FUTURE,
+ CANCELLED,
+ ALREADY_FULFILLED,
+ MAX_FULFILLED_SATISFIED,
+ GENERATE_ORDER_FAILURE
+}
+
+enum ExtraData {
+ NONE,
+ RANDOM
+}
+
+enum ContractOrderRebate {
+ NONE,
+ MORE_OFFER_ITEMS,
+ MORE_OFFER_ITEM_AMOUNTS,
+ LESS_CONSIDERATION_ITEMS,
+ LESS_CONSIDERATION_ITEM_AMOUNTS
+}
diff --git a/contracts/helpers/sol/StructSpace.sol b/contracts/helpers/sol/StructSpace.sol
new file mode 100644
index 000000000..e2291e6a9
--- /dev/null
+++ b/contracts/helpers/sol/StructSpace.sol
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ItemType } from "./SeaportEnums.sol";
+
+import {
+ Amount,
+ BroadOrderType,
+ Caller,
+ ConduitChoice,
+ ContractOrderRebate,
+ Criteria,
+ EOASignature,
+ ExtraData,
+ FulfillmentRecipient,
+ Offerer,
+ Recipient,
+ SignatureMethod,
+ Time,
+ Tips,
+ TokenIndex,
+ UnavailableReason,
+ Zone,
+ ZoneHash
+} from "./SpaceEnums.sol";
+
+import {
+ FulfillmentStrategy
+} from "./fulfillments/lib/FulfillmentLib.sol";
+
+struct OfferItemSpace {
+ ItemType itemType;
+ TokenIndex tokenIndex;
+ Criteria criteria;
+ Amount amount;
+}
+
+struct ConsiderationItemSpace {
+ ItemType itemType;
+ TokenIndex tokenIndex;
+ Criteria criteria;
+ Amount amount;
+ Recipient recipient;
+}
+
+struct SpentItemSpace {
+ ItemType itemType;
+ TokenIndex tokenIndex;
+}
+
+struct ReceivedItemSpace {
+ ItemType itemType;
+ TokenIndex tokenIndex;
+ Recipient recipient;
+}
+
+struct OrderComponentsSpace {
+ Offerer offerer;
+ Zone zone;
+ OfferItemSpace[] offer;
+ ConsiderationItemSpace[] consideration;
+ BroadOrderType orderType;
+ Time time;
+ ZoneHash zoneHash;
+ SignatureMethod signatureMethod;
+ EOASignature eoaSignatureType;
+ uint256 bulkSigHeight;
+ uint256 bulkSigIndex;
+ ConduitChoice conduit;
+ Tips tips;
+ UnavailableReason unavailableReason; // ignored unless unavailable
+ ExtraData extraData;
+ ContractOrderRebate rebate;
+}
+
+struct AdvancedOrdersSpace {
+ OrderComponentsSpace[] orders;
+ bool isMatchable;
+ uint256 maximumFulfilled;
+ FulfillmentRecipient recipient;
+ ConduitChoice conduit;
+ Caller caller;
+ FulfillmentStrategy strategy;
+}
diff --git a/contracts/helpers/sol/ZoneInterface.sol b/contracts/helpers/sol/ZoneInterface.sol
new file mode 100644
index 000000000..6a2531209
--- /dev/null
+++ b/contracts/helpers/sol/ZoneInterface.sol
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ZoneInterface } from "../../interfaces/ZoneInterface.sol";
diff --git a/contracts/helpers/sol/executions/ExecutionHelper.sol b/contracts/helpers/sol/executions/ExecutionHelper.sol
new file mode 100644
index 000000000..b1fda3ee7
--- /dev/null
+++ b/contracts/helpers/sol/executions/ExecutionHelper.sol
@@ -0,0 +1,1188 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AmountDeriverHelper
+} from "../lib/fulfillment/AmountDeriverHelper.sol";
+
+import { AdvancedOrderLib } from "../lib/AdvancedOrderLib.sol";
+
+import { SpentItemLib } from "../lib/SpentItemLib.sol";
+
+import { ReceivedItemLib } from "../lib/ReceivedItemLib.sol";
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ FulfillmentComponent,
+ Order,
+ ReceivedItem,
+ SpentItem
+} from "../../../lib/ConsiderationStructs.sol";
+
+import { ItemType, Side } from "../../../lib/ConsiderationEnums.sol";
+
+import {
+ FulfillmentDetails,
+ OrderDetails
+} from "../fulfillments/lib/Structs.sol";
+
+import { UnavailableReason } from "../SpaceEnums.sol";
+
+/**
+ * @dev Helper contract for deriving explicit and executions from orders and
+ * fulfillment details
+ *
+ * @dev TODO: move to the tests folder? not really useful for normal scripting
+ */
+library ExecutionHelper {
+ using AdvancedOrderLib for AdvancedOrder[];
+ using SpentItemLib for SpentItem[];
+ using ReceivedItemLib for ReceivedItem[];
+
+ /**
+ * @dev get explicit and implicit executions for a fulfillAvailable call
+ *
+ * @param fulfillmentDetails the fulfillment details
+ * @param offerFulfillments 2d array of offer fulfillment components
+ * @param considerationFulfillments 2d array of consideration fulfillment
+ *
+ * @return explicitExecutions the explicit executions
+ * @return implicitExecutionsPre the implicit executions (unspecified offer
+ * items)
+ * @return implicitExecutionsPost the implicit executions (unspecified offer
+ * items)
+ */
+ function getFulfillAvailableExecutions(
+ FulfillmentDetails memory fulfillmentDetails,
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ OrderDetails[] memory orderDetails
+ )
+ public
+ pure
+ returns (
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost,
+ uint256 nativeTokensReturned
+ )
+ {
+ FulfillmentDetails memory details = copy(fulfillmentDetails);
+
+ bool[] memory availableOrders = new bool[](orderDetails.length);
+
+ for (uint256 i = 0; i < orderDetails.length; ++i) {
+ availableOrders[i] =
+ orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE;
+ }
+
+ implicitExecutionsPre = processImplicitPreOrderExecutions(
+ details,
+ availableOrders
+ );
+
+ explicitExecutions = processExplicitExecutionsFromAggregatedComponents(
+ details,
+ offerFulfillments,
+ considerationFulfillments,
+ availableOrders
+ );
+
+ implicitExecutionsPost = processImplicitPostOrderExecutions(
+ details,
+ availableOrders
+ );
+
+ nativeTokensReturned = _handleExcessNativeTokens(
+ details,
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost
+ );
+ }
+
+ /**
+ * @dev Process an array of fulfillments into an array of explicit and
+ * implicit executions.
+ *
+ * @param fulfillmentDetails The fulfillment details.
+ * @param fulfillments An array of fulfillments.
+ *
+ * @return explicitExecutions The explicit executions
+ * @return implicitExecutionsPre The implicit executions
+ * @return implicitExecutionsPost The implicit executions
+ */
+ function getMatchExecutions(
+ FulfillmentDetails memory fulfillmentDetails,
+ Fulfillment[] memory fulfillments
+ )
+ internal
+ pure
+ returns (
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost,
+ uint256 nativeTokensReturned
+ )
+ {
+ FulfillmentDetails memory details = copy(fulfillmentDetails);
+
+ explicitExecutions = new Execution[](fulfillments.length);
+
+ uint256 filteredExecutions = 0;
+
+ bool[] memory availableOrders = new bool[](details.orders.length);
+
+ for (uint256 i = 0; i < details.orders.length; ++i) {
+ availableOrders[i] = true;
+ }
+
+ implicitExecutionsPre = processImplicitPreOrderExecutions(
+ details,
+ availableOrders
+ );
+
+ for (uint256 i = 0; i < fulfillments.length; i++) {
+ Execution memory execution = processExecutionFromFulfillment(
+ details,
+ fulfillments[i]
+ );
+
+ if (
+ execution.item.recipient == execution.offerer &&
+ execution.item.itemType != ItemType.NATIVE
+ ) {
+ filteredExecutions++;
+ } else {
+ explicitExecutions[i - filteredExecutions] = execution;
+ }
+ }
+
+ // If some number of executions have been filtered...
+ if (filteredExecutions != 0) {
+ // reduce the total length of the executions array.
+ assembly {
+ mstore(
+ explicitExecutions,
+ sub(mload(explicitExecutions), filteredExecutions)
+ )
+ }
+ }
+
+ implicitExecutionsPost = processImplicitPostOrderExecutions(
+ details,
+ availableOrders
+ );
+
+ nativeTokensReturned = _handleExcessNativeTokens(
+ details,
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost
+ );
+ }
+
+ function processExcessNativeTokens(
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost
+ ) internal pure returns (uint256 excessNativeTokens) {
+ for (uint256 i; i < implicitExecutionsPre.length; i++) {
+ excessNativeTokens += implicitExecutionsPre[i].item.amount;
+ }
+ for (uint256 i; i < explicitExecutions.length; i++) {
+ ReceivedItem memory item = explicitExecutions[i].item;
+ if (item.itemType == ItemType.NATIVE) {
+ if (item.amount > excessNativeTokens) {
+ revert(
+ "ExecutionsHelper: explicit execution amount exceeds seaport balance"
+ );
+ }
+ excessNativeTokens -= item.amount;
+ }
+ }
+ for (uint256 i; i < implicitExecutionsPost.length; i++) {
+ ReceivedItem memory item = implicitExecutionsPost[i].item;
+ if (item.itemType == ItemType.NATIVE) {
+ if (item.amount > excessNativeTokens) {
+ revert(
+ "ExecutionsHelper: post execution amount exceeds seaport balance"
+ );
+ }
+ excessNativeTokens -= item.amount;
+ }
+ }
+ }
+
+ function getStandardExecutions(
+ FulfillmentDetails memory details
+ )
+ public
+ pure
+ returns (
+ Execution[] memory implicitExecutions,
+ uint256 nativeTokensReturned
+ )
+ {
+ if (details.orders.length != 1) {
+ revert("ExecutionHelper: bad orderDetails length for standard");
+ }
+
+ return
+ getStandardExecutions(
+ details.orders[0],
+ details.fulfiller,
+ details.fulfillerConduitKey,
+ details.recipient,
+ details.nativeTokensSupplied,
+ details.seaport
+ );
+ }
+
+ /**
+ * @dev Return executions for fulfilOrder and fulfillAdvancedOrder.
+ */
+ function getStandardExecutions(
+ OrderDetails memory orderDetails,
+ address fulfiller,
+ bytes32 fulfillerConduitKey,
+ address recipient,
+ uint256 nativeTokensSupplied,
+ address seaport
+ )
+ public
+ pure
+ returns (
+ Execution[] memory implicitExecutions,
+ uint256 nativeTokensReturned
+ )
+ {
+ uint256 currentSeaportBalance = 0;
+
+ // implicit executions (use max and resize at end):
+ // - supplying native tokens (0-1)
+ // - providing native tokens from contract offerer (0-offer.length)
+ // - transferring offer items to recipient (offer.length)
+ // - transferring consideration items to each recipient (consideration.length)
+ // - returning unspent native tokens (0-1)
+ implicitExecutions = new Execution[](
+ 2 *
+ orderDetails.offer.length +
+ orderDetails.consideration.length +
+ 2
+ );
+
+ uint256 executionIndex = 0;
+
+ if (nativeTokensSupplied > 0) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: fulfiller,
+ conduitKey: fulfillerConduitKey,
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: uint256(0),
+ amount: nativeTokensSupplied,
+ recipient: payable(seaport)
+ })
+ });
+ currentSeaportBalance += nativeTokensSupplied;
+ }
+
+ if (orderDetails.isContract) {
+ for (uint256 i = 0; i < orderDetails.offer.length; i++) {
+ SpentItem memory item = orderDetails.offer[i];
+ if (item.itemType == ItemType.NATIVE) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: orderDetails.offerer,
+ conduitKey: orderDetails.conduitKey,
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: uint256(0),
+ amount: orderDetails.offer[i].amount,
+ recipient: payable(seaport)
+ })
+ });
+ currentSeaportBalance += orderDetails.offer[i].amount;
+ }
+ }
+ }
+
+ for (uint256 i = 0; i < orderDetails.offer.length; i++) {
+ SpentItem memory item = orderDetails.offer[i];
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: item.itemType == ItemType.NATIVE
+ ? seaport
+ : orderDetails.offerer,
+ conduitKey: orderDetails.conduitKey,
+ item: ReceivedItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifier,
+ amount: item.amount,
+ recipient: payable(recipient)
+ })
+ });
+ if (item.itemType == ItemType.NATIVE) {
+ if (item.amount > currentSeaportBalance) {
+ revert(
+ "ExecutionHelper: offer item amount exceeds seaport balance"
+ );
+ }
+
+ currentSeaportBalance -= item.amount;
+ }
+ }
+
+ for (uint256 i = 0; i < orderDetails.consideration.length; i++) {
+ ReceivedItem memory item = orderDetails.consideration[i];
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: item.itemType == ItemType.NATIVE ? seaport : fulfiller,
+ conduitKey: fulfillerConduitKey,
+ item: item
+ });
+ if (item.itemType == ItemType.NATIVE) {
+ if (item.amount > currentSeaportBalance) {
+ revert(
+ "ExecutionHelper: consideration item amount exceeds seaport balance"
+ );
+ }
+
+ currentSeaportBalance -= item.amount;
+ }
+ }
+
+ nativeTokensReturned = currentSeaportBalance;
+
+ if (currentSeaportBalance > 0) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: seaport,
+ conduitKey: bytes32(0),
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: currentSeaportBalance,
+ recipient: payable(fulfiller)
+ })
+ });
+ }
+
+ // Set actual length of the implicit executions array.
+ assembly {
+ mstore(implicitExecutions, executionIndex)
+ }
+ }
+
+ function getBasicExecutions(
+ FulfillmentDetails memory details
+ )
+ public
+ pure
+ returns (
+ Execution[] memory implicitExecutions,
+ uint256 nativeTokensReturned
+ )
+ {
+ if (details.orders.length != 1) {
+ revert("ExecutionHelper: bad orderDetails length for basic");
+ }
+
+ return
+ getBasicExecutions(
+ details.orders[0],
+ details.fulfiller,
+ details.fulfillerConduitKey,
+ details.nativeTokensSupplied,
+ details.seaport
+ );
+ }
+
+ /**
+ * @dev return executions for fulfillBasicOrder and
+ * fulfillBasicOrderEfficient.
+ */
+ function getBasicExecutions(
+ OrderDetails memory orderDetails,
+ address fulfiller,
+ bytes32 fulfillerConduitKey,
+ uint256 nativeTokensSupplied,
+ address seaport
+ )
+ public
+ pure
+ returns (
+ Execution[] memory implicitExecutions,
+ uint256 nativeTokensReturned
+ )
+ {
+ if (orderDetails.offer.length != 1) {
+ revert("not a basic order");
+ }
+
+ if (orderDetails.offer[0].itemType == ItemType.ERC20) {
+ require(nativeTokensSupplied == 0, "native tokens not allowed");
+ require(orderDetails.consideration.length > 0, "no items received");
+
+ implicitExecutions = new Execution[](
+ 1 + orderDetails.consideration.length
+ );
+
+ implicitExecutions[0] = Execution({
+ offerer: fulfiller,
+ conduitKey: fulfillerConduitKey,
+ item: orderDetails.consideration[0]
+ });
+
+ uint256 additionalAmounts = 0;
+
+ for (uint256 i = 1; i < orderDetails.consideration.length; i++) {
+ implicitExecutions[i] = Execution({
+ offerer: orderDetails.offerer,
+ conduitKey: orderDetails.conduitKey,
+ item: orderDetails.consideration[i]
+ });
+ additionalAmounts += orderDetails.consideration[i].amount;
+ }
+
+ implicitExecutions[orderDetails.consideration.length] = Execution({
+ offerer: orderDetails.offerer,
+ conduitKey: orderDetails.conduitKey,
+ item: ReceivedItem({
+ itemType: orderDetails.offer[0].itemType,
+ token: orderDetails.offer[0].token,
+ identifier: orderDetails.offer[0].identifier,
+ amount: orderDetails.offer[0].amount - additionalAmounts,
+ recipient: payable(fulfiller)
+ })
+ });
+ } else {
+ uint256 currentSeaportBalance = 0;
+
+ // implicit executions (use max and resize at end):
+ // - supplying native tokens (0-1)
+ // - transferring offer item to recipient (1)
+ // - transfer additional recipient consideration items (consideration.length - 1)
+ // - transfer first consideration item to the fulfiller (1)
+ // - returning unspent native tokens (0-1)
+ implicitExecutions = new Execution[](
+ orderDetails.consideration.length + 3
+ );
+
+ uint256 executionIndex = 0;
+
+ if (nativeTokensSupplied > 0) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: fulfiller,
+ conduitKey: fulfillerConduitKey,
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: uint256(0),
+ amount: nativeTokensSupplied,
+ recipient: payable(seaport)
+ })
+ });
+ currentSeaportBalance += nativeTokensSupplied;
+ }
+
+ {
+ if (orderDetails.offer.length != 1) {
+ revert("ExecutionHelper: wrong length for basic offer");
+ }
+ SpentItem memory offerItem = orderDetails.offer[0];
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: orderDetails.offerer,
+ conduitKey: orderDetails.conduitKey,
+ item: ReceivedItem({
+ itemType: offerItem.itemType,
+ token: offerItem.token,
+ identifier: offerItem.identifier,
+ amount: offerItem.amount,
+ recipient: payable(fulfiller)
+ })
+ });
+ }
+
+ for (uint256 i = 1; i < orderDetails.consideration.length; i++) {
+ ReceivedItem memory item = orderDetails.consideration[i];
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: item.itemType == ItemType.NATIVE
+ ? seaport
+ : fulfiller,
+ conduitKey: fulfillerConduitKey,
+ item: item
+ });
+ if (item.itemType == ItemType.NATIVE) {
+ if (item.amount > currentSeaportBalance) {
+ revert(
+ "ExecutionHelper: basic consideration item amount exceeds seaport balance"
+ );
+ }
+
+ currentSeaportBalance -= item.amount;
+ }
+ }
+
+ {
+ if (orderDetails.consideration.length < 1) {
+ revert(
+ "ExecutionHelper: wrong length for basic consideration"
+ );
+ }
+ ReceivedItem memory item = orderDetails.consideration[0];
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: item.itemType == ItemType.NATIVE
+ ? seaport
+ : fulfiller,
+ conduitKey: fulfillerConduitKey,
+ item: item
+ });
+ if (item.itemType == ItemType.NATIVE) {
+ if (item.amount > currentSeaportBalance) {
+ revert(
+ "ExecutionHelper: first basic consideration item amount exceeds seaport balance"
+ );
+ }
+
+ currentSeaportBalance -= item.amount;
+ }
+ }
+
+ nativeTokensReturned = currentSeaportBalance;
+
+ if (currentSeaportBalance > 0) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: seaport,
+ conduitKey: bytes32(0),
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: currentSeaportBalance,
+ recipient: payable(fulfiller)
+ })
+ });
+ }
+
+ // Set actual length of the implicit executions array.
+ assembly {
+ mstore(implicitExecutions, executionIndex)
+ }
+ }
+ }
+
+ /**
+ * @dev Get the item and recipient for a given fulfillment component.
+ *
+ * @param fulfillmentDetails The order fulfillment details
+ * @param offerRecipient The offer recipient
+ * @param component The fulfillment component
+ * @param side The side of the order
+ *
+ * @return item The item
+ * @return trueRecipient The actual recipient
+ */
+ function getItemAndRecipient(
+ FulfillmentDetails memory fulfillmentDetails,
+ address payable offerRecipient,
+ FulfillmentComponent memory component,
+ Side side
+ )
+ internal
+ pure
+ returns (SpentItem memory item, address payable trueRecipient)
+ {
+ OrderDetails memory details = fulfillmentDetails.orders[
+ component.orderIndex
+ ];
+
+ if (side == Side.OFFER) {
+ item = details.offer[component.itemIndex];
+ trueRecipient = offerRecipient;
+ } else {
+ ReceivedItem memory _item = details.consideration[
+ component.itemIndex
+ ];
+ // cast to SpentItem
+ assembly {
+ item := _item
+ }
+ trueRecipient = _item.recipient;
+ }
+ }
+
+ /**
+ * @dev Process the aggregated fulfillment components for a given side of an
+ * order.
+ *
+ * @param fulfillmentDetails The order fulfillment details
+ * @param offerRecipient The recipient for any offer items. Note: may
+ * not be FulfillmentDetails' recipient, eg,
+ * when processing matchOrders fulfillments
+ * @param aggregatedComponents The aggregated fulfillment components
+ * @param side The side of the order
+ *
+ * @return The execution
+ */
+ function processExecutionFromAggregatedFulfillmentComponents(
+ FulfillmentDetails memory fulfillmentDetails,
+ address payable offerRecipient,
+ FulfillmentComponent[] memory aggregatedComponents,
+ Side side
+ ) internal pure returns (Execution memory) {
+ // aggregate the amounts of each item
+ uint256 aggregatedAmount;
+ for (uint256 j = 0; j < aggregatedComponents.length; j++) {
+ (SpentItem memory item, ) = getItemAndRecipient(
+ fulfillmentDetails,
+ offerRecipient,
+ aggregatedComponents[j],
+ side
+ );
+ aggregatedAmount += item.amount;
+ }
+
+ // use the first fulfillment component to get the order details
+ FulfillmentComponent memory first = aggregatedComponents[0];
+ (
+ SpentItem memory firstItem,
+ address payable trueRecipient
+ ) = getItemAndRecipient(
+ fulfillmentDetails,
+ offerRecipient,
+ first,
+ side
+ );
+ OrderDetails memory details = fulfillmentDetails.orders[
+ first.orderIndex
+ ];
+
+ return
+ Execution({
+ offerer: side == Side.OFFER
+ ? details.offerer
+ : fulfillmentDetails.fulfiller,
+ conduitKey: side == Side.OFFER
+ ? details.conduitKey
+ : fulfillmentDetails.fulfillerConduitKey,
+ item: ReceivedItem({
+ itemType: firstItem.itemType,
+ token: firstItem.token,
+ identifier: firstItem.identifier,
+ amount: aggregatedAmount,
+ recipient: trueRecipient
+ })
+ });
+ }
+
+ /**
+ * @dev Process explicit executions from 2d aggregated fulfillAvailable
+ * fulfillment components arrays. Note that amounts on OrderDetails are
+ * modified in-place during fulfillment processing.
+ *
+ * @param fulfillmentDetails The fulfillment details
+ * @param offerComponents The offer components
+ * @param considerationComponents The consideration components
+ *
+ * @return explicitExecutions The explicit executions
+ */
+ function processExplicitExecutionsFromAggregatedComponents(
+ FulfillmentDetails memory fulfillmentDetails,
+ FulfillmentComponent[][] memory offerComponents,
+ FulfillmentComponent[][] memory considerationComponents,
+ bool[] memory availableOrders
+ ) internal pure returns (Execution[] memory explicitExecutions) {
+ explicitExecutions = new Execution[](
+ offerComponents.length + considerationComponents.length
+ );
+
+ uint256 filteredExecutions = 0;
+
+ // process offer components
+ // iterate over each array of fulfillment components
+ for (uint256 i = 0; i < offerComponents.length; i++) {
+ FulfillmentComponent[]
+ memory aggregatedComponents = offerComponents[i];
+
+ // aggregate & zero-out the amounts of each offer item
+ uint256 aggregatedAmount;
+ for (uint256 j = 0; j < aggregatedComponents.length; j++) {
+ FulfillmentComponent memory component = aggregatedComponents[j];
+
+ if (!availableOrders[component.orderIndex]) {
+ continue;
+ }
+
+ OrderDetails memory offerOrderDetails = fulfillmentDetails
+ .orders[component.orderIndex];
+
+ if (component.itemIndex < offerOrderDetails.offer.length) {
+ SpentItem memory item = offerOrderDetails.offer[
+ component.itemIndex
+ ];
+
+ aggregatedAmount += item.amount;
+
+ item.amount = 0;
+ }
+ }
+
+ if (aggregatedAmount == 0) {
+ filteredExecutions++;
+ continue;
+ }
+
+ // use the first fulfillment component to get the order details
+ FulfillmentComponent memory first = aggregatedComponents[0];
+ OrderDetails memory details = fulfillmentDetails.orders[
+ first.orderIndex
+ ];
+ SpentItem memory firstItem = details.offer[first.itemIndex];
+
+ if (
+ fulfillmentDetails.recipient == details.offerer &&
+ firstItem.itemType != ItemType.NATIVE
+ ) {
+ filteredExecutions++;
+ } else {
+ explicitExecutions[i - filteredExecutions] = Execution({
+ offerer: details.offerer,
+ conduitKey: details.conduitKey,
+ item: ReceivedItem({
+ itemType: firstItem.itemType,
+ token: firstItem.token,
+ identifier: firstItem.identifier,
+ amount: aggregatedAmount,
+ recipient: fulfillmentDetails.recipient
+ })
+ });
+ }
+ }
+
+ // process consideration components
+ // iterate over each array of fulfillment components
+ for (uint256 i; i < considerationComponents.length; i++) {
+ FulfillmentComponent[]
+ memory aggregatedComponents = considerationComponents[i];
+
+ // aggregate & zero-out the amounts of each offer item
+ uint256 aggregatedAmount;
+ for (uint256 j = 0; j < aggregatedComponents.length; j++) {
+ FulfillmentComponent memory component = aggregatedComponents[j];
+
+ if (!availableOrders[component.orderIndex]) {
+ continue;
+ }
+
+ OrderDetails
+ memory considerationOrderDetails = fulfillmentDetails
+ .orders[component.orderIndex];
+
+ if (
+ component.itemIndex <
+ considerationOrderDetails.consideration.length
+ ) {
+ ReceivedItem memory item = considerationOrderDetails
+ .consideration[component.itemIndex];
+
+ aggregatedAmount += item.amount;
+
+ item.amount = 0;
+ }
+ }
+
+ if (aggregatedAmount == 0) {
+ filteredExecutions++;
+ continue;
+ }
+
+ // use the first fulfillment component to get the order details
+ FulfillmentComponent memory first = aggregatedComponents[0];
+ OrderDetails memory details = fulfillmentDetails.orders[
+ first.orderIndex
+ ];
+ ReceivedItem memory firstItem = details.consideration[
+ first.itemIndex
+ ];
+
+ if (
+ firstItem.recipient == fulfillmentDetails.fulfiller &&
+ firstItem.itemType != ItemType.NATIVE
+ ) {
+ filteredExecutions++;
+ } else {
+ explicitExecutions[
+ i + offerComponents.length - filteredExecutions
+ ] = Execution({
+ offerer: fulfillmentDetails.fulfiller,
+ conduitKey: fulfillmentDetails.fulfillerConduitKey,
+ item: ReceivedItem({
+ itemType: firstItem.itemType,
+ token: firstItem.token,
+ identifier: firstItem.identifier,
+ amount: aggregatedAmount,
+ recipient: firstItem.recipient
+ })
+ });
+ }
+ }
+
+ // If some number of executions have been filtered...
+ if (filteredExecutions != 0) {
+ // reduce the total length of the executions array.
+ assembly {
+ mstore(
+ explicitExecutions,
+ sub(mload(explicitExecutions), filteredExecutions)
+ )
+ }
+ }
+ }
+
+ /**
+ * @dev Process an array of *sorted* fulfillment components into an array of
+ * executions. Note that components must be sorted.
+ *
+ * @param orderDetails The order details
+ * @param components The fulfillment components
+ * @param recipient The recipient of implicit executions
+ *
+ * @return executions The executions
+ */
+ function processExecutionsFromIndividualOfferFulfillmentComponents(
+ OrderDetails[] memory orderDetails,
+ address payable recipient,
+ FulfillmentComponent[] memory components
+ ) internal pure returns (Execution[] memory executions) {
+ executions = new Execution[](components.length);
+
+ for (uint256 i = 0; i < components.length; i++) {
+ FulfillmentComponent memory component = components[i];
+ OrderDetails memory details = orderDetails[component.orderIndex];
+ SpentItem memory item = details.offer[component.itemIndex];
+ executions[i] = Execution({
+ offerer: details.offerer,
+ conduitKey: details.conduitKey,
+ item: ReceivedItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifier,
+ amount: item.amount,
+ recipient: recipient
+ })
+ });
+ }
+ }
+
+ /**
+ * @dev Generate implicit Executions for a set of orders by getting all
+ * offer items that are not fully spent as part of a fulfillment.
+ *
+ * @param fulfillmentDetails fulfillment details
+ *
+ * @return implicitExecutions The implicit executions
+ */
+ function processImplicitPreOrderExecutions(
+ FulfillmentDetails memory fulfillmentDetails,
+ bool[] memory availableOrders
+ ) internal pure returns (Execution[] memory implicitExecutions) {
+ // Get the maximum possible number of implicit executions.
+ uint256 maxPossible = 1;
+ for (uint256 i = 0; i < fulfillmentDetails.orders.length; ++i) {
+ if (availableOrders[i]) {
+ maxPossible += fulfillmentDetails.orders[i].offer.length;
+ }
+ }
+ implicitExecutions = new Execution[](maxPossible);
+
+ uint256 executionIndex;
+ if (fulfillmentDetails.nativeTokensSupplied > 0) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: fulfillmentDetails.fulfiller,
+ conduitKey: bytes32(0),
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: uint256(0),
+ amount: fulfillmentDetails.nativeTokensSupplied,
+ recipient: payable(fulfillmentDetails.seaport)
+ })
+ });
+ }
+
+ for (uint256 o; o < fulfillmentDetails.orders.length; o++) {
+ OrderDetails memory orderDetails = fulfillmentDetails.orders[o];
+ if (!availableOrders[o]) {
+ continue;
+ }
+
+ if (orderDetails.isContract) {
+ for (uint256 i = 0; i < orderDetails.offer.length; i++) {
+ SpentItem memory item = orderDetails.offer[i];
+ if (item.itemType == ItemType.NATIVE) {
+ implicitExecutions[executionIndex++] = Execution({
+ offerer: orderDetails.offerer,
+ conduitKey: bytes32(0),
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: uint256(0),
+ amount: item.amount,
+ recipient: payable(fulfillmentDetails.seaport)
+ })
+ });
+ }
+ }
+ }
+ }
+
+ // Set the final length of the implicit executions array.
+ // Leave space for possible excess native token return.
+ assembly {
+ mstore(implicitExecutions, executionIndex)
+ }
+ }
+
+ /**
+ * @dev Generate implicit Executions for a set of orders by getting all
+ * offer items that are not fully spent as part of a fulfillment.
+ *
+ * @param fulfillmentDetails fulfillment details
+ *
+ * @return implicitExecutions The implicit executions
+ */
+ function processImplicitPostOrderExecutions(
+ FulfillmentDetails memory fulfillmentDetails,
+ bool[] memory availableOrders
+ ) internal pure returns (Execution[] memory implicitExecutions) {
+ OrderDetails[] memory orderDetails = fulfillmentDetails.orders;
+
+ // Get the maximum possible number of implicit executions.
+ uint256 maxPossible = 1;
+ for (uint256 i = 0; i < orderDetails.length; ++i) {
+ if (availableOrders[i]) {
+ maxPossible += orderDetails[i].offer.length;
+ }
+ }
+
+ // Insert an implicit execution for each non-zero offer item.
+ implicitExecutions = new Execution[](maxPossible);
+ uint256 insertionIndex = 0;
+ for (uint256 i = 0; i < orderDetails.length; ++i) {
+ if (!availableOrders[i]) {
+ continue;
+ }
+
+ OrderDetails memory details = orderDetails[i];
+ for (uint256 j; j < details.offer.length; ++j) {
+ SpentItem memory item = details.offer[j];
+ if (item.amount != 0) {
+ // Insert the item and increment insertion index.
+ implicitExecutions[insertionIndex++] = Execution({
+ offerer: item.itemType == ItemType.NATIVE
+ ? fulfillmentDetails.seaport
+ : details.offerer,
+ conduitKey: details.conduitKey,
+ item: ReceivedItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifier,
+ amount: item.amount,
+ recipient: fulfillmentDetails.recipient
+ })
+ });
+ }
+ }
+ }
+
+ // Set the final length of the implicit executions array.
+ // Leave space for possible excess native token return.
+ assembly {
+ mstore(implicitExecutions, add(insertionIndex, 1))
+ }
+ }
+
+ /**
+ * @dev Process a Fulfillment into an Execution
+ *
+ * @param fulfillmentDetails fulfillment details
+ * @param fulfillment A Fulfillment.
+ *
+ * @return An Execution.
+ */
+ function processExecutionFromFulfillment(
+ FulfillmentDetails memory fulfillmentDetails,
+ Fulfillment memory fulfillment
+ ) internal pure returns (Execution memory) {
+ // aggregate & zero-out the amounts of each offer item
+ uint256 aggregatedOfferAmount;
+ for (uint256 j = 0; j < fulfillment.offerComponents.length; j++) {
+ FulfillmentComponent memory component = fulfillment.offerComponents[
+ j
+ ];
+
+ OrderDetails memory details = fulfillmentDetails.orders[
+ component.orderIndex
+ ];
+
+ if (component.itemIndex < details.offer.length) {
+ SpentItem memory offerSpentItem = details.offer[
+ component.itemIndex
+ ];
+
+ aggregatedOfferAmount += offerSpentItem.amount;
+
+ offerSpentItem.amount = 0;
+ }
+ }
+
+ // aggregate & zero-out the amounts of each offer item
+ uint256 aggregatedConsiderationAmount;
+ for (
+ uint256 j = 0;
+ j < fulfillment.considerationComponents.length;
+ j++
+ ) {
+ FulfillmentComponent memory component = fulfillment
+ .considerationComponents[j];
+
+ OrderDetails memory details = fulfillmentDetails.orders[
+ component.orderIndex
+ ];
+
+ if (component.itemIndex < details.consideration.length) {
+ ReceivedItem memory considerationSpentItem = details
+ .consideration[component.itemIndex];
+
+ aggregatedConsiderationAmount += considerationSpentItem.amount;
+
+ considerationSpentItem.amount = 0;
+ }
+ }
+
+ // Get the first item on each side
+ FulfillmentComponent memory firstOfferComponent = fulfillment
+ .offerComponents[0];
+ OrderDetails memory sourceOrder = fulfillmentDetails.orders[
+ firstOfferComponent.orderIndex
+ ];
+
+ FulfillmentComponent memory firstConsiderationComponent = fulfillment
+ .considerationComponents[0];
+ ReceivedItem memory item = fulfillmentDetails
+ .orders[firstConsiderationComponent.orderIndex]
+ .consideration[firstConsiderationComponent.itemIndex];
+
+ // put back any extra (TODO: put it on first *in-range* item)
+ uint256 amount = aggregatedOfferAmount;
+ if (aggregatedOfferAmount > aggregatedConsiderationAmount) {
+ sourceOrder
+ .offer[firstOfferComponent.itemIndex]
+ .amount += (aggregatedOfferAmount -
+ aggregatedConsiderationAmount);
+ amount = aggregatedConsiderationAmount;
+ } else if (aggregatedOfferAmount < aggregatedConsiderationAmount) {
+ item.amount += (aggregatedConsiderationAmount -
+ aggregatedOfferAmount);
+ }
+
+ return
+ Execution({
+ offerer: sourceOrder.offerer,
+ conduitKey: sourceOrder.conduitKey,
+ item: ReceivedItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifier,
+ amount: amount,
+ recipient: item.recipient
+ })
+ });
+ }
+
+ /**
+ * @dev Process excess native tokens. If there are excess native tokens,
+ * insert an implicit execution at the end of the implicitExecutions
+ * array. If not, reduce the length of the implicitExecutions array.
+ *
+ * @param fulfillmentDetails fulfillment details
+ * @param explicitExecutions explicit executions
+ * @param implicitExecutionsPre implicit executions
+ * @param implicitExecutionsPost implicit executions
+ */
+ function _handleExcessNativeTokens(
+ FulfillmentDetails memory fulfillmentDetails,
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost
+ ) internal pure returns (uint256 excessNativeTokens) {
+ excessNativeTokens = processExcessNativeTokens(
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost
+ );
+
+ if (excessNativeTokens > 0) {
+ implicitExecutionsPost[
+ implicitExecutionsPost.length - 1
+ ] = Execution({
+ offerer: fulfillmentDetails.seaport,
+ conduitKey: bytes32(0),
+ item: ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: excessNativeTokens,
+ recipient: fulfillmentDetails.fulfiller
+ })
+ });
+ } else {
+ // Reduce length of the implicit executions array by one.
+ assembly {
+ mstore(
+ implicitExecutionsPost,
+ sub(mload(implicitExecutionsPost), 1)
+ )
+ }
+ }
+ }
+
+ function copy(
+ OrderDetails[] memory orderDetails
+ ) internal pure returns (OrderDetails[] memory copiedOrderDetails) {
+ copiedOrderDetails = new OrderDetails[](orderDetails.length);
+ for (uint256 i = 0; i < orderDetails.length; ++i) {
+ OrderDetails memory order = orderDetails[i];
+
+ copiedOrderDetails[i] = OrderDetails({
+ offerer: order.offerer,
+ conduitKey: order.conduitKey,
+ offer: order.offer.copy(),
+ consideration: order.consideration.copy(),
+ isContract: order.isContract,
+ orderHash: order.orderHash,
+ unavailableReason: order.unavailableReason
+ });
+ }
+ }
+
+ function copy(
+ FulfillmentDetails memory fulfillmentDetails
+ ) internal pure returns (FulfillmentDetails memory) {
+ return
+ FulfillmentDetails({
+ orders: copy(fulfillmentDetails.orders),
+ recipient: fulfillmentDetails.recipient,
+ fulfiller: fulfillmentDetails.fulfiller,
+ nativeTokensSupplied: fulfillmentDetails.nativeTokensSupplied,
+ fulfillerConduitKey: fulfillmentDetails.fulfillerConduitKey,
+ seaport: fulfillmentDetails.seaport
+ });
+ }
+}
diff --git a/contracts/helpers/sol/executions/FulfillmentComponentSet.sol b/contracts/helpers/sol/executions/FulfillmentComponentSet.sol
new file mode 100644
index 000000000..0bc9e7901
--- /dev/null
+++ b/contracts/helpers/sol/executions/FulfillmentComponentSet.sol
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { FulfillmentComponent } from "../SeaportStructs.sol";
+
+struct FulfillmentComponentSet {
+ mapping(bytes32 => uint256) offByOneIndex;
+ FulfillmentComponent[] enumeration;
+}
+
+library FulfillmentComponentSetLib {
+ error NotPresent();
+
+ function add(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent memory value
+ ) internal returns (bool added) {
+ // add value to enumeration; hash it to set its entry in the offByOneIndex
+ bytes32 key = keccak256(abi.encode(value));
+ if (set.offByOneIndex[key] == 0) {
+ set.enumeration.push(value);
+ set.offByOneIndex[key] = set.enumeration.length;
+ added = true;
+ } else {
+ added = false;
+ }
+ }
+
+ // remove value from enumeration and replace it with last member of enumeration
+ // if not last member, update offByOneIndex of last member
+ function remove(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent memory value
+ ) internal returns (bool removed) {
+ bytes32 key = keccak256(abi.encode(value));
+ uint256 index = set.offByOneIndex[key];
+ if (index > 0) {
+ uint256 lastIndex = set.enumeration.length - 1;
+ FulfillmentComponent memory lastValue = set.enumeration[lastIndex];
+ set.enumeration[index - 1] = lastValue;
+ bytes32 lastKey = keccak256(abi.encode(lastValue));
+ // if lastKey is the same as key, then we are removing the last element; do not update it
+ if (lastKey != key) {
+ set.offByOneIndex[lastKey] = index;
+ }
+ set.enumeration.pop();
+ delete set.offByOneIndex[key];
+ removed = true;
+ } else {
+ removed = false;
+ }
+ }
+
+ function removeAll(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent[] memory values
+ ) internal {
+ for (uint256 i = 0; i < values.length; i++) {
+ remove(set, values[i]);
+ }
+ }
+
+ function removeAll(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent[][] memory values
+ ) internal {
+ for (uint256 i = 0; i < values.length; i++) {
+ removeAll(set, values[i]);
+ }
+ }
+
+ function contains(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent memory value
+ ) internal view returns (bool) {
+ return set.offByOneIndex[keccak256(abi.encode(value))] > 0;
+ }
+
+ function length(
+ FulfillmentComponentSet storage set
+ ) internal view returns (uint256) {
+ return set.enumeration.length;
+ }
+
+ function at(
+ FulfillmentComponentSet storage set,
+ uint256 index
+ ) internal view returns (FulfillmentComponent memory) {
+ return set.enumeration[index];
+ }
+
+ function clear(FulfillmentComponentSet storage set) internal {
+ while (set.enumeration.length > 0) {
+ FulfillmentComponent memory component = set.enumeration[
+ set.enumeration.length - 1
+ ];
+ delete set.offByOneIndex[keccak256(abi.encode(component))];
+ set.enumeration.pop();
+ }
+ }
+}
diff --git a/contracts/helpers/sol/executions/FulfillmentComponentSortLib.sol b/contracts/helpers/sol/executions/FulfillmentComponentSortLib.sol
new file mode 100644
index 000000000..8b9ef90c1
--- /dev/null
+++ b/contracts/helpers/sol/executions/FulfillmentComponentSortLib.sol
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { FulfillmentComponent } from "../SeaportStructs.sol";
+
+library FulfillmentComponentSortLib {
+ function key(
+ FulfillmentComponent memory component
+ ) internal pure returns (uint256) {
+ return (uint256(component.orderIndex) << 8) | component.itemIndex;
+ }
+
+ function sort(FulfillmentComponent[] memory components) internal pure {
+ sort(components, key);
+ }
+
+ // Sorts the array in-place with intro-quicksort.
+ function sort(
+ FulfillmentComponent[] memory a,
+ function(FulfillmentComponent memory)
+ internal
+ pure
+ returns (uint256) accessor
+ ) internal pure {
+ if (a.length < 2) {
+ return;
+ }
+
+ uint256[] memory stack = new uint256[](2 * a.length);
+ uint256 stackIndex = 0;
+
+ uint256 l = 0;
+ uint256 h = a.length - 1;
+
+ stack[stackIndex++] = l;
+ stack[stackIndex++] = h;
+
+ while (stackIndex > 0) {
+ h = stack[--stackIndex];
+ l = stack[--stackIndex];
+
+ if (h - l <= 12) {
+ // Insertion sort for small subarrays
+ for (uint256 i = l + 1; i <= h; i++) {
+ FulfillmentComponent memory k = a[i];
+ uint256 j = i;
+ while (j > l && accessor(a[j - 1]) > accessor(k)) {
+ a[j] = a[j - 1];
+ j--;
+ }
+ a[j] = k;
+ }
+ } else {
+ // Intro-Quicksort
+ uint256 p = (l + h) / 2;
+
+ // Median of 3
+ if (accessor(a[l]) > accessor(a[p])) {
+ (a[l], a[p]) = (a[p], a[l]);
+ }
+ if (accessor(a[l]) > accessor(a[h])) {
+ (a[l], a[h]) = (a[h], a[l]);
+ }
+ if (accessor(a[p]) > accessor(a[h])) {
+ (a[p], a[h]) = (a[h], a[p]);
+ }
+
+ uint256 pivot = accessor(a[p]);
+ uint256 i = l;
+ uint256 j = h;
+
+ while (i <= j) {
+ while (accessor(a[i]) < pivot) {
+ i++;
+ }
+ while (accessor(a[j]) > pivot) {
+ j--;
+ }
+ if (i <= j) {
+ (a[i], a[j]) = (a[j], a[i]);
+ i++;
+ j--;
+ }
+ }
+
+ if (j > l) {
+ stack[stackIndex++] = l;
+ stack[stackIndex++] = j;
+ }
+ if (i < h) {
+ stack[stackIndex++] = i;
+ stack[stackIndex++] = h;
+ }
+ }
+ }
+ }
+}
diff --git a/contracts/helpers/sol/executions/GenericEnumerableMapping.sol b/contracts/helpers/sol/executions/GenericEnumerableMapping.sol
new file mode 100644
index 000000000..0bc9e7901
--- /dev/null
+++ b/contracts/helpers/sol/executions/GenericEnumerableMapping.sol
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { FulfillmentComponent } from "../SeaportStructs.sol";
+
+struct FulfillmentComponentSet {
+ mapping(bytes32 => uint256) offByOneIndex;
+ FulfillmentComponent[] enumeration;
+}
+
+library FulfillmentComponentSetLib {
+ error NotPresent();
+
+ function add(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent memory value
+ ) internal returns (bool added) {
+ // add value to enumeration; hash it to set its entry in the offByOneIndex
+ bytes32 key = keccak256(abi.encode(value));
+ if (set.offByOneIndex[key] == 0) {
+ set.enumeration.push(value);
+ set.offByOneIndex[key] = set.enumeration.length;
+ added = true;
+ } else {
+ added = false;
+ }
+ }
+
+ // remove value from enumeration and replace it with last member of enumeration
+ // if not last member, update offByOneIndex of last member
+ function remove(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent memory value
+ ) internal returns (bool removed) {
+ bytes32 key = keccak256(abi.encode(value));
+ uint256 index = set.offByOneIndex[key];
+ if (index > 0) {
+ uint256 lastIndex = set.enumeration.length - 1;
+ FulfillmentComponent memory lastValue = set.enumeration[lastIndex];
+ set.enumeration[index - 1] = lastValue;
+ bytes32 lastKey = keccak256(abi.encode(lastValue));
+ // if lastKey is the same as key, then we are removing the last element; do not update it
+ if (lastKey != key) {
+ set.offByOneIndex[lastKey] = index;
+ }
+ set.enumeration.pop();
+ delete set.offByOneIndex[key];
+ removed = true;
+ } else {
+ removed = false;
+ }
+ }
+
+ function removeAll(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent[] memory values
+ ) internal {
+ for (uint256 i = 0; i < values.length; i++) {
+ remove(set, values[i]);
+ }
+ }
+
+ function removeAll(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent[][] memory values
+ ) internal {
+ for (uint256 i = 0; i < values.length; i++) {
+ removeAll(set, values[i]);
+ }
+ }
+
+ function contains(
+ FulfillmentComponentSet storage set,
+ FulfillmentComponent memory value
+ ) internal view returns (bool) {
+ return set.offByOneIndex[keccak256(abi.encode(value))] > 0;
+ }
+
+ function length(
+ FulfillmentComponentSet storage set
+ ) internal view returns (uint256) {
+ return set.enumeration.length;
+ }
+
+ function at(
+ FulfillmentComponentSet storage set,
+ uint256 index
+ ) internal view returns (FulfillmentComponent memory) {
+ return set.enumeration[index];
+ }
+
+ function clear(FulfillmentComponentSet storage set) internal {
+ while (set.enumeration.length > 0) {
+ FulfillmentComponent memory component = set.enumeration[
+ set.enumeration.length - 1
+ ];
+ delete set.offByOneIndex[keccak256(abi.encode(component))];
+ set.enumeration.pop();
+ }
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/available/FulfillAvailableHelper.sol b/contracts/helpers/sol/fulfillments/available/FulfillAvailableHelper.sol
new file mode 100644
index 000000000..f1460c0b2
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/available/FulfillAvailableHelper.sol
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import "../../SeaportStructs.sol";
+import "../../lib/SeaportArrays.sol";
+import {
+ FulfillAvailableHelperStorageLayout,
+ FulfillmentHelperCounterLayout,
+ AggregatableOffer,
+ AggregatableConsideration
+} from "../lib/Structs.sol";
+import { FulfillAvailableLayout } from "./FulfillAvailableLayout.sol";
+import {
+ FULFILL_AVAILABLE_COUNTER_KEY,
+ FULFILL_AVAILABLE_STORAGE_BASE_KEY
+} from "../lib/Constants.sol";
+import { OrderDetails } from "../lib/Structs.sol";
+
+contract FulfillAvailableHelper {
+ /**
+ * @notice get naive 2d fulfillment component arrays for
+ * fulfillAvailableOrders, one 1d array for each offer and consideration
+ * item
+ * @param orders orders
+ * @return offer
+ * @return consideration
+ */
+ function getNaiveFulfillmentComponents(
+ Order[] memory orders
+ )
+ public
+ pure
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ OrderParameters[] memory orderParameters = new OrderParameters[](
+ orders.length
+ );
+ for (uint256 i = 0; i < orders.length; i++) {
+ orderParameters[i] = orders[i].parameters;
+ }
+ return getNaiveFulfillmentComponents(orderParameters);
+ }
+
+ /**
+ * @notice get naive 2d fulfillment component arrays for
+ * fulfillAvailableOrders, one 1d array for each offer and consideration
+ * item
+ * @param orders orders
+ * @return offer
+ * @return consideration
+ */
+ function getNaiveFulfillmentComponents(
+ AdvancedOrder[] memory orders
+ )
+ public
+ pure
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ OrderParameters[] memory orderParameters = new OrderParameters[](
+ orders.length
+ );
+ for (uint256 i = 0; i < orders.length; i++) {
+ orderParameters[i] = orders[i].parameters;
+ }
+ return getNaiveFulfillmentComponents(orderParameters);
+ }
+
+ /**
+ * @notice get naive 2d fulfillment component arrays for
+ * fulfillAvailableOrders, one 1d array for each offer and consideration
+ * item
+ * @param orderParameters orderParameters
+ * @return offer
+ * @return consideration
+ */
+ function getNaiveFulfillmentComponents(
+ OrderParameters[] memory orderParameters
+ )
+ public
+ pure
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ {
+ // get total number of offer items and consideration items
+ uint256 numOffers;
+ uint256 numConsiderations;
+ for (uint256 i = 0; i < orderParameters.length; i++) {
+ OrderParameters memory parameters = orderParameters[i];
+
+ numOffers += parameters.offer.length;
+ numConsiderations += parameters.consideration.length;
+ }
+
+ // create arrays
+ offer = new FulfillmentComponent[][](numOffers);
+ consideration = new FulfillmentComponent[][](numConsiderations);
+ }
+
+ uint256 offerIndex;
+ uint256 considerationIndex;
+ // iterate over orders again, creating one one-element array per offer and consideration item
+ for (uint256 i = 0; i < orderParameters.length; i++) {
+ OrderParameters memory parameters = orderParameters[i];
+ for (uint256 j; j < parameters.offer.length; j++) {
+ offer[offerIndex] = SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: i, itemIndex: j })
+ );
+ ++offerIndex;
+ }
+ // do the same for consideration
+ for (uint256 j; j < parameters.consideration.length; j++) {
+ consideration[considerationIndex] = SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: i, itemIndex: j })
+ );
+ ++considerationIndex;
+ }
+ }
+ return (offer, consideration);
+ }
+
+ /**
+ * @notice get naive 2d fulfillment component arrays for
+ * fulfillAvailableOrders, one 1d array for each offer and consideration
+ * item
+ * @param orders OrderDetails[]
+ * @return offer
+ * @return consideration
+ */
+ function getNaiveFulfillmentComponents(
+ OrderDetails[] memory orders
+ )
+ public
+ pure
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ {
+ // get total number of offer items and consideration items
+ uint256 numOffers;
+ uint256 numConsiderations;
+ for (uint256 i = 0; i < orders.length; i++) {
+ OrderDetails memory order = orders[i];
+
+ numOffers += order.offer.length;
+ numConsiderations += order.consideration.length;
+ }
+
+ // create arrays
+ offer = new FulfillmentComponent[][](numOffers);
+ consideration = new FulfillmentComponent[][](numConsiderations);
+ }
+
+ uint256 offerIndex;
+ uint256 considerationIndex;
+ // iterate over orders again, creating one one-element array per offer and consideration item
+ for (uint256 i = 0; i < orders.length; i++) {
+ OrderDetails memory order = orders[i];
+ for (uint256 j; j < order.offer.length; j++) {
+ offer[offerIndex] = SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: i, itemIndex: j })
+ );
+ ++offerIndex;
+ }
+ // do the same for consideration
+ for (uint256 j; j < order.consideration.length; j++) {
+ consideration[considerationIndex] = SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: i, itemIndex: j })
+ );
+ ++considerationIndex;
+ }
+ }
+ return (offer, consideration);
+ }
+
+ /**
+ * @notice Get aggregated fulfillment components for aggregatable types from the same offerer or to the same recipient
+ * NOTE: this will break for multiple criteria items that resolve
+ * to different identifiers
+ * @param orders orders
+ * @return offer
+ * @return consideration
+ */
+ function getAggregatedFulfillmentComponents(
+ Order[] memory orders
+ )
+ public
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ OrderParameters[] memory orderParameters = new OrderParameters[](
+ orders.length
+ );
+ for (uint256 i = 0; i < orders.length; i++) {
+ orderParameters[i] = orders[i].parameters;
+ }
+ return getAggregatedFulfillmentComponents(orderParameters);
+ }
+
+ /**
+ * @notice Get aggregated fulfillment components for aggregatable types from the same offerer or to the same recipient
+ * NOTE: this will break for multiple criteria items that resolve
+ * to different identifiers
+ * @param orders orders
+ * @return offer
+ * @return consideration
+ */
+ function getAggregatedFulfillmentComponents(
+ AdvancedOrder[] memory orders
+ )
+ public
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ OrderParameters[] memory orderParameters = new OrderParameters[](
+ orders.length
+ );
+ for (uint256 i = 0; i < orders.length; i++) {
+ orderParameters[i] = orders[i].parameters;
+ }
+ return getAggregatedFulfillmentComponents(orderParameters);
+ }
+
+ /**
+ * @notice Get aggregated fulfillment components for aggregatable types from the same offerer or to the same recipient
+ * NOTE: this will break for multiple criteria items that resolve
+ * to different identifiers
+ * @param orders orders
+ * @return offer
+ * @return consideration
+ */
+ function getAggregatedFulfillmentComponents(
+ OrderParameters[] memory orders
+ )
+ public
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ )
+ {
+ // increment counter to get clean mappings and enumeration
+ FulfillAvailableLayout.incrementFulfillmentCounter();
+ FulfillAvailableHelperStorageLayout
+ storage layout = FulfillAvailableLayout.getStorageLayout();
+
+ // iterate over each order
+ for (uint256 i; i < orders.length; ++i) {
+ OrderParameters memory parameters = orders[i];
+ preProcessOffer(
+ parameters.offer,
+ parameters.offerer,
+ parameters.conduitKey,
+ i,
+ layout
+ );
+ preProcessConsideration(parameters.consideration, i, layout);
+ }
+
+ // allocate offer arrays
+ offer = new FulfillmentComponent[][](layout.offerEnumeration.length);
+ // iterate over enumerated groupings and add to array
+ for (uint256 i; i < layout.offerEnumeration.length; ++i) {
+ AggregatableOffer memory token = layout.offerEnumeration[i];
+
+ offer[i] = layout.offerMap[token.contractAddress][token.tokenId][
+ token.offerer
+ ][token.conduitKey];
+ }
+ // do the same for considerations
+ consideration = new FulfillmentComponent[][](
+ layout.considerationEnumeration.length
+ );
+ for (uint256 i; i < layout.considerationEnumeration.length; ++i) {
+ AggregatableConsideration memory token = layout
+ .considerationEnumeration[i];
+ consideration[i] = layout.considerationMap[token.recipient][
+ token.contractAddress
+ ][token.tokenId];
+ }
+ return (offer, consideration);
+ }
+
+ function extend(
+ FulfillmentComponent[][] memory array,
+ FulfillmentComponent[] memory toAdd
+ ) internal pure returns (FulfillmentComponent[][] memory extended) {
+ extended = new FulfillmentComponent[][](array.length + 1);
+ for (uint256 i = 0; i < array.length; i++) {
+ extended[i] = array[i];
+ }
+ extended[array.length] = toAdd;
+ }
+
+ /**
+ * @notice Process offer items and insert them into enumeration and map
+ * @param offer offer items
+ * @param offerer offerer
+ * @param orderIndex order index of processed items
+ * @param layout layout
+ */
+ function preProcessOffer(
+ OfferItem[] memory offer,
+ address offerer,
+ bytes32 conduitKey,
+ uint256 orderIndex,
+ FulfillAvailableHelperStorageLayout storage layout
+ ) private {
+ // iterate over each offer item
+ for (uint256 j; j < offer.length; ++j) {
+ // create the fulfillment component for this offer item
+ FulfillmentComponent memory component = FulfillmentComponent({
+ orderIndex: orderIndex,
+ itemIndex: j
+ });
+ // grab order parameters to get offerer
+ // grab offer item
+ OfferItem memory item = offer[j];
+ // create enumeration struct
+ AggregatableOffer memory aggregatableOffer = AggregatableOffer({
+ offerer: offerer,
+ conduitKey: conduitKey,
+ contractAddress: item.token,
+ tokenId: item.identifierOrCriteria
+ });
+ // if it does not exist in the map, add it to our enumeration
+ if (
+ !FulfillAvailableLayout.aggregatableOfferExists(
+ aggregatableOffer,
+ layout
+ )
+ ) {
+ layout.offerEnumeration.push(aggregatableOffer);
+ }
+ // update mapping with this component
+ layout
+ .offerMap[aggregatableOffer.contractAddress][
+ aggregatableOffer.tokenId
+ ][aggregatableOffer.offerer][aggregatableOffer.conduitKey].push(
+ component
+ );
+ }
+ }
+
+ /**
+ * @notice Process consideration items and insert them into enumeration and map
+ * @param consideration consideration items
+ * @param orderIndex order index of processed items
+ * @param layout layout
+ */
+ function preProcessConsideration(
+ ConsiderationItem[] memory consideration,
+ uint256 orderIndex,
+ FulfillAvailableHelperStorageLayout storage layout
+ ) private {
+ // iterate over each offer item
+ for (uint256 j; j < consideration.length; ++j) {
+ // create the fulfillment component for this offer item
+ FulfillmentComponent memory component = FulfillmentComponent({
+ orderIndex: orderIndex,
+ itemIndex: j
+ });
+ // grab consideration item
+ ConsiderationItem memory item = consideration[j];
+ // create enumeration struct
+ AggregatableConsideration memory token = AggregatableConsideration({
+ recipient: item.recipient,
+ contractAddress: item.token,
+ tokenId: item.identifierOrCriteria
+ });
+ // if it does not exist in the map, add it to our enumeration
+ if (
+ !FulfillAvailableLayout.aggregatableConsiderationExists(
+ token,
+ layout
+ )
+ ) {
+ layout.considerationEnumeration.push(token);
+ }
+ // update mapping with this component
+ layout
+ .considerationMap[token.recipient][token.contractAddress][
+ token.tokenId
+ ].push(component);
+ }
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/available/FulfillAvailableLayout.sol b/contracts/helpers/sol/fulfillments/available/FulfillAvailableLayout.sol
new file mode 100644
index 000000000..d9fa4db8c
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/available/FulfillAvailableLayout.sol
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ MatchComponent,
+ MatchComponentType
+} from "../../lib/types/MatchComponentType.sol";
+import {
+ FulfillAvailableHelperStorageLayout,
+ FulfillmentHelperCounterLayout,
+ AggregatableConsideration,
+ AggregatableOffer
+} from "../lib/Structs.sol";
+import {
+ FULFILL_AVAILABLE_COUNTER_KEY,
+ FULFILL_AVAILABLE_STORAGE_BASE_KEY
+} from "../lib/Constants.sol";
+
+library FulfillAvailableLayout {
+ /**
+ * @notice Check if a token already exists in a mapping by checking the length of the array at that slot
+ * @param token token to check
+ * @param layout storage layout
+ */
+ function aggregatableConsiderationExists(
+ AggregatableConsideration memory token,
+ FulfillAvailableHelperStorageLayout storage layout
+ ) internal view returns (bool) {
+ return
+ layout
+ .considerationMap[token.recipient][token.contractAddress][
+ token.tokenId
+ ].length > 0;
+ }
+
+ /**
+ * @notice Check if an entry into the offer component mapping already exists by checking its length
+ */
+ function aggregatableOfferExists(
+ AggregatableOffer memory offer,
+ FulfillAvailableHelperStorageLayout storage layout
+ ) internal view returns (bool) {
+ return
+ layout
+ .offerMap[offer.contractAddress][offer.tokenId][offer.offerer][
+ offer.conduitKey
+ ].length > 0;
+ }
+
+ /**
+ * @notice load storage layout for the current fulfillmentCounter
+ */
+ function getStorageLayout()
+ internal
+ view
+ returns (FulfillAvailableHelperStorageLayout storage layout)
+ {
+ FulfillmentHelperCounterLayout
+ storage counterLayout = getCounterLayout();
+ uint256 counter = counterLayout.fulfillmentCounter;
+ bytes32 storageLayoutKey = FULFILL_AVAILABLE_STORAGE_BASE_KEY;
+ assembly {
+ mstore(0, counter)
+ mstore(0x20, storageLayoutKey)
+ layout.slot := keccak256(0, 0x40)
+ }
+ }
+
+ /**
+ * @notice load storage layout for the counter itself
+ */
+ function getCounterLayout()
+ internal
+ pure
+ returns (FulfillmentHelperCounterLayout storage layout)
+ {
+ bytes32 counterLayoutKey = FULFILL_AVAILABLE_COUNTER_KEY;
+ assembly {
+ layout.slot := counterLayoutKey
+ }
+ }
+
+ /**
+ * @notice increment the fulfillmentCounter to effectively clear the mappings and enumerations between calls
+ */
+ function incrementFulfillmentCounter() internal {
+ FulfillmentHelperCounterLayout
+ storage counterLayout = getCounterLayout();
+ counterLayout.fulfillmentCounter += 1;
+ }
+
+ /**
+ * @notice Get the mapping of tokens for a given key (offer or consideration), derived from the hash of the key and the current fulfillmentCounter value
+ * @param key Original key used to derive the slot of the enumeration
+ */
+ function getMap(
+ bytes32 key
+ )
+ internal
+ view
+ returns (
+ mapping(address /*offererOrRecipient*/ => mapping(address /*tokenContract*/ => mapping(uint256 /*identifier*/ => MatchComponent[] /*components*/)))
+ storage map
+ )
+ {
+ bytes32 counterKey = FULFILL_AVAILABLE_COUNTER_KEY;
+ assembly {
+ mstore(0, key)
+ mstore(0x20, sload(counterKey))
+ map.slot := keccak256(0, 0x40)
+ }
+ }
+
+ /**
+ * @notice Get the enumeration of AggregatableConsiderations for a given key (offer or consideration), derived from the hash of the key and the current fulfillmentCounter value
+ * @param key Original key used to derive the slot of the enumeration
+ */
+ function getEnumeration(
+ bytes32 key
+ ) internal view returns (AggregatableConsideration[] storage tokens) {
+ bytes32 counterKey = FULFILL_AVAILABLE_COUNTER_KEY;
+ assembly {
+ mstore(0, key)
+ mstore(0x20, sload(counterKey))
+ tokens.slot := keccak256(0, 0x40)
+ }
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/lib/Constants.sol b/contracts/helpers/sol/fulfillments/lib/Constants.sol
new file mode 100644
index 000000000..d97eb31e7
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/lib/Constants.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+// used to effectively "wipe" the mappings and enumerations each time getAggregated is called
+bytes32 constant MATCH_FULFILLMENT_COUNTER_KEY = keccak256(
+ "MatchFulfillmentHelper.fulfillmentCounter"
+);
+
+bytes32 constant MATCH_FULFILLMENT_STORAGE_BASE_KEY = keccak256(
+ "MatchFulfillmentHelper.storageBase"
+);
+
+// used to effectively "wipe" the mappings and enumerations each time getAggregated is called
+bytes32 constant FULFILL_AVAILABLE_COUNTER_KEY = keccak256(
+ "FulfillAvailableHelper.fulfillmentCounter"
+);
+
+bytes32 constant FULFILL_AVAILABLE_STORAGE_BASE_KEY = keccak256(
+ "FulfillAvailableHelper.storageBase"
+);
diff --git a/contracts/helpers/sol/fulfillments/lib/FulfillmentLib.sol b/contracts/helpers/sol/fulfillments/lib/FulfillmentLib.sol
new file mode 100644
index 000000000..4fa28488f
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/lib/FulfillmentLib.sol
@@ -0,0 +1,2271 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import { LibSort } from "solady/src/utils/LibSort.sol";
+
+import {
+ FulfillmentComponent,
+ Fulfillment,
+ Order,
+ AdvancedOrder,
+ OrderParameters,
+ SpentItem,
+ ReceivedItem,
+ CriteriaResolver
+} from "../../SeaportStructs.sol";
+
+import { ItemType, Side } from "../../SeaportEnums.sol";
+
+import { MatchComponent, OrderDetails } from "./Structs.sol";
+
+enum FulfillmentEligibility {
+ NONE,
+ FULFILL_AVAILABLE,
+ MATCH,
+ BOTH
+}
+
+enum AggregationStrategy {
+ MINIMUM, // Aggregate as few items as possible
+ MAXIMUM, // Aggregate as many items as possible
+ RANDOM // Randomize aggregation quantity
+ // NOTE: for match cases, there may be more sophisticated optimal strategies
+}
+
+enum FulfillAvailableStrategy {
+ KEEP_ALL, // Persist default aggregation strategy
+ DROP_SINGLE_OFFER, // Exclude aggregations for single offer items
+ DROP_ALL_OFFER, // Exclude offer aggregations (keep one if no consideration)
+ DROP_RANDOM_OFFER, // Exclude random offer aggregations
+ DROP_SINGLE_KEEP_FILTERED, // Exclude single unless it would be filtered
+ DROP_ALL_KEEP_FILTERED, // Exclude all unfilterable offer aggregations
+ DROP_RANDOM_KEEP_FILTERED // Exclude random, unfilterable offer aggregations
+}
+
+enum MatchStrategy {
+ MAX_FILTERS, // prioritize locating filterable executions
+ MIN_FILTERS, // prioritize avoiding filterable executions where possible
+ MAX_INCLUSION, // try not to leave any unspent offer items
+ MIN_INCLUSION, // leave as many unspent offer items as possible
+ MIN_INCLUSION_MAX_FILTERS, // leave unspent items if not filterable
+ MAX_EXECUTIONS, // use as many fulfillments as possible given aggregations
+ MIN_EXECUTIONS, // use as few fulfillments as possible given aggregations
+ MIN_EXECUTIONS_MAX_FILTERS // minimize fulfillments and prioritize filters
+ // NOTE: more sophisticated match strategies require modifying aggregations
+}
+
+enum ItemCategory {
+ NATIVE,
+ ERC721,
+ OTHER
+}
+
+struct FulfillmentStrategy {
+ AggregationStrategy aggregationStrategy;
+ FulfillAvailableStrategy fulfillAvailableStrategy;
+ MatchStrategy matchStrategy;
+}
+
+struct FulfillmentItem {
+ uint256 orderIndex;
+ uint256 itemIndex;
+ uint256 amount;
+ address account;
+}
+
+struct FulfillmentItems {
+ ItemCategory itemCategory;
+ uint256 totalAmount;
+ FulfillmentItem[] items;
+}
+
+struct DualFulfillmentItems {
+ FulfillmentItems[] offer;
+ FulfillmentItems[] consideration;
+}
+
+struct DualFulfillmentMatchContext {
+ ItemCategory itemCategory;
+ uint256 totalOfferAmount;
+ uint256 totalConsiderationAmount;
+}
+
+struct FulfillAvailableDetails {
+ DualFulfillmentItems items;
+ address caller;
+ address recipient;
+ uint256 totalItems;
+}
+
+struct MatchDetails {
+ DualFulfillmentItems[] items;
+ DualFulfillmentMatchContext[] context;
+ address recipient;
+ uint256 totalItems;
+}
+
+library FulfillmentGeneratorLib {
+ using LibPRNG for LibPRNG.PRNG;
+ using ItemReferenceLib for OrderDetails[];
+ using FulfillmentPrepLib for ItemReferenceLib.ItemReference[];
+
+ function getDefaultFulfillmentStrategy()
+ internal
+ pure
+ returns (FulfillmentStrategy memory)
+ {
+ return
+ FulfillmentStrategy({
+ aggregationStrategy: AggregationStrategy.MAXIMUM,
+ fulfillAvailableStrategy: FulfillAvailableStrategy.KEEP_ALL,
+ matchStrategy: MatchStrategy.MAX_INCLUSION
+ });
+ }
+
+ // This uses the "default" set of strategies and applies no randomization.
+ function getFulfillments(
+ OrderDetails[] memory orderDetails,
+ address recipient,
+ address caller
+ )
+ internal
+ pure
+ returns (
+ FulfillmentEligibility eligibility,
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ uint256 seed = 0;
+
+ return
+ getFulfillments(
+ orderDetails,
+ getDefaultFulfillmentStrategy(),
+ recipient,
+ caller,
+ seed
+ );
+ }
+
+ function getFulfillments(
+ OrderDetails[] memory orderDetails,
+ FulfillmentStrategy memory strategy,
+ address recipient,
+ address caller,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ FulfillmentEligibility eligibility,
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ ItemReferenceLib.ItemReference[] memory references = orderDetails
+ .getItemReferences(seed);
+
+ (
+ FulfillAvailableDetails memory fulfillAvailableDetails,
+ MatchDetails memory matchDetails
+ ) = references.getDetails(recipient, caller);
+
+ return
+ getFulfillmentsFromDetails(
+ fulfillAvailableDetails,
+ matchDetails,
+ strategy,
+ seed
+ );
+ }
+
+ function getFulfillmentsFromDetails(
+ FulfillAvailableDetails memory fulfillAvailableDetails,
+ MatchDetails memory matchDetails,
+ FulfillmentStrategy memory strategy,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ FulfillmentEligibility eligibility,
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ assertSupportedStrategy(strategy);
+
+ (
+ fulfillments,
+ unspentOfferComponents,
+ unmetConsiderationComponents
+ ) = getMatchFulfillments(matchDetails, strategy, seed);
+
+ eligibility = determineEligibility(
+ fulfillAvailableDetails,
+ unmetConsiderationComponents.length
+ );
+
+ if (
+ eligibility == FulfillmentEligibility.FULFILL_AVAILABLE ||
+ eligibility == FulfillmentEligibility.BOTH
+ ) {
+ (
+ offerFulfillments,
+ considerationFulfillments
+ ) = getFulfillAvailableFulfillments(
+ fulfillAvailableDetails,
+ strategy,
+ seed
+ );
+ }
+ }
+
+ function assertSupportedStrategy(
+ FulfillmentStrategy memory strategy
+ ) internal pure {
+ // TODO: add more strategies here as support is added for them.
+ if (uint256(strategy.fulfillAvailableStrategy) > 3) {
+ revert(
+ "FulfillmentGeneratorLib: unsupported fulfillAvailable strategy"
+ );
+ }
+
+ MatchStrategy matchStrategy = strategy.matchStrategy;
+ if (matchStrategy != MatchStrategy.MAX_INCLUSION) {
+ revert("FulfillmentGeneratorLib: unsupported match strategy");
+ }
+ }
+
+ function determineEligibility(
+ FulfillAvailableDetails memory fulfillAvailableDetails,
+ uint256 totalUnmetConsiderationComponents
+ ) internal pure returns (FulfillmentEligibility) {
+ // FulfillAvailable: cannot be used if native offer items are present on
+ // non-contract orders or if ERC721 items with amounts != 1 are present.
+ // There must also be at least one unfiltered explicit execution. Note
+ // that it is also *very* tricky to use FulfillAvailable in cases where
+ // ERC721 items are present on both the offer side & consideration side.
+ bool eligibleForFulfillAvailable = determineFulfillAvailableEligibility(
+ fulfillAvailableDetails
+ );
+
+ // Match: cannot be used if there is no way to meet each consideration
+ // item. In these cases, remaining offer components should be returned.
+ bool eligibleForMatch = totalUnmetConsiderationComponents == 0;
+
+ if (eligibleForFulfillAvailable) {
+ return
+ eligibleForMatch
+ ? FulfillmentEligibility.BOTH
+ : FulfillmentEligibility.FULFILL_AVAILABLE;
+ }
+
+ return
+ eligibleForMatch
+ ? FulfillmentEligibility.MATCH
+ : FulfillmentEligibility.NONE;
+ }
+
+ // This uses the "default" set of strategies, applies no randomization, and
+ // does not give a recipient & will not properly detect filtered executions.
+ function getMatchedFulfillments(
+ OrderDetails[] memory orderDetails
+ )
+ internal
+ pure
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ return
+ getMatchFulfillments(
+ orderDetails.getItemReferences(0).getMatchDetailsFromReferences(
+ address(0)
+ )
+ );
+ }
+
+ // This does not give a recipient & so will not detect filtered executions.
+ function getMatchedFulfillments(
+ OrderDetails[] memory orderDetails,
+ FulfillmentStrategy memory strategy,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ return
+ getMatchFulfillments(
+ orderDetails.getItemReferences(0).getMatchDetailsFromReferences(
+ address(0)
+ ),
+ strategy,
+ seed
+ );
+ }
+
+ function getMatchDetails(
+ OrderDetails[] memory orderDetails,
+ FulfillmentStrategy memory strategy,
+ address recipient,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ return
+ getMatchFulfillments(
+ orderDetails
+ .getItemReferences(seed)
+ .getMatchDetailsFromReferences(recipient),
+ strategy,
+ seed
+ );
+ }
+
+ // This uses the "default" set of strategies and applies no randomization.
+ function getMatchFulfillments(
+ MatchDetails memory matchDetails
+ )
+ internal
+ pure
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ uint256 seed = 0;
+
+ return
+ getMatchFulfillments(
+ matchDetails,
+ getDefaultFulfillmentStrategy(),
+ seed
+ );
+ }
+
+ function getMatchFulfillments(
+ MatchDetails memory matchDetails,
+ FulfillmentStrategy memory strategy,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ MatchStrategy matchStrategy = strategy.matchStrategy;
+
+ if (matchStrategy == MatchStrategy.MAX_INCLUSION) {
+ (
+ fulfillments,
+ unspentOfferComponents,
+ unmetConsiderationComponents
+ ) = getMatchFulfillmentsUsingConsumeMethod(
+ matchDetails,
+ getMaxInclusionConsumeMethod(strategy.aggregationStrategy),
+ seed
+ );
+ } else {
+ revert("FulfillmentGeneratorLib: unsupported match strategy");
+ }
+ }
+
+ function getMaxInclusionConsumeMethod(
+ AggregationStrategy aggregationStrategy
+ )
+ internal
+ pure
+ returns (
+ function(FulfillmentItems memory, FulfillmentItems memory, uint256)
+ internal
+ pure
+ returns (Fulfillment memory)
+ )
+ {
+ if (aggregationStrategy == AggregationStrategy.MAXIMUM) {
+ return consumeMaximumItemsAndGetFulfillment;
+ } else if (aggregationStrategy == AggregationStrategy.MINIMUM) {
+ return consumeMinimumItemsAndGetFulfillment;
+ } else if (aggregationStrategy == AggregationStrategy.RANDOM) {
+ return consumeRandomItemsAndGetFulfillment;
+ } else {
+ revert(
+ "FulfillmentGeneratorLib: unknown match aggregation strategy"
+ );
+ }
+ }
+
+ function getTotalUncoveredComponents(
+ DualFulfillmentMatchContext[] memory contexts
+ )
+ internal
+ pure
+ returns (
+ uint256 totalUnspentOfferComponents,
+ uint256 totalUnmetConsiderationComponents
+ )
+ {
+ for (uint256 i = 0; i < contexts.length; ++i) {
+ DualFulfillmentMatchContext memory context = contexts[i];
+
+ if (context.totalConsiderationAmount > context.totalOfferAmount) {
+ ++totalUnmetConsiderationComponents;
+ } else if (
+ context.totalConsiderationAmount < context.totalOfferAmount
+ ) {
+ ++totalUnspentOfferComponents;
+ }
+ }
+ }
+
+ function getUncoveredComponents(
+ MatchDetails memory matchDetails
+ )
+ internal
+ pure
+ returns (
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ (
+ uint256 totalUnspentOfferComponents,
+ uint256 totalUnmetConsiderationComponents
+ ) = getTotalUncoveredComponents(matchDetails.context);
+
+ unspentOfferComponents = (
+ new MatchComponent[](totalUnspentOfferComponents)
+ );
+
+ unmetConsiderationComponents = (
+ new MatchComponent[](totalUnmetConsiderationComponents)
+ );
+
+ if (
+ totalUnspentOfferComponents + totalUnmetConsiderationComponents == 0
+ ) {
+ return (unspentOfferComponents, unmetConsiderationComponents);
+ }
+
+ totalUnspentOfferComponents = 0;
+ totalUnmetConsiderationComponents = 0;
+
+ for (uint256 i = 0; i < matchDetails.items.length; ++i) {
+ DualFulfillmentMatchContext memory context = (
+ matchDetails.context[i]
+ );
+
+ FulfillmentItems[] memory offer = matchDetails.items[i].offer;
+
+ FulfillmentItems[] memory consideration = (
+ matchDetails.items[i].consideration
+ );
+
+ if (context.totalConsiderationAmount > context.totalOfferAmount) {
+ uint256 amount = (context.totalConsiderationAmount -
+ context.totalOfferAmount);
+
+ if (consideration.length == 0) {
+ revert(
+ "FulfillmentGeneratorLib: empty consideration array"
+ );
+ }
+
+ if (consideration[0].items.length == 0) {
+ revert(
+ "FulfillmentGeneratorLib: empty consideration items"
+ );
+ }
+
+ FulfillmentItem memory item = consideration[0].items[0];
+
+ if (
+ item.orderIndex > type(uint8).max ||
+ item.itemIndex > type(uint8).max
+ ) {
+ revert(
+ "FulfillmentGeneratorLib: OOR consideration item index"
+ );
+ }
+
+ unmetConsiderationComponents[
+ totalUnmetConsiderationComponents++
+ ] = MatchComponent({
+ amount: amount,
+ orderIndex: uint8(item.orderIndex),
+ itemIndex: uint8(item.itemIndex)
+ });
+ } else if (
+ context.totalConsiderationAmount < context.totalOfferAmount
+ ) {
+ uint256 amount = (context.totalOfferAmount -
+ context.totalConsiderationAmount);
+
+ if (offer.length == 0) {
+ revert("FulfillmentGeneratorLib: empty offer array");
+ }
+
+ if (offer[0].items.length == 0) {
+ revert("FulfillmentGeneratorLib: empty offer items");
+ }
+
+ FulfillmentItem memory item = offer[0].items[0];
+
+ if (
+ item.orderIndex > type(uint8).max ||
+ item.itemIndex > type(uint8).max
+ ) {
+ revert("FulfillmentGeneratorLib: OOR offer item index");
+ }
+
+ unspentOfferComponents[
+ totalUnspentOfferComponents++
+ ] = MatchComponent({
+ amount: amount,
+ orderIndex: uint8(item.orderIndex),
+ itemIndex: uint8(item.itemIndex)
+ });
+ }
+ }
+
+ // Sanity checks
+ if (unspentOfferComponents.length != totalUnspentOfferComponents) {
+ revert(
+ "FulfillmentGeneratorLib: unspent match item assignment error"
+ );
+ }
+
+ if (
+ unmetConsiderationComponents.length !=
+ totalUnmetConsiderationComponents
+ ) {
+ revert(
+ "FulfillmentGeneratorLib: unmet match item assignment error"
+ );
+ }
+
+ for (uint256 i = 0; i < unspentOfferComponents.length; ++i) {
+ if (unspentOfferComponents[i].amount == 0) {
+ revert("FulfillmentGeneratorLib: unspent match amount of zero");
+ }
+ }
+
+ for (uint256 i = 0; i < unmetConsiderationComponents.length; ++i) {
+ if (unmetConsiderationComponents[i].amount == 0) {
+ revert("FulfillmentGeneratorLib: unmet match amount of zero");
+ }
+ }
+ }
+
+ function getMatchFulfillmentsUsingConsumeMethod(
+ MatchDetails memory matchDetails,
+ function(FulfillmentItems memory, FulfillmentItems memory, uint256)
+ internal
+ pure
+ returns (Fulfillment memory) consumeMethod,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory unspentOfferComponents,
+ MatchComponent[] memory unmetConsiderationComponents
+ )
+ {
+ if (matchDetails.totalItems == 0) {
+ return (
+ fulfillments,
+ unspentOfferComponents,
+ unmetConsiderationComponents
+ );
+ }
+
+ (
+ unspentOfferComponents,
+ unmetConsiderationComponents
+ ) = getUncoveredComponents(matchDetails);
+
+ // Allocate based on max possible fulfillments; reduce after assignment.
+ fulfillments = new Fulfillment[](matchDetails.totalItems - 1);
+ uint256 currentFulfillment = 0;
+
+ // The outer loop processes each matchable group.
+ for (uint256 i = 0; i < matchDetails.items.length; ++i) {
+ // This is actually a "while" loop, but bound it as a sanity check.
+ bool allProcessed = false;
+ for (uint256 j = 0; j < matchDetails.totalItems; ++j) {
+ Fulfillment memory fulfillment = consumeItems(
+ matchDetails.items[i],
+ consumeMethod,
+ seed
+ );
+
+ // Exit the inner loop if no fulfillment was located.
+ if (fulfillment.offerComponents.length == 0) {
+ allProcessed = true;
+ break;
+ }
+
+ // append the located fulfillment and continue searching.
+ fulfillments[currentFulfillment++] = fulfillment;
+ }
+ if (!allProcessed) {
+ revert("FulfillmentGeneratorLib: did not complete processing");
+ }
+ }
+
+ // Resize the fulfillments array based on number of elements assigned.
+ assembly {
+ mstore(fulfillments, currentFulfillment)
+ }
+ }
+
+ // NOTE: this does not currently minimize the number of fulfillments.
+ function consumeItems(
+ DualFulfillmentItems memory matchItems,
+ function(FulfillmentItems memory, FulfillmentItems memory, uint256)
+ internal
+ pure
+ returns (Fulfillment memory) consumeMethod,
+ uint256 seed
+ ) internal pure returns (Fulfillment memory) {
+ // Search for something that can be offered.
+ for (uint256 i = 0; i < matchItems.offer.length; ++i) {
+ FulfillmentItems memory offerItems = matchItems.offer[i];
+ if (offerItems.totalAmount != 0) {
+ // Search for something it can be matched against.
+ for (uint256 j = 0; j < matchItems.consideration.length; ++j) {
+ FulfillmentItems memory considerationItems = (
+ matchItems.consideration[j]
+ );
+
+ if (considerationItems.totalAmount != 0) {
+ return
+ consumeMethod(offerItems, considerationItems, seed);
+ }
+ }
+ }
+ }
+
+ // If none were found, return an empty fulfillment.
+ return emptyFulfillment();
+ }
+
+ function consumeMinimumItemsAndGetFulfillment(
+ FulfillmentItems memory offerItems,
+ FulfillmentItems memory considerationItems,
+ uint256 /* seed */
+ ) internal pure returns (Fulfillment memory) {
+ if (
+ offerItems.totalAmount == 0 || considerationItems.totalAmount == 0
+ ) {
+ revert("FulfillmentGeneratorLib: missing item amounts to consume");
+ }
+
+ // Allocate fulfillment component arrays with a single element.
+ FulfillmentComponent[] memory offerComponents = (
+ new FulfillmentComponent[](1)
+ );
+ FulfillmentComponent[] memory considerationComponents = (
+ new FulfillmentComponent[](1)
+ );
+
+ FulfillmentItem memory offerItem;
+ for (uint256 i = 0; i < offerItems.items.length; ++i) {
+ offerItem = offerItems.items[i];
+
+ if (offerItem.amount != 0) {
+ break;
+ }
+ }
+
+ FulfillmentItem memory considerationItem;
+ for (uint256 i = 0; i < considerationItems.items.length; ++i) {
+ considerationItem = considerationItems.items[i];
+
+ if (considerationItem.amount != 0) {
+ break;
+ }
+ }
+
+ offerComponents[0] = getFulfillmentComponent(offerItem);
+ considerationComponents[0] = getFulfillmentComponent(considerationItem);
+
+ if (offerItem.amount < considerationItem.amount) {
+ offerItems.totalAmount -= offerItem.amount;
+ considerationItems.totalAmount -= offerItem.amount;
+ considerationItem.amount -= offerItem.amount;
+ offerItem.amount = 0;
+ } else {
+ offerItems.totalAmount -= considerationItem.amount;
+ considerationItems.totalAmount -= considerationItem.amount;
+ offerItem.amount -= considerationItem.amount;
+ considerationItem.amount = 0;
+ }
+
+ return
+ Fulfillment({
+ offerComponents: offerComponents,
+ considerationComponents: considerationComponents
+ });
+ }
+
+ function consumeMaximumItemsAndGetFulfillment(
+ FulfillmentItems memory offerItems,
+ FulfillmentItems memory considerationItems,
+ uint256 /* seed */
+ ) internal pure returns (Fulfillment memory) {
+ if (
+ offerItems.totalAmount == 0 || considerationItems.totalAmount == 0
+ ) {
+ revert("FulfillmentGeneratorLib: missing item amounts to consume");
+ }
+
+ // Allocate fulfillment component arrays using total items; reduce
+ // length after based on the total number of elements assigned to each.
+ FulfillmentComponent[] memory offerComponents = (
+ new FulfillmentComponent[](offerItems.items.length)
+ );
+ FulfillmentComponent[] memory considerationComponents = (
+ new FulfillmentComponent[](considerationItems.items.length)
+ );
+
+ uint256 assignmentIndex = 0;
+
+ uint256 amountToConsume = offerItems.totalAmount >
+ considerationItems.totalAmount
+ ? considerationItems.totalAmount
+ : offerItems.totalAmount;
+
+ uint256 amountToCredit = amountToConsume;
+
+ bool firstConsumedItemLocated = false;
+ uint256 firstConsumedItemIndex;
+
+ for (uint256 i = 0; i < offerItems.items.length; ++i) {
+ FulfillmentItem memory item = offerItems.items[i];
+ if (item.amount != 0) {
+ if (!firstConsumedItemLocated) {
+ firstConsumedItemLocated = true;
+ firstConsumedItemIndex = i;
+ }
+
+ offerComponents[assignmentIndex++] = getFulfillmentComponent(
+ item
+ );
+
+ if (item.amount >= amountToConsume) {
+ uint256 amountToAddBack = item.amount - amountToConsume;
+
+ item.amount = 0;
+
+ offerItems.items[firstConsumedItemIndex].amount += (
+ amountToAddBack
+ );
+
+ offerItems.totalAmount -= amountToConsume;
+
+ amountToConsume = 0;
+ break;
+ } else {
+ amountToConsume -= item.amount;
+ offerItems.totalAmount -= item.amount;
+
+ item.amount = 0;
+ }
+ }
+ }
+
+ // Sanity check
+ if (amountToConsume != 0) {
+ revert("FulfillmentGeneratorLib: did not consume expected amount");
+ }
+
+ // Reduce offerComponents length based on number of elements assigned.
+ assembly {
+ mstore(offerComponents, assignmentIndex)
+ }
+
+ firstConsumedItemLocated = false;
+ assignmentIndex = 0;
+
+ for (uint256 i = 0; i < considerationItems.items.length; ++i) {
+ FulfillmentItem memory item = considerationItems.items[i];
+ if (item.amount != 0) {
+ if (!firstConsumedItemLocated) {
+ firstConsumedItemLocated = true;
+ firstConsumedItemIndex = i;
+ }
+
+ considerationComponents[assignmentIndex++] = (
+ getFulfillmentComponent(item)
+ );
+
+ if (item.amount >= amountToCredit) {
+ uint256 amountToAddBack = item.amount - amountToCredit;
+
+ item.amount = 0;
+
+ considerationItems.items[firstConsumedItemIndex].amount += (
+ amountToAddBack
+ );
+
+ considerationItems.totalAmount -= amountToCredit;
+
+ amountToCredit = 0;
+ break;
+ } else {
+ amountToCredit -= item.amount;
+ considerationItems.totalAmount -= item.amount;
+
+ item.amount = 0;
+ }
+ }
+ }
+
+ // Sanity check
+ if (amountToCredit != 0) {
+ revert("FulfillmentGeneratorLib: did not credit expected amount");
+ }
+
+ // Reduce considerationComponents length based on # elements assigned.
+ assembly {
+ mstore(considerationComponents, assignmentIndex)
+ }
+
+ // Sanity check
+ if (
+ offerComponents.length == 0 || considerationComponents.length == 0
+ ) {
+ revert("FulfillmentGeneratorLib: empty match component generated");
+ }
+
+ return
+ Fulfillment({
+ offerComponents: offerComponents,
+ considerationComponents: considerationComponents
+ });
+ }
+
+ function consumeRandomItemsAndGetFulfillment(
+ FulfillmentItems memory offerItems,
+ FulfillmentItems memory considerationItems,
+ uint256 seed
+ ) internal pure returns (Fulfillment memory) {
+ if (
+ offerItems.totalAmount == 0 || considerationItems.totalAmount == 0
+ ) {
+ revert("FulfillmentGeneratorLib: missing item amounts to consume");
+ }
+
+ // Allocate fulfillment component arrays using total items; reduce
+ // length after based on the total number of elements assigned to each.
+ FulfillmentComponent[] memory offerComponents = (
+ new FulfillmentComponent[](offerItems.items.length)
+ );
+
+ FulfillmentComponent[] memory considerationComponents = (
+ new FulfillmentComponent[](considerationItems.items.length)
+ );
+
+ uint256[] memory consumableOfferIndices = new uint256[](
+ offerItems.items.length
+ );
+ uint256[] memory consumableConsiderationIndices = new uint256[](
+ considerationItems.items.length
+ );
+
+ {
+ uint256 assignmentIndex = 0;
+
+ for (uint256 i = 0; i < offerItems.items.length; ++i) {
+ FulfillmentItem memory item = offerItems.items[i];
+ if (item.amount != 0) {
+ consumableOfferIndices[assignmentIndex++] = i;
+ }
+ }
+
+ assembly {
+ mstore(consumableOfferIndices, assignmentIndex)
+ }
+
+ assignmentIndex = 0;
+
+ for (uint256 i = 0; i < considerationItems.items.length; ++i) {
+ FulfillmentItem memory item = considerationItems.items[i];
+ if (item.amount != 0) {
+ consumableConsiderationIndices[assignmentIndex++] = i;
+ }
+ }
+
+ assembly {
+ mstore(consumableConsiderationIndices, assignmentIndex)
+ }
+
+ // Sanity check
+ if (
+ consumableOfferIndices.length == 0 ||
+ consumableConsiderationIndices.length == 0
+ ) {
+ revert(
+ "FulfillmentGeneratorLib: did not find consumable items"
+ );
+ }
+
+ LibPRNG.PRNG memory prng;
+ prng.seed(seed ^ 0xdd);
+
+ prng.shuffle(consumableOfferIndices);
+ prng.shuffle(consumableConsiderationIndices);
+
+ assignmentIndex = prng.uniform(consumableOfferIndices.length) + 1;
+ assembly {
+ mstore(offerComponents, assignmentIndex)
+ mstore(consumableOfferIndices, assignmentIndex)
+ }
+
+ assignmentIndex =
+ prng.uniform(consumableConsiderationIndices.length) +
+ 1;
+ assembly {
+ mstore(considerationComponents, assignmentIndex)
+ mstore(consumableConsiderationIndices, assignmentIndex)
+ }
+ }
+
+ uint256 totalOfferAmount = 0;
+ uint256 totalConsiderationAmount = 0;
+
+ for (uint256 i = 0; i < consumableOfferIndices.length; ++i) {
+ FulfillmentItem memory item = offerItems.items[
+ consumableOfferIndices[i]
+ ];
+
+ offerComponents[i] = getFulfillmentComponent(item);
+
+ totalOfferAmount += item.amount;
+ item.amount = 0;
+ }
+
+ for (uint256 i = 0; i < consumableConsiderationIndices.length; ++i) {
+ FulfillmentItem memory item = considerationItems.items[
+ consumableConsiderationIndices[i]
+ ];
+
+ considerationComponents[i] = getFulfillmentComponent(item);
+
+ totalConsiderationAmount += item.amount;
+ item.amount = 0;
+ }
+
+ if (totalOfferAmount > totalConsiderationAmount) {
+ uint256 remainingAmount = (totalOfferAmount -
+ totalConsiderationAmount);
+
+ // add back excess to first offer item
+ offerItems.items[consumableOfferIndices[0]].amount += (
+ remainingAmount
+ );
+
+ offerItems.totalAmount -= totalConsiderationAmount;
+ considerationItems.totalAmount -= totalConsiderationAmount;
+ } else {
+ uint256 remainingAmount = (totalConsiderationAmount -
+ totalOfferAmount);
+
+ // add back excess to first consideration item
+ considerationItems
+ .items[consumableConsiderationIndices[0]]
+ .amount += remainingAmount;
+
+ offerItems.totalAmount -= totalOfferAmount;
+ considerationItems.totalAmount -= totalOfferAmount;
+ }
+
+ return
+ Fulfillment({
+ offerComponents: offerComponents,
+ considerationComponents: considerationComponents
+ });
+ }
+
+ function emptyFulfillment() internal pure returns (Fulfillment memory) {
+ FulfillmentComponent[] memory components;
+ return
+ Fulfillment({
+ offerComponents: components,
+ considerationComponents: components
+ });
+ }
+
+ function getFulfillAvailableFulfillments(
+ FulfillAvailableDetails memory fulfillAvailableDetails,
+ FulfillmentStrategy memory strategy,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments
+ )
+ {
+ ItemCategory[] memory offerCategories;
+ (
+ offerFulfillments,
+ offerCategories,
+ considerationFulfillments,
+
+ ) = getFulfillmentComponentsUsingMethod(
+ fulfillAvailableDetails,
+ getFulfillmentMethod(strategy.aggregationStrategy),
+ seed
+ );
+
+ FulfillAvailableStrategy dropStrategy = (
+ strategy.fulfillAvailableStrategy
+ );
+
+ if (dropStrategy == FulfillAvailableStrategy.KEEP_ALL) {
+ return (offerFulfillments, considerationFulfillments);
+ }
+
+ if (dropStrategy == FulfillAvailableStrategy.DROP_SINGLE_OFFER) {
+ return (
+ dropSingle(offerFulfillments, offerCategories),
+ considerationFulfillments
+ );
+ }
+
+ if (dropStrategy == FulfillAvailableStrategy.DROP_ALL_OFFER) {
+ return (
+ dropAllNon721(offerFulfillments, offerCategories),
+ considerationFulfillments
+ );
+ }
+
+ if (dropStrategy == FulfillAvailableStrategy.DROP_RANDOM_OFFER) {
+ return (
+ dropRandom(offerFulfillments, offerCategories, seed),
+ considerationFulfillments
+ );
+ }
+
+ if (
+ dropStrategy == FulfillAvailableStrategy.DROP_SINGLE_KEEP_FILTERED
+ ) {
+ revert(
+ "FulfillmentGeneratorLib: DROP_SINGLE_KEEP_FILTERED unsupported"
+ );
+ }
+
+ if (dropStrategy == FulfillAvailableStrategy.DROP_ALL_KEEP_FILTERED) {
+ revert(
+ "FulfillmentGeneratorLib: DROP_ALL_KEEP_FILTERED unsupported"
+ );
+ }
+
+ if (
+ dropStrategy == FulfillAvailableStrategy.DROP_RANDOM_KEEP_FILTERED
+ ) {
+ revert(
+ "FulfillmentGeneratorLib: DROP_RANDOM_KEEP_FILTERED unsupported"
+ );
+ }
+
+ revert("FulfillmentGeneratorLib: unknown fulfillAvailable strategy");
+ }
+
+ function dropSingle(
+ FulfillmentComponent[][] memory offerFulfillments,
+ ItemCategory[] memory offerCategories
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory fulfillments = (
+ new FulfillmentComponent[][](offerFulfillments.length)
+ );
+
+ uint256 assignmentIndex = 0;
+
+ for (uint256 i = 0; i < offerFulfillments.length; ++i) {
+ FulfillmentComponent[] memory components = offerFulfillments[i];
+ if (
+ offerCategories[i] == ItemCategory.ERC721 ||
+ components.length > 1
+ ) {
+ fulfillments[assignmentIndex++] = components;
+ }
+ }
+
+ assembly {
+ mstore(fulfillments, assignmentIndex)
+ }
+
+ return fulfillments;
+ }
+
+ function dropAllNon721(
+ FulfillmentComponent[][] memory offerFulfillments,
+ ItemCategory[] memory offerCategories
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory fulfillments = (
+ new FulfillmentComponent[][](offerFulfillments.length)
+ );
+
+ uint256 assignmentIndex = 0;
+
+ for (uint256 i = 0; i < offerFulfillments.length; ++i) {
+ FulfillmentComponent[] memory components = offerFulfillments[i];
+ if (offerCategories[i] == ItemCategory.ERC721) {
+ fulfillments[assignmentIndex++] = components;
+ }
+ }
+
+ assembly {
+ mstore(fulfillments, assignmentIndex)
+ }
+
+ return fulfillments;
+ }
+
+ function dropRandom(
+ FulfillmentComponent[][] memory offerFulfillments,
+ ItemCategory[] memory offerCategories,
+ uint256 seed
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ LibPRNG.PRNG memory prng;
+ prng.seed(seed ^ 0xbb);
+
+ FulfillmentComponent[][] memory fulfillments = (
+ new FulfillmentComponent[][](offerFulfillments.length)
+ );
+
+ uint256 assignmentIndex = 0;
+
+ for (uint256 i = 0; i < offerFulfillments.length; ++i) {
+ FulfillmentComponent[] memory components = offerFulfillments[i];
+ if (
+ offerCategories[i] == ItemCategory.ERC721 ||
+ prng.uniform(2) == 0
+ ) {
+ fulfillments[assignmentIndex++] = components;
+ }
+ }
+
+ assembly {
+ mstore(fulfillments, assignmentIndex)
+ }
+
+ return fulfillments;
+ }
+
+ function getFulfillmentMethod(
+ AggregationStrategy aggregationStrategy
+ )
+ internal
+ pure
+ returns (
+ function(FulfillmentItems[] memory, uint256)
+ internal
+ pure
+ returns (FulfillmentComponent[][] memory, ItemCategory[] memory)
+ )
+ {
+ if (aggregationStrategy == AggregationStrategy.MAXIMUM) {
+ return getMaxFulfillmentComponents;
+ } else if (aggregationStrategy == AggregationStrategy.MINIMUM) {
+ return getMinFulfillmentComponents;
+ } else if (aggregationStrategy == AggregationStrategy.RANDOM) {
+ return getRandomFulfillmentComponents;
+ } else {
+ revert("FulfillmentGeneratorLib: unknown aggregation strategy");
+ }
+ }
+
+ function getFulfillmentComponentsUsingMethod(
+ FulfillAvailableDetails memory fulfillAvailableDetails,
+ function(FulfillmentItems[] memory, uint256)
+ internal
+ pure
+ returns (
+ FulfillmentComponent[][] memory,
+ ItemCategory[] memory
+ ) fulfillmentMethod,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[][] memory offerFulfillments,
+ ItemCategory[] memory offerCategories,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ ItemCategory[] memory considerationCategories
+ )
+ {
+ (offerFulfillments, offerCategories) = fulfillmentMethod(
+ fulfillAvailableDetails.items.offer,
+ seed
+ );
+
+ (
+ considerationFulfillments,
+ considerationCategories
+ ) = fulfillmentMethod(
+ fulfillAvailableDetails.items.consideration,
+ seed
+ );
+ }
+
+ function getMaxFulfillmentComponents(
+ FulfillmentItems[] memory fulfillmentItems,
+ uint256 /* seed */
+ )
+ internal
+ pure
+ returns (FulfillmentComponent[][] memory, ItemCategory[] memory)
+ {
+ FulfillmentComponent[][] memory fulfillments = (
+ new FulfillmentComponent[][](fulfillmentItems.length)
+ );
+
+ ItemCategory[] memory categories = new ItemCategory[](
+ fulfillmentItems.length
+ );
+
+ for (uint256 i = 0; i < fulfillmentItems.length; ++i) {
+ fulfillments[i] = getFulfillmentComponents(
+ fulfillmentItems[i].items
+ );
+ categories[i] = fulfillmentItems[i].itemCategory;
+ }
+
+ return (fulfillments, categories);
+ }
+
+ function getRandomFulfillmentComponents(
+ FulfillmentItems[] memory fulfillmentItems,
+ uint256 seed
+ )
+ internal
+ pure
+ returns (FulfillmentComponent[][] memory, ItemCategory[] memory)
+ {
+ uint256 fulfillmentCount = 0;
+
+ for (uint256 i = 0; i < fulfillmentItems.length; ++i) {
+ fulfillmentCount += fulfillmentItems[i].items.length;
+ }
+
+ FulfillmentComponent[][] memory fulfillments = (
+ new FulfillmentComponent[][](fulfillmentCount)
+ );
+
+ ItemCategory[] memory categories = new ItemCategory[](fulfillmentCount);
+
+ LibPRNG.PRNG memory prng;
+ prng.seed(seed ^ 0xcc);
+
+ fulfillmentCount = 0;
+ for (uint256 i = 0; i < fulfillmentItems.length; ++i) {
+ FulfillmentItem[] memory items = fulfillmentItems[i].items;
+
+ for (uint256 j = 0; j < items.length; ++j) {
+ FulfillmentComponent[] memory fulfillment = (
+ consumeRandomFulfillmentItems(items, prng)
+ );
+
+ if (fulfillment.length == 0) {
+ break;
+ }
+
+ categories[fulfillmentCount] = fulfillmentItems[i].itemCategory;
+ fulfillments[fulfillmentCount++] = fulfillment;
+ }
+ }
+
+ assembly {
+ mstore(fulfillments, fulfillmentCount)
+ mstore(categories, fulfillmentCount)
+ }
+
+ uint256[] memory componentIndices = new uint256[](fulfillments.length);
+ for (uint256 i = 0; i < fulfillments.length; ++i) {
+ componentIndices[i] = i;
+ }
+
+ prng.shuffle(componentIndices);
+
+ FulfillmentComponent[][] memory shuffledFulfillments = (
+ new FulfillmentComponent[][](fulfillments.length)
+ );
+
+ ItemCategory[] memory shuffledCategories = (
+ new ItemCategory[](fulfillments.length)
+ );
+
+ for (uint256 i = 0; i < fulfillments.length; ++i) {
+ uint256 priorIndex = componentIndices[i];
+ shuffledFulfillments[i] = fulfillments[priorIndex];
+ shuffledCategories[i] = categories[priorIndex];
+ }
+
+ return (shuffledFulfillments, shuffledCategories);
+ }
+
+ function consumeRandomFulfillmentItems(
+ FulfillmentItem[] memory items,
+ LibPRNG.PRNG memory prng
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ uint256[] memory consumableItemIndices = new uint256[](items.length);
+ uint256 assignmentIndex = 0;
+ for (uint256 i = 0; i < items.length; ++i) {
+ if (items[i].amount != 0) {
+ consumableItemIndices[assignmentIndex++] = i;
+ }
+ }
+
+ if (assignmentIndex == 0) {
+ return new FulfillmentComponent[](0);
+ }
+
+ assembly {
+ mstore(consumableItemIndices, assignmentIndex)
+ }
+
+ prng.shuffle(consumableItemIndices);
+
+ assignmentIndex = prng.uniform(assignmentIndex) + 1;
+
+ assembly {
+ mstore(consumableItemIndices, assignmentIndex)
+ }
+
+ FulfillmentComponent[] memory fulfillment = new FulfillmentComponent[](
+ consumableItemIndices.length
+ );
+
+ for (uint256 i = 0; i < consumableItemIndices.length; ++i) {
+ FulfillmentItem memory item = items[consumableItemIndices[i]];
+
+ fulfillment[i] = getFulfillmentComponent(item);
+
+ item.amount = 0;
+ }
+
+ return fulfillment;
+ }
+
+ function getMinFulfillmentComponents(
+ FulfillmentItems[] memory fulfillmentItems,
+ uint256 /* seed */
+ )
+ internal
+ pure
+ returns (FulfillmentComponent[][] memory, ItemCategory[] memory)
+ {
+ uint256 fulfillmentCount = 0;
+
+ for (uint256 i = 0; i < fulfillmentItems.length; ++i) {
+ fulfillmentCount += fulfillmentItems[i].items.length;
+ }
+
+ FulfillmentComponent[][] memory fulfillments = (
+ new FulfillmentComponent[][](fulfillmentCount)
+ );
+
+ ItemCategory[] memory categories = (
+ new ItemCategory[](fulfillmentCount)
+ );
+
+ fulfillmentCount = 0;
+ for (uint256 i = 0; i < fulfillmentItems.length; ++i) {
+ FulfillmentItem[] memory items = fulfillmentItems[i].items;
+
+ for (uint256 j = 0; j < items.length; ++j) {
+ FulfillmentComponent[] memory fulfillment = (
+ new FulfillmentComponent[](1)
+ );
+ fulfillment[0] = getFulfillmentComponent(items[j]);
+ categories[fulfillmentCount] = fulfillmentItems[i].itemCategory;
+ fulfillments[fulfillmentCount++] = fulfillment;
+ }
+ }
+
+ return (fulfillments, categories);
+ }
+
+ function getFulfillmentComponents(
+ FulfillmentItem[] memory items
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory fulfillment = new FulfillmentComponent[](
+ items.length
+ );
+
+ for (uint256 i = 0; i < items.length; ++i) {
+ fulfillment[i] = getFulfillmentComponent(items[i]);
+ }
+
+ return fulfillment;
+ }
+
+ function getFulfillmentComponent(
+ FulfillmentItem memory item
+ ) internal pure returns (FulfillmentComponent memory) {
+ return
+ FulfillmentComponent({
+ orderIndex: item.orderIndex,
+ itemIndex: item.itemIndex
+ });
+ }
+
+ function determineFulfillAvailableEligibility(
+ FulfillAvailableDetails memory fulfillAvailableDetails
+ ) internal pure returns (bool) {
+ bool atLeastOneExecution = false;
+
+ FulfillmentItems[] memory offer = fulfillAvailableDetails.items.offer;
+ for (uint256 i = 0; i < offer.length; ++i) {
+ FulfillmentItems memory fulfillmentItems = offer[i];
+ if (
+ fulfillmentItems.itemCategory == ItemCategory.NATIVE ||
+ (fulfillmentItems.itemCategory == ItemCategory.ERC721 &&
+ fulfillmentItems.totalAmount != 1)
+ ) {
+ return false;
+ }
+
+ // TODO: Ensure that the same ERC721 item doesn't appear on both the
+ // offer side and the consideration side if the recipient does not
+ // equal the caller.
+
+ if (!atLeastOneExecution) {
+ for (uint256 j = 0; j < fulfillmentItems.items.length; ++j) {
+ FulfillmentItem memory item = fulfillmentItems.items[j];
+ if (item.account != fulfillAvailableDetails.recipient) {
+ atLeastOneExecution = true;
+ break;
+ }
+ }
+ }
+ }
+
+ FulfillmentItems[] memory consideration = (
+ fulfillAvailableDetails.items.consideration
+ );
+ for (uint256 i = 0; i < consideration.length; ++i) {
+ FulfillmentItems memory fulfillmentItems = consideration[i];
+ if (
+ fulfillmentItems.itemCategory == ItemCategory.ERC721 &&
+ fulfillmentItems.totalAmount != 1
+ ) {
+ return false;
+ }
+
+ if (!atLeastOneExecution) {
+ for (uint256 j = 0; j < fulfillmentItems.items.length; ++j) {
+ FulfillmentItem memory item = fulfillmentItems.items[j];
+ if (item.account != fulfillAvailableDetails.caller) {
+ atLeastOneExecution = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+}
+
+library FulfillmentPrepLib {
+ using ItemReferenceLib for OrderDetails[];
+ using ItemReferenceGroupLib for ItemReferenceLib.ItemReference[];
+ using ItemReferenceGroupLib for ItemReferenceGroupLib.ItemReferenceGroup[];
+ using MatchableItemReferenceGroupLib for MatchableItemReferenceGroupLib.MatchableItemReferenceGroup[];
+ using FulfillAvailableReferenceGroupLib for FulfillAvailableReferenceGroupLib.FulfillAvailableReferenceGroup;
+
+ function getFulfillAvailableDetails(
+ OrderDetails[] memory orderDetails,
+ address recipient,
+ address caller,
+ uint256 seed
+ ) internal pure returns (FulfillAvailableDetails memory) {
+ return
+ getFulfillAvailableDetailsFromReferences(
+ orderDetails.getItemReferences(seed),
+ recipient,
+ caller
+ );
+ }
+
+ function getDetails(
+ ItemReferenceLib.ItemReference[] memory itemReferences,
+ address recipient,
+ address caller
+ )
+ internal
+ pure
+ returns (FulfillAvailableDetails memory, MatchDetails memory)
+ {
+ ItemReferenceGroupLib.ItemReferenceGroup[]
+ memory groups = itemReferences.bundleByAggregatable();
+
+ return (
+ groups.splitBySide(recipient, caller).getFulfillAvailableDetails(),
+ groups.bundleByMatchable(itemReferences).getMatchDetails(recipient)
+ );
+ }
+
+ function getFulfillAvailableDetailsFromReferences(
+ ItemReferenceLib.ItemReference[] memory itemReferences,
+ address recipient,
+ address caller
+ ) internal pure returns (FulfillAvailableDetails memory) {
+ return
+ itemReferences
+ .bundleByAggregatable()
+ .splitBySide(recipient, caller)
+ .getFulfillAvailableDetails();
+ }
+
+ function getMatchDetails(
+ OrderDetails[] memory orderDetails,
+ address recipient,
+ uint256 seed
+ ) internal pure returns (MatchDetails memory) {
+ return
+ getMatchDetailsFromReferences(
+ orderDetails.getItemReferences(seed),
+ recipient
+ );
+ }
+
+ function getMatchDetailsFromReferences(
+ ItemReferenceLib.ItemReference[] memory itemReferences,
+ address recipient
+ ) internal pure returns (MatchDetails memory) {
+ return
+ itemReferences
+ .bundleByAggregatable()
+ .bundleByMatchable(itemReferences)
+ .getMatchDetails(recipient);
+ }
+
+ function getFulfillmentMatchContext(
+ DualFulfillmentItems[] memory matchItems
+ ) internal pure returns (DualFulfillmentMatchContext[] memory) {
+ DualFulfillmentMatchContext[] memory context = (
+ new DualFulfillmentMatchContext[](matchItems.length)
+ );
+
+ for (uint256 i = 0; i < matchItems.length; ++i) {
+ bool itemCategorySet = false;
+ ItemCategory itemCategory;
+ uint256 totalOfferAmount = 0;
+ uint256 totalConsiderationAmount = 0;
+
+ FulfillmentItems[] memory offer = matchItems[i].offer;
+ for (uint256 j = 0; j < offer.length; ++j) {
+ FulfillmentItems memory items = offer[j];
+
+ if (!itemCategorySet) {
+ itemCategory = items.itemCategory;
+ } else if (itemCategory != items.itemCategory) {
+ revert("FulfillmentPrepLib: mismatched item categories");
+ }
+
+ totalOfferAmount += items.totalAmount;
+ }
+
+ FulfillmentItems[] memory consideration = (
+ matchItems[i].consideration
+ );
+ for (uint256 j = 0; j < consideration.length; ++j) {
+ FulfillmentItems memory items = consideration[j];
+
+ if (!itemCategorySet) {
+ itemCategory = items.itemCategory;
+ } else if (itemCategory != items.itemCategory) {
+ revert("FulfillmentPrepLib: mismatched item categories");
+ }
+
+ totalConsiderationAmount += items.totalAmount;
+ }
+
+ context[i] = DualFulfillmentMatchContext({
+ itemCategory: itemCategory,
+ totalOfferAmount: totalOfferAmount,
+ totalConsiderationAmount: totalConsiderationAmount
+ });
+ }
+
+ return context;
+ }
+
+ function getDualFulfillmentItems(
+ ItemReferenceGroupLib.ItemReferenceGroup[] memory offerGroups,
+ ItemReferenceGroupLib.ItemReferenceGroup[] memory considerationGroups
+ ) internal pure returns (DualFulfillmentItems memory, uint256 totalItems) {
+ DualFulfillmentItems memory items = DualFulfillmentItems({
+ offer: new FulfillmentItems[](offerGroups.length),
+ consideration: new FulfillmentItems[](considerationGroups.length)
+ });
+
+ uint256 currentItems;
+
+ for (uint256 i = 0; i < offerGroups.length; ++i) {
+ (items.offer[i], currentItems) = getFulfillmentItems(
+ offerGroups[i].references
+ );
+
+ totalItems += currentItems;
+ }
+
+ for (uint256 i = 0; i < considerationGroups.length; ++i) {
+ (items.consideration[i], currentItems) = getFulfillmentItems(
+ considerationGroups[i].references
+ );
+
+ totalItems += currentItems;
+ }
+
+ return (items, totalItems);
+ }
+
+ function getFulfillmentItems(
+ ItemReferenceLib.ItemReference[] memory itemReferences
+ ) internal pure returns (FulfillmentItems memory, uint256 totalItems) {
+ // Sanity check: ensure there's at least one reference
+ if (itemReferences.length == 0) {
+ revert("FulfillmentPrepLib: empty item references supplied");
+ }
+
+ ItemReferenceLib.ItemReference memory firstReference = itemReferences[
+ 0
+ ];
+ FulfillmentItems memory fulfillmentItems = FulfillmentItems({
+ itemCategory: firstReference.itemCategory,
+ totalAmount: 0,
+ items: new FulfillmentItem[](itemReferences.length)
+ });
+
+ for (uint256 i = 0; i < itemReferences.length; ++i) {
+ ItemReferenceLib.ItemReference
+ memory itemReference = itemReferences[i];
+ uint256 amount = itemReference.amount;
+ fulfillmentItems.totalAmount += amount;
+ fulfillmentItems.items[i] = FulfillmentItem({
+ orderIndex: itemReference.orderIndex,
+ itemIndex: itemReference.itemIndex,
+ amount: amount,
+ account: itemReference.account
+ });
+ }
+
+ totalItems = itemReferences.length;
+
+ return (fulfillmentItems, totalItems);
+ }
+}
+
+library FulfillAvailableReferenceGroupLib {
+ struct FulfillAvailableReferenceGroup {
+ ItemReferenceGroupLib.ItemReferenceGroup[] offerGroups;
+ ItemReferenceGroupLib.ItemReferenceGroup[] considerationGroups;
+ address recipient;
+ address caller;
+ }
+
+ function getFulfillAvailableDetails(
+ FulfillAvailableReferenceGroup memory group
+ ) internal pure returns (FulfillAvailableDetails memory) {
+ (
+ DualFulfillmentItems memory items,
+ uint256 totalItems
+ ) = FulfillmentPrepLib.getDualFulfillmentItems(
+ group.offerGroups,
+ group.considerationGroups
+ );
+
+ return
+ FulfillAvailableDetails({
+ items: items,
+ caller: group.caller,
+ recipient: group.recipient,
+ totalItems: totalItems
+ });
+ }
+}
+
+library MatchableItemReferenceGroupLib {
+ struct MatchableItemReferenceGroup {
+ bytes32 dataHash;
+ ItemReferenceGroupLib.ItemReferenceGroup[] offerGroups;
+ ItemReferenceGroupLib.ItemReferenceGroup[] considerationGroups;
+ uint256 offerAssigned;
+ uint256 considerationAssigned;
+ }
+
+ function getMatchDetails(
+ MatchableItemReferenceGroup[] memory matchableGroups,
+ address recipient
+ ) internal pure returns (MatchDetails memory) {
+ DualFulfillmentItems[] memory items = new DualFulfillmentItems[](
+ matchableGroups.length
+ );
+
+ uint256 totalItems = 0;
+ uint256 itemsInGroup = 0;
+
+ for (uint256 i = 0; i < matchableGroups.length; ++i) {
+ MatchableItemReferenceGroup memory matchableGroup = (
+ matchableGroups[i]
+ );
+
+ (items[i], itemsInGroup) = FulfillmentPrepLib
+ .getDualFulfillmentItems(
+ matchableGroup.offerGroups,
+ matchableGroup.considerationGroups
+ );
+
+ totalItems += itemsInGroup;
+ }
+
+ return
+ MatchDetails({
+ items: items,
+ context: FulfillmentPrepLib.getFulfillmentMatchContext(items),
+ recipient: recipient,
+ totalItems: totalItems
+ });
+ }
+}
+
+library ItemReferenceGroupLib {
+ using HashCountLib for ItemReferenceLib.ItemReference[];
+ using HashAllocatorLib for HashCountLib.HashCount[];
+
+ struct ItemReferenceGroup {
+ bytes32 fullHash;
+ ItemReferenceLib.ItemReference[] references;
+ uint256 assigned;
+ }
+
+ function bundleByAggregatable(
+ ItemReferenceLib.ItemReference[] memory itemReferences
+ ) internal pure returns (ItemReferenceGroup[] memory) {
+ ItemReferenceGroup[] memory groups = itemReferences
+ .getUniqueFullHashes()
+ .allocateItemReferenceGroup();
+
+ for (uint256 i = 0; i < itemReferences.length; ++i) {
+ ItemReferenceLib.ItemReference
+ memory itemReference = itemReferences[i];
+ for (uint256 j = 0; j < groups.length; ++j) {
+ ItemReferenceGroup memory group = groups[j];
+ if (group.fullHash == itemReference.fullHash) {
+ group.references[group.assigned++] = itemReference;
+ break;
+ }
+ }
+ }
+
+ // Sanity check: ensure at least one reference item on each group
+ for (uint256 i = 0; i < groups.length; ++i) {
+ if (groups[i].references.length == 0) {
+ revert(
+ "ItemReferenceGroupLib: missing item reference in group"
+ );
+ }
+ }
+
+ return groups;
+ }
+
+ function splitBySide(
+ ItemReferenceGroup[] memory groups,
+ address recipient,
+ address caller
+ )
+ internal
+ pure
+ returns (
+ FulfillAvailableReferenceGroupLib.FulfillAvailableReferenceGroup
+ memory
+ )
+ {
+ // NOTE: lengths are overallocated; reduce after assignment.
+ ItemReferenceGroup[] memory offerGroups = (
+ new ItemReferenceGroup[](groups.length)
+ );
+ ItemReferenceGroup[] memory considerationGroups = (
+ new ItemReferenceGroup[](groups.length)
+ );
+ uint256 offerItems = 0;
+ uint256 considerationItems = 0;
+
+ for (uint256 i = 0; i < groups.length; ++i) {
+ ItemReferenceGroup memory group = groups[i];
+
+ if (group.references.length == 0) {
+ revert("ItemReferenceGroupLib: no items in group");
+ }
+
+ Side side = group.references[0].side;
+
+ if (side == Side.OFFER) {
+ offerGroups[offerItems++] = group;
+ } else if (side == Side.CONSIDERATION) {
+ considerationGroups[considerationItems++] = group;
+ } else {
+ revert("ItemReferenceGroupLib: invalid side located (split)");
+ }
+ }
+
+ // Reduce group lengths based on number of elements assigned.
+ assembly {
+ mstore(offerGroups, offerItems)
+ mstore(considerationGroups, considerationItems)
+ }
+
+ return
+ FulfillAvailableReferenceGroupLib.FulfillAvailableReferenceGroup({
+ offerGroups: offerGroups,
+ considerationGroups: considerationGroups,
+ recipient: recipient,
+ caller: caller
+ });
+ }
+
+ function bundleByMatchable(
+ ItemReferenceGroup[] memory groups,
+ ItemReferenceLib.ItemReference[] memory itemReferences
+ )
+ internal
+ pure
+ returns (
+ MatchableItemReferenceGroupLib.MatchableItemReferenceGroup[] memory
+ )
+ {
+ MatchableItemReferenceGroupLib.MatchableItemReferenceGroup[]
+ memory matchableGroups = (
+ itemReferences
+ .getUniqueDataHashes()
+ .allocateMatchableItemReferenceGroup()
+ );
+
+ for (uint256 i = 0; i < groups.length; ++i) {
+ ItemReferenceGroup memory group = groups[i];
+
+ if (group.references.length == 0) {
+ revert(
+ "ItemReferenceGroupLib: empty item reference group supplied"
+ );
+ }
+
+ ItemReferenceLib.ItemReference memory firstReference = group
+ .references[0];
+ for (uint256 j = 0; j < matchableGroups.length; ++j) {
+ MatchableItemReferenceGroupLib.MatchableItemReferenceGroup
+ memory matchableGroup = (matchableGroups[j]);
+
+ if (matchableGroup.dataHash == firstReference.dataHash) {
+ if (firstReference.side == Side.OFFER) {
+ matchableGroup.offerGroups[
+ matchableGroup.offerAssigned++
+ ] = group;
+ } else if (firstReference.side == Side.CONSIDERATION) {
+ matchableGroup.considerationGroups[
+ matchableGroup.considerationAssigned++
+ ] = group;
+ } else {
+ revert(
+ "ItemReferenceGroupLib: invalid match side located"
+ );
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Reduce reference group array lengths based on assigned elements.
+ for (uint256 i = 0; i < matchableGroups.length; ++i) {
+ MatchableItemReferenceGroupLib.MatchableItemReferenceGroup
+ memory group = matchableGroups[i];
+ uint256 offerAssigned = group.offerAssigned;
+ uint256 considerationAssigned = group.considerationAssigned;
+ ItemReferenceGroup[] memory offerGroups = (group.offerGroups);
+ ItemReferenceGroup[] memory considerationGroups = (
+ group.considerationGroups
+ );
+
+ assembly {
+ mstore(offerGroups, offerAssigned)
+ mstore(considerationGroups, considerationAssigned)
+ }
+ }
+
+ return matchableGroups;
+ }
+}
+
+library HashAllocatorLib {
+ function allocateItemReferenceGroup(
+ HashCountLib.HashCount[] memory hashCount
+ )
+ internal
+ pure
+ returns (ItemReferenceGroupLib.ItemReferenceGroup[] memory)
+ {
+ ItemReferenceGroupLib.ItemReferenceGroup[]
+ memory group = new ItemReferenceGroupLib.ItemReferenceGroup[](
+ hashCount.length
+ );
+
+ for (uint256 i = 0; i < hashCount.length; ++i) {
+ group[i] = ItemReferenceGroupLib.ItemReferenceGroup({
+ fullHash: hashCount[i].hash,
+ references: new ItemReferenceLib.ItemReference[](
+ hashCount[i].count
+ ),
+ assigned: 0
+ });
+ }
+
+ return group;
+ }
+
+ function allocateMatchableItemReferenceGroup(
+ HashCountLib.HashCount[] memory hashCount
+ )
+ internal
+ pure
+ returns (
+ MatchableItemReferenceGroupLib.MatchableItemReferenceGroup[] memory
+ )
+ {
+ MatchableItemReferenceGroupLib.MatchableItemReferenceGroup[]
+ memory group = (
+ new MatchableItemReferenceGroupLib.MatchableItemReferenceGroup[](
+ hashCount.length
+ )
+ );
+
+ for (uint256 i = 0; i < hashCount.length; ++i) {
+ // NOTE: reference group lengths are overallocated and will need to
+ // be reduced once their respective elements have been assigned.
+ uint256 count = hashCount[i].count;
+ group[i] = MatchableItemReferenceGroupLib
+ .MatchableItemReferenceGroup({
+ dataHash: hashCount[i].hash,
+ offerGroups: new ItemReferenceGroupLib.ItemReferenceGroup[](
+ count
+ ),
+ considerationGroups: (
+ new ItemReferenceGroupLib.ItemReferenceGroup[](count)
+ ),
+ offerAssigned: 0,
+ considerationAssigned: 0
+ });
+ }
+
+ return group;
+ }
+}
+
+library HashCountLib {
+ using LibPRNG for LibPRNG.PRNG;
+ using LibSort for uint256[];
+ using ItemReferenceLib for OrderDetails[];
+ using ItemReferenceLib for ItemReferenceLib.ItemReference[];
+
+ struct HashCount {
+ bytes32 hash;
+ uint256 count;
+ }
+
+ function getUniqueFullHashes(
+ ItemReferenceLib.ItemReference[] memory itemReferences
+ ) internal pure returns (HashCount[] memory) {
+ uint256[] memory fullHashes = new uint256[](itemReferences.length);
+
+ for (uint256 i = 0; i < itemReferences.length; ++i) {
+ fullHashes[i] = uint256(itemReferences[i].fullHash);
+ }
+
+ return getHashCount(fullHashes);
+ }
+
+ function getUniqueDataHashes(
+ ItemReferenceLib.ItemReference[] memory itemReferences
+ ) internal pure returns (HashCount[] memory) {
+ uint256[] memory dataHashes = new uint256[](itemReferences.length);
+
+ for (uint256 i = 0; i < itemReferences.length; ++i) {
+ dataHashes[i] = uint256(itemReferences[i].dataHash);
+ }
+
+ return getHashCount(dataHashes);
+ }
+
+ function getHashCount(
+ uint256[] memory hashes
+ ) internal pure returns (HashCount[] memory) {
+ if (hashes.length == 0) {
+ return new HashCount[](0);
+ }
+
+ hashes.sort();
+
+ HashCount[] memory hashCount = new HashCount[](hashes.length);
+ hashCount[0] = HashCount({ hash: bytes32(hashes[0]), count: 1 });
+
+ uint256 hashCountPointer = 0;
+ for (uint256 i = 1; i < hashes.length; ++i) {
+ bytes32 element = bytes32(hashes[i]);
+
+ if (element != hashCount[hashCountPointer].hash) {
+ hashCount[++hashCountPointer] = HashCount({
+ hash: element,
+ count: 1
+ });
+ } else {
+ ++hashCount[hashCountPointer].count;
+ }
+ }
+
+ // update length of the hashCount array based on the hash count pointer.
+ assembly {
+ mstore(hashCount, add(hashCountPointer, 1))
+ }
+
+ return hashCount;
+ }
+}
+
+library ItemReferenceLib {
+ using LibPRNG for LibPRNG.PRNG;
+ using LibSort for uint256[];
+
+ struct ItemReference {
+ uint256 orderIndex;
+ uint256 itemIndex;
+ Side side;
+ bytes32 dataHash; // itemType ++ token ++ identifier
+ bytes32 fullHash; // dataHash ++ [offerer ++ conduitKey || recipient]
+ uint256 amount;
+ ItemCategory itemCategory;
+ address account;
+ }
+
+ function getItemReferences(
+ OrderDetails[] memory orderDetails,
+ uint256 seed
+ ) internal pure returns (ItemReference[] memory) {
+ ItemReference[] memory itemReferences = new ItemReference[](
+ getTotalItems(orderDetails)
+ );
+
+ uint256 itemReferenceIndex = 0;
+
+ for (
+ uint256 orderIndex = 0;
+ orderIndex < orderDetails.length;
+ ++orderIndex
+ ) {
+ OrderDetails memory order = orderDetails[orderIndex];
+ for (
+ uint256 itemIndex = 0;
+ itemIndex < order.offer.length;
+ ++itemIndex
+ ) {
+ itemReferences[itemReferenceIndex++] = getItemReference(
+ order.offer[itemIndex],
+ orderIndex,
+ itemIndex,
+ order.offerer,
+ order.conduitKey
+ );
+ }
+ for (
+ uint256 itemIndex = 0;
+ itemIndex < order.consideration.length;
+ ++itemIndex
+ ) {
+ itemReferences[itemReferenceIndex++] = getItemReference(
+ order.consideration[itemIndex],
+ orderIndex,
+ itemIndex
+ );
+ }
+ }
+
+ if (seed == 0) {
+ return itemReferences;
+ }
+
+ return shuffle(itemReferences, seed);
+ }
+
+ function shuffle(
+ ItemReference[] memory itemReferences,
+ uint256 seed
+ ) internal pure returns (ItemReference[] memory) {
+ ItemReference[] memory shuffledItemReferences = new ItemReference[](
+ itemReferences.length
+ );
+
+ uint256[] memory indices = new uint256[](itemReferences.length);
+ for (uint256 i = 0; i < indices.length; ++i) {
+ indices[i] = i;
+ }
+
+ LibPRNG.PRNG memory prng;
+ prng.seed(seed ^ 0xee);
+ prng.shuffle(indices);
+
+ for (uint256 i = 0; i < indices.length; ++i) {
+ shuffledItemReferences[i] = itemReferences[indices[i]];
+ }
+
+ return shuffledItemReferences;
+ }
+
+ function getItemReference(
+ SpentItem memory item,
+ uint256 orderIndex,
+ uint256 itemIndex,
+ address offerer,
+ bytes32 conduitKey
+ ) internal pure returns (ItemReference memory) {
+ return
+ getItemReference(
+ orderIndex,
+ itemIndex,
+ Side.OFFER,
+ item.itemType,
+ item.token,
+ item.identifier,
+ offerer,
+ conduitKey,
+ item.amount
+ );
+ }
+
+ function getItemReference(
+ ReceivedItem memory item,
+ uint256 orderIndex,
+ uint256 itemIndex
+ ) internal pure returns (ItemReference memory) {
+ return
+ getItemReference(
+ orderIndex,
+ itemIndex,
+ Side.CONSIDERATION,
+ item.itemType,
+ item.token,
+ item.identifier,
+ item.recipient,
+ bytes32(0),
+ item.amount
+ );
+ }
+
+ function getItemReference(
+ uint256 orderIndex,
+ uint256 itemIndex,
+ Side side,
+ ItemType itemType,
+ address token,
+ uint256 identifier,
+ address account,
+ bytes32 conduitKey,
+ uint256 amount
+ ) internal pure returns (ItemReference memory) {
+ bytes32 dataHash = keccak256(
+ abi.encodePacked(itemType, token, identifier)
+ );
+
+ bytes32 fullHash;
+ if (side == Side.OFFER) {
+ fullHash = keccak256(
+ abi.encodePacked(dataHash, account, conduitKey)
+ );
+ } else if (side == Side.CONSIDERATION) {
+ fullHash = keccak256(abi.encodePacked(dataHash, account));
+ } else {
+ revert("ItemReferenceLib: invalid side located");
+ }
+
+ ItemCategory itemCategory;
+ if (itemType == ItemType.NATIVE) {
+ itemCategory = ItemCategory.NATIVE;
+ } else if (itemType == ItemType.ERC721) {
+ itemCategory = ItemCategory.ERC721;
+ } else if (itemType == ItemType.ERC20 || itemType == ItemType.ERC1155) {
+ itemCategory = ItemCategory.OTHER;
+ } else {
+ revert("ItemReferenceLib: invalid item type located");
+ }
+
+ return
+ ItemReference({
+ orderIndex: orderIndex,
+ itemIndex: itemIndex,
+ side: side,
+ dataHash: dataHash,
+ fullHash: fullHash,
+ amount: amount,
+ itemCategory: itemCategory,
+ account: account
+ });
+ }
+
+ function getTotalItems(
+ OrderDetails[] memory orderDetails
+ ) internal pure returns (uint256) {
+ uint256 totalItems = 0;
+
+ for (uint256 i = 0; i < orderDetails.length; ++i) {
+ totalItems += getTotalItems(orderDetails[i]);
+ }
+
+ return totalItems;
+ }
+
+ function getTotalItems(
+ OrderDetails memory order
+ ) internal pure returns (uint256) {
+ return (order.offer.length + order.consideration.length);
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/lib/MatchArrays.sol b/contracts/helpers/sol/fulfillments/lib/MatchArrays.sol
new file mode 100644
index 000000000..52c3168e4
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/lib/MatchArrays.sol
@@ -0,0 +1,3036 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+import "../../SeaportStructs.sol";
+import "../../lib/types/MatchComponentType.sol";
+
+library MatchArrays {
+ function FulfillmentComponents(
+ FulfillmentComponent memory a
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function FulfillmentComponents(
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function FulfillmentComponents(
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function FulfillmentComponents(
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function FulfillmentComponents(
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d,
+ FulfillmentComponent memory e
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function FulfillmentComponents(
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d,
+ FulfillmentComponent memory e,
+ FulfillmentComponent memory f
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function FulfillmentComponents(
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d,
+ FulfillmentComponent memory e,
+ FulfillmentComponent memory f,
+ FulfillmentComponent memory g
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 1)
+ }
+ arr[0] = a;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 2)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 3)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 4)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d,
+ FulfillmentComponent memory e
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 5)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d,
+ FulfillmentComponent memory e,
+ FulfillmentComponent memory f
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 6)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function FulfillmentComponentsWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent memory a,
+ FulfillmentComponent memory b,
+ FulfillmentComponent memory c,
+ FulfillmentComponent memory d,
+ FulfillmentComponent memory e,
+ FulfillmentComponent memory f,
+ FulfillmentComponent memory g
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[] memory arr = new FulfillmentComponent[](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 7)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function extend(
+ FulfillmentComponent[] memory arr1,
+ FulfillmentComponent[] memory arr2
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ uint256 length1 = arr1.length;
+ uint256 length2 = arr2.length;
+ newArr = new FulfillmentComponent[](length1 + length2);
+ for (uint256 i = 0; i < length1; ) {
+ newArr[i] = arr1[i];
+ unchecked {
+ ++i;
+ }
+ }
+ for (uint256 i = 0; i < arr2.length; ) {
+ uint256 j;
+ unchecked {
+ j = i + length1;
+ }
+ newArr[j] = arr2[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function allocateFulfillmentComponents(
+ uint256 length
+ ) internal pure returns (FulfillmentComponent[] memory arr) {
+ arr = new FulfillmentComponent[](length);
+ assembly {
+ mstore(arr, 0)
+ }
+ }
+
+ function truncate(
+ FulfillmentComponent[] memory arr,
+ uint256 newLength
+ ) internal pure returns (FulfillmentComponent[] memory _arr) {
+ // truncate the array
+ assembly {
+ let oldLength := mload(arr)
+ returndatacopy(
+ returndatasize(),
+ returndatasize(),
+ gt(newLength, oldLength)
+ )
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function truncateUnsafe(
+ FulfillmentComponent[] memory arr,
+ uint256 newLength
+ ) internal pure returns (FulfillmentComponent[] memory _arr) {
+ // truncate the array
+ assembly {
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function append(
+ FulfillmentComponent[] memory arr,
+ FulfillmentComponent memory value
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new FulfillmentComponent[](length + 1);
+ newArr[length] = value;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function appendUnsafe(
+ FulfillmentComponent[] memory arr,
+ FulfillmentComponent memory value
+ ) internal pure returns (FulfillmentComponent[] memory modifiedArr) {
+ uint256 length = arr.length;
+ modifiedArr = arr;
+ assembly {
+ mstore(modifiedArr, add(length, 1))
+ mstore(add(modifiedArr, shl(5, add(length, 1))), value)
+ }
+ }
+
+ function copy(
+ FulfillmentComponent[] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new FulfillmentComponent[](length);
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function copyAndResize(
+ FulfillmentComponent[] memory arr,
+ uint256 newLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](newLength);
+ uint256 length = arr.length;
+ // allow shrinking a copy without copying extra members
+ length = (length > newLength) ? newLength : length;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ // TODO: consider writing 0-pointer to the rest of the array if longer for dynamic elements
+ }
+
+ function copyAndAllocate(
+ FulfillmentComponent[] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ uint256 originalLength = arr.length;
+ for (uint256 i = 0; i < originalLength; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, originalLength)
+ }
+ }
+
+ function pop(
+ FulfillmentComponent[] memory arr
+ ) internal pure returns (FulfillmentComponent memory value) {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popUnsafe(
+ FulfillmentComponent[] memory arr
+ ) internal pure returns (FulfillmentComponent memory value) {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popLeft(
+ FulfillmentComponent[] memory arr
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[] memory newArr,
+ FulfillmentComponent memory value
+ )
+ {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function popLeftUnsafe(
+ FulfillmentComponent[] memory arr
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[] memory newArr,
+ FulfillmentComponent memory value
+ )
+ {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[1] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](1);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[1] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 1)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[2] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](2);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[2] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 2)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[3] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](3);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[3] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 3)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[4] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](4);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[4] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 4)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[5] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](5);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[5] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 5)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[6] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](6);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[6] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 6)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[7] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](7);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[7] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[] memory newArr) {
+ newArr = new FulfillmentComponent[](maxLength);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 7)
+ }
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d,
+ FulfillmentComponent[] memory e
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d,
+ FulfillmentComponent[] memory e,
+ FulfillmentComponent[] memory f
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function FulfillmentComponentArrays(
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d,
+ FulfillmentComponent[] memory e,
+ FulfillmentComponent[] memory f,
+ FulfillmentComponent[] memory g
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 1)
+ }
+ arr[0] = a;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 2)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 3)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 4)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d,
+ FulfillmentComponent[] memory e
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 5)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d,
+ FulfillmentComponent[] memory e,
+ FulfillmentComponent[] memory f
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 6)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function FulfillmentComponentArraysWithMaxLength(
+ uint256 maxLength,
+ FulfillmentComponent[] memory a,
+ FulfillmentComponent[] memory b,
+ FulfillmentComponent[] memory c,
+ FulfillmentComponent[] memory d,
+ FulfillmentComponent[] memory e,
+ FulfillmentComponent[] memory f,
+ FulfillmentComponent[] memory g
+ ) internal pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][] memory arr = new FulfillmentComponent[][](
+ maxLength
+ );
+ assembly {
+ mstore(arr, 7)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function extend(
+ FulfillmentComponent[][] memory arr1,
+ FulfillmentComponent[][] memory arr2
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ uint256 length1 = arr1.length;
+ uint256 length2 = arr2.length;
+ newArr = new FulfillmentComponent[][](length1 + length2);
+ for (uint256 i = 0; i < length1; ) {
+ newArr[i] = arr1[i];
+ unchecked {
+ ++i;
+ }
+ }
+ for (uint256 i = 0; i < arr2.length; ) {
+ uint256 j;
+ unchecked {
+ j = i + length1;
+ }
+ newArr[j] = arr2[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function allocateFulfillmentComponentArrays(
+ uint256 length
+ ) internal pure returns (FulfillmentComponent[][] memory arr) {
+ arr = new FulfillmentComponent[][](length);
+ assembly {
+ mstore(arr, 0)
+ }
+ }
+
+ function truncate(
+ FulfillmentComponent[][] memory arr,
+ uint256 newLength
+ ) internal pure returns (FulfillmentComponent[][] memory _arr) {
+ // truncate the array
+ assembly {
+ let oldLength := mload(arr)
+ returndatacopy(
+ returndatasize(),
+ returndatasize(),
+ gt(newLength, oldLength)
+ )
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function truncateUnsafe(
+ FulfillmentComponent[][] memory arr,
+ uint256 newLength
+ ) internal pure returns (FulfillmentComponent[][] memory _arr) {
+ // truncate the array
+ assembly {
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function append(
+ FulfillmentComponent[][] memory arr,
+ FulfillmentComponent[] memory value
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new FulfillmentComponent[][](length + 1);
+ newArr[length] = value;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function appendUnsafe(
+ FulfillmentComponent[][] memory arr,
+ FulfillmentComponent[] memory value
+ ) internal pure returns (FulfillmentComponent[][] memory modifiedArr) {
+ uint256 length = arr.length;
+ modifiedArr = arr;
+ assembly {
+ mstore(modifiedArr, add(length, 1))
+ mstore(add(modifiedArr, shl(5, add(length, 1))), value)
+ }
+ }
+
+ function copy(
+ FulfillmentComponent[][] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new FulfillmentComponent[][](length);
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function copyAndResize(
+ FulfillmentComponent[][] memory arr,
+ uint256 newLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](newLength);
+ uint256 length = arr.length;
+ // allow shrinking a copy without copying extra members
+ length = (length > newLength) ? newLength : length;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ // TODO: consider writing 0-pointer to the rest of the array if longer for dynamic elements
+ }
+
+ function copyAndAllocate(
+ FulfillmentComponent[][] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ uint256 originalLength = arr.length;
+ for (uint256 i = 0; i < originalLength; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, originalLength)
+ }
+ }
+
+ function pop(
+ FulfillmentComponent[][] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory value) {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popUnsafe(
+ FulfillmentComponent[][] memory arr
+ ) internal pure returns (FulfillmentComponent[] memory value) {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popLeft(
+ FulfillmentComponent[][] memory arr
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[][] memory newArr,
+ FulfillmentComponent[] memory value
+ )
+ {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function popLeftUnsafe(
+ FulfillmentComponent[][] memory arr
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[][] memory newArr,
+ FulfillmentComponent[] memory value
+ )
+ {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][1] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](1);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][1] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 1)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][2] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](2);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][2] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 2)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][3] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](3);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][3] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 3)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][4] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](4);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][4] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 4)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][5] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](5);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][5] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 5)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][6] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](6);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][6] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 6)
+ }
+ }
+
+ function fromFixed(
+ FulfillmentComponent[][7] memory arr
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](7);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ FulfillmentComponent[][7] memory arr,
+ uint256 maxLength
+ ) internal pure returns (FulfillmentComponent[][] memory newArr) {
+ newArr = new FulfillmentComponent[][](maxLength);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 7)
+ }
+ }
+
+ function Fulfillments(
+ Fulfillment memory a
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function Fulfillments(
+ Fulfillment memory a,
+ Fulfillment memory b
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function Fulfillments(
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function Fulfillments(
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function Fulfillments(
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d,
+ Fulfillment memory e
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function Fulfillments(
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d,
+ Fulfillment memory e,
+ Fulfillment memory f
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function Fulfillments(
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d,
+ Fulfillment memory e,
+ Fulfillment memory f,
+ Fulfillment memory g
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 1)
+ }
+ arr[0] = a;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a,
+ Fulfillment memory b
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 2)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 3)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 4)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d,
+ Fulfillment memory e
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 5)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d,
+ Fulfillment memory e,
+ Fulfillment memory f
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 6)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function FulfillmentsWithMaxLength(
+ uint256 maxLength,
+ Fulfillment memory a,
+ Fulfillment memory b,
+ Fulfillment memory c,
+ Fulfillment memory d,
+ Fulfillment memory e,
+ Fulfillment memory f,
+ Fulfillment memory g
+ ) internal pure returns (Fulfillment[] memory) {
+ Fulfillment[] memory arr = new Fulfillment[](maxLength);
+ assembly {
+ mstore(arr, 7)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function extend(
+ Fulfillment[] memory arr1,
+ Fulfillment[] memory arr2
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ uint256 length1 = arr1.length;
+ uint256 length2 = arr2.length;
+ newArr = new Fulfillment[](length1 + length2);
+ for (uint256 i = 0; i < length1; ) {
+ newArr[i] = arr1[i];
+ unchecked {
+ ++i;
+ }
+ }
+ for (uint256 i = 0; i < arr2.length; ) {
+ uint256 j;
+ unchecked {
+ j = i + length1;
+ }
+ newArr[j] = arr2[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function allocateFulfillments(
+ uint256 length
+ ) internal pure returns (Fulfillment[] memory arr) {
+ arr = new Fulfillment[](length);
+ assembly {
+ mstore(arr, 0)
+ }
+ }
+
+ function truncate(
+ Fulfillment[] memory arr,
+ uint256 newLength
+ ) internal pure returns (Fulfillment[] memory _arr) {
+ // truncate the array
+ assembly {
+ let oldLength := mload(arr)
+ returndatacopy(
+ returndatasize(),
+ returndatasize(),
+ gt(newLength, oldLength)
+ )
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function truncateUnsafe(
+ Fulfillment[] memory arr,
+ uint256 newLength
+ ) internal pure returns (Fulfillment[] memory _arr) {
+ // truncate the array
+ assembly {
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function append(
+ Fulfillment[] memory arr,
+ Fulfillment memory value
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new Fulfillment[](length + 1);
+ newArr[length] = value;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function appendUnsafe(
+ Fulfillment[] memory arr,
+ Fulfillment memory value
+ ) internal pure returns (Fulfillment[] memory modifiedArr) {
+ uint256 length = arr.length;
+ modifiedArr = arr;
+ assembly {
+ mstore(modifiedArr, add(length, 1))
+ mstore(add(modifiedArr, shl(5, add(length, 1))), value)
+ }
+ }
+
+ function copy(
+ Fulfillment[] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new Fulfillment[](length);
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function copyAndResize(
+ Fulfillment[] memory arr,
+ uint256 newLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](newLength);
+ uint256 length = arr.length;
+ // allow shrinking a copy without copying extra members
+ length = (length > newLength) ? newLength : length;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ // TODO: consider writing 0-pointer to the rest of the array if longer for dynamic elements
+ }
+
+ function copyAndAllocate(
+ Fulfillment[] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ uint256 originalLength = arr.length;
+ for (uint256 i = 0; i < originalLength; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, originalLength)
+ }
+ }
+
+ function pop(
+ Fulfillment[] memory arr
+ ) internal pure returns (Fulfillment memory value) {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popUnsafe(
+ Fulfillment[] memory arr
+ ) internal pure returns (Fulfillment memory value) {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popLeft(
+ Fulfillment[] memory arr
+ )
+ internal
+ pure
+ returns (Fulfillment[] memory newArr, Fulfillment memory value)
+ {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function popLeftUnsafe(
+ Fulfillment[] memory arr
+ )
+ internal
+ pure
+ returns (Fulfillment[] memory newArr, Fulfillment memory value)
+ {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[1] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](1);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[1] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 1)
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[2] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](2);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[2] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 2)
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[3] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](3);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[3] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 3)
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[4] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](4);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[4] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 4)
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[5] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](5);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[5] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 5)
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[6] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](6);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[6] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 6)
+ }
+ }
+
+ function fromFixed(
+ Fulfillment[7] memory arr
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](7);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ Fulfillment[7] memory arr,
+ uint256 maxLength
+ ) internal pure returns (Fulfillment[] memory newArr) {
+ newArr = new Fulfillment[](maxLength);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 7)
+ }
+ }
+
+ function MatchComponents(
+ MatchComponent memory a
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function MatchComponents(
+ MatchComponent memory a,
+ MatchComponent memory b
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function MatchComponents(
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function MatchComponents(
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function MatchComponents(
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d,
+ MatchComponent memory e
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function MatchComponents(
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d,
+ MatchComponent memory e,
+ MatchComponent memory f
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function MatchComponents(
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d,
+ MatchComponent memory e,
+ MatchComponent memory f,
+ MatchComponent memory g
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 1)
+ }
+ arr[0] = a;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a,
+ MatchComponent memory b
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 2)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 3)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 4)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d,
+ MatchComponent memory e
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 5)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d,
+ MatchComponent memory e,
+ MatchComponent memory f
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 6)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function MatchComponentsWithMaxLength(
+ uint256 maxLength,
+ MatchComponent memory a,
+ MatchComponent memory b,
+ MatchComponent memory c,
+ MatchComponent memory d,
+ MatchComponent memory e,
+ MatchComponent memory f,
+ MatchComponent memory g
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory arr = new MatchComponent[](maxLength);
+ assembly {
+ mstore(arr, 7)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function extend(
+ MatchComponent[] memory arr1,
+ MatchComponent[] memory arr2
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ uint256 length1 = arr1.length;
+ uint256 length2 = arr2.length;
+ newArr = new MatchComponent[](length1 + length2);
+ for (uint256 i = 0; i < length1; ) {
+ newArr[i] = arr1[i];
+ unchecked {
+ ++i;
+ }
+ }
+ for (uint256 i = 0; i < arr2.length; ) {
+ uint256 j;
+ unchecked {
+ j = i + length1;
+ }
+ newArr[j] = arr2[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function allocateMatchComponents(
+ uint256 length
+ ) internal pure returns (MatchComponent[] memory arr) {
+ arr = new MatchComponent[](length);
+ assembly {
+ mstore(arr, 0)
+ }
+ }
+
+ function truncate(
+ MatchComponent[] memory arr,
+ uint256 newLength
+ ) internal pure returns (MatchComponent[] memory _arr) {
+ // truncate the array
+ assembly {
+ let oldLength := mload(arr)
+ returndatacopy(
+ returndatasize(),
+ returndatasize(),
+ gt(newLength, oldLength)
+ )
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function truncateUnsafe(
+ MatchComponent[] memory arr,
+ uint256 newLength
+ ) internal pure returns (MatchComponent[] memory _arr) {
+ // truncate the array
+ assembly {
+ mstore(arr, newLength)
+ _arr := arr
+ }
+ }
+
+ function append(
+ MatchComponent[] memory arr,
+ MatchComponent memory value
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new MatchComponent[](length + 1);
+ newArr[length] = value;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function appendUnsafe(
+ MatchComponent[] memory arr,
+ MatchComponent memory value
+ ) internal pure returns (MatchComponent[] memory modifiedArr) {
+ uint256 length = arr.length;
+ modifiedArr = arr;
+ assembly {
+ mstore(modifiedArr, add(length, 1))
+ mstore(add(modifiedArr, shl(5, add(length, 1))), value)
+ }
+ }
+
+ function copy(
+ MatchComponent[] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ uint256 length = arr.length;
+ newArr = new MatchComponent[](length);
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function copyAndResize(
+ MatchComponent[] memory arr,
+ uint256 newLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](newLength);
+ uint256 length = arr.length;
+ // allow shrinking a copy without copying extra members
+ length = (length > newLength) ? newLength : length;
+ for (uint256 i = 0; i < length; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ // TODO: consider writing 0-pointer to the rest of the array if longer for dynamic elements
+ }
+
+ function copyAndAllocate(
+ MatchComponent[] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ uint256 originalLength = arr.length;
+ for (uint256 i = 0; i < originalLength; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, originalLength)
+ }
+ }
+
+ function pop(
+ MatchComponent[] memory arr
+ ) internal pure returns (MatchComponent memory value) {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popUnsafe(
+ MatchComponent[] memory arr
+ ) internal pure returns (MatchComponent memory value) {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, shl(5, length)))
+ mstore(arr, sub(length, 1))
+ }
+ }
+
+ function popLeft(
+ MatchComponent[] memory arr
+ )
+ internal
+ pure
+ returns (MatchComponent[] memory newArr, MatchComponent memory value)
+ {
+ assembly {
+ let length := mload(arr)
+ returndatacopy(returndatasize(), returndatasize(), iszero(length))
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function popLeftUnsafe(
+ MatchComponent[] memory arr
+ )
+ internal
+ pure
+ returns (MatchComponent[] memory newArr, MatchComponent memory value)
+ {
+ // This function is unsafe because it does not check if the array is empty.
+ assembly {
+ let length := mload(arr)
+ value := mload(add(arr, 0x20))
+ newArr := add(arr, 0x20)
+ mstore(newArr, sub(length, 1))
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[1] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](1);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[1] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 1; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 1)
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[2] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](2);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[2] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 2; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 2)
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[3] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](3);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[3] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 3; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 3)
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[4] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](4);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[4] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 4; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 4)
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[5] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](5);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[5] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 5; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 5)
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[6] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](6);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[6] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 6; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 6)
+ }
+ }
+
+ function fromFixed(
+ MatchComponent[7] memory arr
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](7);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function fromFixedWithMaxLength(
+ MatchComponent[7] memory arr,
+ uint256 maxLength
+ ) internal pure returns (MatchComponent[] memory newArr) {
+ newArr = new MatchComponent[](maxLength);
+ for (uint256 i = 0; i < 7; ) {
+ newArr[i] = arr[i];
+ unchecked {
+ ++i;
+ }
+ }
+ assembly {
+ mstore(newArr, 7)
+ }
+ }
+
+ function uints(uint a) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function uints(uint a, uint b) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uints(
+ uint a,
+ uint b,
+ uint c
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uints(
+ uint a,
+ uint b,
+ uint c,
+ uint d
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uints(
+ uint a,
+ uint b,
+ uint c,
+ uint d,
+ uint e
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uints(
+ uint a,
+ uint b,
+ uint c,
+ uint d,
+ uint e,
+ uint f
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uints(
+ uint a,
+ uint b,
+ uint c,
+ uint d,
+ uint e,
+ uint f,
+ uint g
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 1)
+ }
+ arr[0] = a;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a,
+ uint b
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 2)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a,
+ uint b,
+ uint c
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 3)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a,
+ uint b,
+ uint c,
+ uint d
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 4)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a,
+ uint b,
+ uint c,
+ uint d,
+ uint e
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 5)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a,
+ uint b,
+ uint c,
+ uint d,
+ uint e,
+ uint f
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 6)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function uintsWithMaxLength(
+ uint256 maxLength,
+ uint a,
+ uint b,
+ uint c,
+ uint d,
+ uint e,
+ uint f,
+ uint g
+ ) internal pure returns (uint[] memory) {
+ uint[] memory arr = new uint[](maxLength);
+ assembly {
+ mstore(arr, 7)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function allocateUints(
+ uint256 length
+ ) internal pure returns (uint[] memory arr) {
+ arr = new uint[](length);
+ assembly {
+ mstore(arr, 0)
+ }
+ }
+
+ function ints(int a) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function ints(int a, int b) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function ints(int a, int b, int c) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function ints(
+ int a,
+ int b,
+ int c,
+ int d
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function ints(
+ int a,
+ int b,
+ int c,
+ int d,
+ int e
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function ints(
+ int a,
+ int b,
+ int c,
+ int d,
+ int e,
+ int f
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function ints(
+ int a,
+ int b,
+ int c,
+ int d,
+ int e,
+ int f,
+ int g
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 1)
+ }
+ arr[0] = a;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a,
+ int b
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 2)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a,
+ int b,
+ int c
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 3)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a,
+ int b,
+ int c,
+ int d
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 4)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a,
+ int b,
+ int c,
+ int d,
+ int e
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 5)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a,
+ int b,
+ int c,
+ int d,
+ int e,
+ int f
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 6)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function intsWithMaxLength(
+ uint256 maxLength,
+ int a,
+ int b,
+ int c,
+ int d,
+ int e,
+ int f,
+ int g
+ ) internal pure returns (int[] memory) {
+ int[] memory arr = new int[](maxLength);
+ assembly {
+ mstore(arr, 7)
+ }
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function allocateInts(
+ uint256 length
+ ) internal pure returns (int[] memory arr) {
+ arr = new int[](length);
+ assembly {
+ mstore(arr, 0)
+ }
+ }
+
+ function amountKey(
+ MatchComponent memory component
+ ) internal pure returns (uint256) {
+ return component.amount;
+ }
+
+ function indexKey(
+ MatchComponent memory component
+ ) internal pure returns (uint256) {
+ return (component.orderIndex << 8) | component.itemIndex;
+ }
+
+ function sortByAmount(MatchComponent[] memory components) internal pure {
+ sort(components, amountKey);
+ }
+
+ function sortByIndex(MatchComponent[] memory components) internal pure {
+ sort(components, indexKey);
+ }
+
+ // Sorts the array in-place with intro-quicksort.
+ function sort(
+ MatchComponent[] memory a,
+ function(MatchComponent memory) internal pure returns (uint256) accessor
+ ) internal pure {
+ if (a.length < 2) {
+ return;
+ }
+
+ uint256[] memory stack = new uint256[](2 * a.length);
+ uint256 stackIndex = 0;
+
+ uint256 l = 0;
+ uint256 h = a.length - 1;
+
+ stack[stackIndex++] = l;
+ stack[stackIndex++] = h;
+
+ while (stackIndex > 0) {
+ h = stack[--stackIndex];
+ l = stack[--stackIndex];
+
+ if (h - l <= 12) {
+ // Insertion sort for small subarrays
+ for (uint256 i = l + 1; i <= h; i++) {
+ MatchComponent memory k = a[i];
+ uint256 j = i;
+ while (j > l && accessor(a[j - 1]) > accessor(k)) {
+ a[j] = a[j - 1];
+ j--;
+ }
+ a[j] = k;
+ }
+ } else {
+ // Intro-Quicksort
+ uint256 p = (l + h) / 2;
+
+ // Median of 3
+ if (accessor(a[l]) > accessor(a[p])) {
+ (a[l], a[p]) = (a[p], a[l]);
+ }
+ if (accessor(a[l]) > accessor(a[h])) {
+ (a[l], a[h]) = (a[h], a[l]);
+ }
+ if (accessor(a[p]) > accessor(a[h])) {
+ (a[p], a[h]) = (a[h], a[p]);
+ }
+
+ uint256 pivot = accessor(a[p]);
+ uint256 i = l;
+ uint256 j = h;
+
+ while (i <= j) {
+ while (accessor(a[i]) < pivot) {
+ i++;
+ }
+ while (accessor(a[j]) > pivot) {
+ j--;
+ }
+ if (i <= j) {
+ (a[i], a[j]) = (a[j], a[i]);
+ i++;
+ j--;
+ }
+ }
+
+ if (j > l) {
+ stack[stackIndex++] = l;
+ stack[stackIndex++] = j;
+ }
+ if (i < h) {
+ stack[stackIndex++] = i;
+ stack[stackIndex++] = h;
+ }
+ }
+ }
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/lib/Structs.sol b/contracts/helpers/sol/fulfillments/lib/Structs.sol
new file mode 100644
index 000000000..acd9e539d
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/lib/Structs.sol
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ MatchComponent,
+ MatchComponentType
+} from "../../lib/types/MatchComponentType.sol";
+
+import {
+ FulfillmentComponent,
+ SpentItem,
+ ReceivedItem
+} from "../../SeaportStructs.sol";
+
+import { UnavailableReason } from "../../SpaceEnums.sol";
+
+struct FulfillmentHelperCounterLayout {
+ uint256 fulfillmentCounter;
+}
+
+// TODO: won't work for partial fulfills of criteria resolved
+// TODO: won't work for hybrid tokens that implement multiple token interfaces
+struct MatchFulfillmentStorageLayout {
+ mapping(address /*tokenContract*/ => mapping(uint256 /*identifier*/ => mapping(address /*offerer*/ => mapping(bytes32 /*conduitKey*/ => MatchComponent[] /*components*/)))) offerMap;
+ mapping(address /*recipient*/ => mapping(address /*tokenContract*/ => mapping(uint256 /*identifier*/ => MatchComponent[] /*components*/))) considerationMap;
+ // a given aggregatable consideration component will have its own set of aggregatable offer components
+ mapping(address /*token*/ => mapping(uint256 /*tokenId*/ => AggregatableOfferer[] /*offererEnumeration*/)) tokenToOffererEnumeration;
+ // aggregatable consideration components can be enumerated normally
+ AggregatableConsideration[] considerationEnumeration;
+}
+
+struct FulfillAvailableHelperStorageLayout {
+ mapping(address /*tokenContract*/ => mapping(uint256 /*identifier*/ => mapping(address /*offerer*/ => mapping(bytes32 /*conduitKey*/ => FulfillmentComponent[] /*components*/)))) offerMap;
+ mapping(address /*recipient*/ => mapping(address /*tokenContract*/ => mapping(uint256 /*identifier*/ => FulfillmentComponent[] /*components*/))) considerationMap;
+ // a given aggregatable consideration component will have its own set of aggregatable offer components
+ AggregatableOffer[] offerEnumeration;
+ // aggregatable consideration components can be enumerated normally
+ AggregatableConsideration[] considerationEnumeration;
+}
+
+/**
+ * @notice Offers can only be aggregated if they share an offerer *and* conduitKey
+ */
+struct AggregatableOfferer {
+ address offerer;
+ bytes32 conduitKey;
+}
+
+struct AggregatableOffer {
+ address offerer;
+ bytes32 conduitKey;
+ address contractAddress;
+ uint256 tokenId;
+}
+/**
+ *
+ * @notice Considerations can only be aggregated if they share a token address, id, and recipient (and itemType, but in the vast majority of cases, a token is only one type)
+ */
+
+struct AggregatableConsideration {
+ address recipient;
+ address contractAddress;
+ uint256 tokenId;
+}
+
+struct ProcessComponentParams {
+ FulfillmentComponent[] offerFulfillmentComponents;
+ FulfillmentComponent[] considerationFulfillmentComponents;
+ uint256 offerItemIndex;
+ uint256 considerationItemIndex;
+ bool midCredit;
+}
+
+struct OrderDetails {
+ address offerer;
+ bytes32 conduitKey;
+ SpentItem[] offer;
+ ReceivedItem[] consideration;
+ bool isContract;
+ bytes32 orderHash;
+ UnavailableReason unavailableReason;
+}
+
+/**
+ * @dev Represents the details of a single fulfill/match call to Seaport.
+ *
+ * @param orders processed details of individual orders
+ * @param recipient the explicit recipient of all offer items in
+ * the fulfillAvailable case; implicit recipient
+ * of excess offer items in the match case
+ * @param fulfiller the explicit recipient of all unspent native
+ * tokens; provides all consideration items in
+ * the fulfillAvailable case
+ * @param fulfillerConduitKey used to transfer tokens from the fulfiller
+ * providing all consideration items in the
+ * fulfillAvailable case
+ */
+struct FulfillmentDetails {
+ OrderDetails[] orders;
+ address payable recipient;
+ address payable fulfiller;
+ uint256 nativeTokensSupplied;
+ bytes32 fulfillerConduitKey;
+ address seaport;
+}
diff --git a/contracts/helpers/sol/fulfillments/match/MatchFulfillmentHelper.sol b/contracts/helpers/sol/fulfillments/match/MatchFulfillmentHelper.sol
new file mode 100644
index 000000000..be3ae1389
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/match/MatchFulfillmentHelper.sol
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AggregatableConsideration,
+ ProcessComponentParams,
+ AggregatableOfferer,
+ OrderDetails,
+ MatchFulfillmentStorageLayout
+} from "../lib/Structs.sol";
+import {
+ MatchComponent,
+ MatchComponentType
+} from "../../lib/types/MatchComponentType.sol";
+import {
+ FulfillmentComponent,
+ Fulfillment,
+ Order,
+ AdvancedOrder,
+ OrderParameters,
+ SpentItem,
+ ReceivedItem,
+ CriteriaResolver
+} from "../../SeaportStructs.sol";
+import { UnavailableReason } from "../../SpaceEnums.sol";
+import { MatchFulfillmentLib } from "./MatchFulfillmentLib.sol";
+import { MatchFulfillmentLayout } from "./MatchFulfillmentLayout.sol";
+
+import {
+ AmountDeriverHelper
+} from "../../lib/fulfillment/AmountDeriverHelper.sol";
+import { MatchArrays } from "../lib/MatchArrays.sol";
+
+contract MatchFulfillmentHelper is AmountDeriverHelper {
+ /**
+ * @notice Generate matched fulfillments for a list of orders
+ * NOTE: this will break for multiple criteria items that resolve
+ * to different identifiers
+ * @param orders orders
+ * @return fulfillments
+ */
+ function getMatchedFulfillments(
+ Order[] memory orders,
+ bytes32[] memory orderHashes,
+ UnavailableReason[] memory unavailableReasons
+ )
+ public
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents,
+ MatchComponent[] memory remainingConsiderationComponents
+ )
+ {
+ OrderDetails[] memory orderDetails = toOrderDetails(
+ orders,
+ orderHashes,
+ unavailableReasons
+ );
+
+ return getMatchedFulfillments(orderDetails);
+ }
+
+ /**
+ * @notice Generate matched fulfillments for a list of orders
+ * NOTE: this will break for multiple criteria items that resolve
+ * to different identifiers
+ * @param orders orders
+ * @param resolvers resolvers
+ * @return fulfillments
+ */
+ function getMatchedFulfillments(
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ bytes32[] memory orderHashes,
+ UnavailableReason[] memory unavailableReasons
+ )
+ public
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents,
+ MatchComponent[] memory remainingConsiderationComponents
+ )
+ {
+ OrderDetails[] memory details = toOrderDetails(
+ orders,
+ resolvers,
+ orderHashes,
+ unavailableReasons
+ );
+ return getMatchedFulfillments(details);
+ }
+
+ /**
+ * @notice Generate matched fulfillments for a list of orders
+ * NOTE: this will break for multiple criteria items that resolve
+ * to different identifiers
+ * @param orders orders
+ * @return fulfillments
+ */
+ function getMatchedFulfillments(
+ OrderDetails[] memory orders
+ )
+ public
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents,
+ MatchComponent[] memory remainingConsiderationComponents
+ )
+ {
+ // increment counter to get clean mappings and enumeration
+ MatchFulfillmentLayout.incrementFulfillmentCounter();
+ // load the storage layout
+ MatchFulfillmentStorageLayout storage layout = MatchFulfillmentLayout
+ .getStorageLayout();
+
+ // iterate over each order and process the offer and consideration components
+ for (uint256 i; i < orders.length; ++i) {
+ OrderDetails memory details = orders[i];
+
+ // insert MatchComponents into the offer mapping, grouped by token, tokenId, offerer, and conduitKey
+ // also update per-token+tokenId enumerations of AggregatableOfferer
+
+ preProcessSpentItems(
+ details.offer,
+ details.offerer,
+ details.conduitKey,
+ i,
+ layout
+ );
+ // insert MatchComponents into the offer mapping, grouped by token, tokenId, and recipient
+ // also update AggregatableConsideration enumeration
+ preProcessSpentItems(details.consideration, i, layout);
+ }
+
+ // iterate over groups of consideration components and find matching offer components
+ uint256 considerationLength = layout.considerationEnumeration.length;
+ for (uint256 i; i < considerationLength; ++i) {
+ // get the token information
+ AggregatableConsideration storage token = layout
+ .considerationEnumeration[i];
+ // load the consideration components
+ MatchComponent[] storage considerationComponents = layout
+ .considerationMap[token.recipient][token.contractAddress][
+ token.tokenId
+ ];
+ // load the enumeration of offerer+conduit keys for offer components that match this token
+ AggregatableOfferer[] storage offererEnumeration = layout
+ .tokenToOffererEnumeration[token.contractAddress][
+ token.tokenId
+ ];
+ // iterate over each offerer+conduit with offer components that match this token and create matching fulfillments
+ // this will update considerationComponents in-place in storage, which we check at the beginning of each loop
+ for (uint256 j; j < offererEnumeration.length; ++j) {
+ // if all consideration components have been fulfilled, break
+ if (considerationComponents.length == 0) {
+ break;
+ }
+ // load the AggregatableOfferer
+ AggregatableOfferer
+ storage aggregatableOfferer = offererEnumeration[j];
+ // load the associated offer components for this offerer+conduit
+ MatchComponent[] storage offerComponents = layout.offerMap[
+ token.contractAddress
+ ][token.tokenId][aggregatableOfferer.offerer][
+ aggregatableOfferer.conduitKey
+ ];
+ // if there are no offer components left, continue
+ // TODO: remove from enumeration?
+ if (offerComponents.length == 0) {
+ continue;
+ }
+
+ // create a fulfillment matching the offer and consideration components until either or both are exhausted
+ Fulfillment memory fulfillment = MatchFulfillmentLib
+ .createFulfillment(
+ offerComponents,
+ considerationComponents
+ );
+ // append the fulfillment to the array of fulfillments
+ fulfillments = MatchArrays.append(fulfillments, fulfillment);
+ // loop back around in case not all considerationComponents have been completely fulfilled
+ }
+ }
+
+ // get any remaining offer components
+ for (uint256 i; i < orders.length; ++i) {
+ OrderDetails memory details = orders[i];
+
+ // insert MatchComponents into the offer mapping, grouped by token, tokenId, offerer, and conduitKey
+ // also update per-token+tokenId enumerations of AggregatableOfferer
+ remainingOfferComponents = MatchArrays.extend(
+ remainingOfferComponents,
+ postProcessSpentItems(
+ details.offer,
+ details.offerer,
+ details.conduitKey,
+ layout
+ )
+ );
+
+ remainingConsiderationComponents = MatchArrays.extend(
+ remainingConsiderationComponents,
+ postProcessReceivedItems(details.consideration, layout)
+ );
+ }
+ remainingOfferComponents = MatchFulfillmentLib.dedupe(
+ remainingOfferComponents
+ );
+ remainingConsiderationComponents = MatchFulfillmentLib.dedupe(
+ remainingConsiderationComponents
+ );
+ }
+
+ /**
+ * @notice Process offer items and insert them into enumeration and map
+ * @param offer offer items
+ * @param offerer offerer
+ * @param orderIndex order index of processed items
+ * @param layout storage layout of helper
+ */
+ function preProcessSpentItems(
+ SpentItem[] memory offer,
+ address offerer,
+ bytes32 conduitKey,
+ uint256 orderIndex,
+ MatchFulfillmentStorageLayout storage layout
+ ) private {
+ // iterate over each offer item
+ for (uint256 j; j < offer.length; ++j) {
+ // grab offer item
+ // TODO: spentItems?
+ SpentItem memory item = offer[j];
+ MatchComponent memory component = MatchComponentType
+ .createMatchComponent({
+ amount: uint240(item.amount),
+ orderIndex: uint8(orderIndex),
+ itemIndex: uint8(j)
+ });
+ AggregatableOfferer
+ memory aggregatableOfferer = AggregatableOfferer({
+ offerer: offerer,
+ conduitKey: conduitKey
+ });
+
+ // if it does not exist in the map, add it to our per-token+id enumeration
+ if (
+ !MatchFulfillmentLib.aggregatableOffererExists(
+ item.token,
+ item.identifier,
+ aggregatableOfferer,
+ layout
+ )
+ ) {
+ // add to enumeration for specific tokenhash (tokenAddress+tokenId)
+ layout
+ .tokenToOffererEnumeration[item.token][item.identifier].push(
+ aggregatableOfferer
+ );
+ }
+ // update aggregatable mapping array with this component
+ layout
+ .offerMap[item.token][item.identifier][offerer][conduitKey].push(
+ component
+ );
+ }
+ }
+
+ function postProcessSpentItems(
+ SpentItem[] memory offer,
+ address offerer,
+ bytes32 conduitKey,
+ MatchFulfillmentStorageLayout storage layout
+ ) private view returns (MatchComponent[] memory remainingOfferComponents) {
+ // iterate over each offer item
+ for (uint256 j; j < offer.length; ++j) {
+ // grab offer item
+ // TODO: spentItems?
+ SpentItem memory item = offer[j];
+
+ // update aggregatable mapping array with this component
+ remainingOfferComponents = MatchArrays.extend(
+ remainingOfferComponents,
+ layout.offerMap[item.token][item.identifier][offerer][
+ conduitKey
+ ]
+ );
+ }
+ }
+
+ /**
+ * @notice Process consideration items and insert them into enumeration and map
+ * @param consideration consideration items
+ * @param orderIndex order index of processed items
+ * @param layout storage layout of helper
+ */
+ function preProcessSpentItems(
+ ReceivedItem[] memory consideration,
+ uint256 orderIndex,
+ MatchFulfillmentStorageLayout storage layout
+ ) private {
+ // iterate over each consideration item
+ for (uint256 j; j < consideration.length; ++j) {
+ // grab consideration item
+ ReceivedItem memory item = consideration[j];
+ // TODO: use receivedItem here?
+ MatchComponent memory component = MatchComponentType
+ .createMatchComponent({
+ amount: uint240(item.amount),
+ orderIndex: uint8(orderIndex),
+ itemIndex: uint8(j)
+ });
+ // create enumeration struct
+ AggregatableConsideration memory token = AggregatableConsideration({
+ recipient: item.recipient,
+ contractAddress: item.token,
+ tokenId: item.identifier
+ });
+ // if it does not exist in the map, add it to our enumeration
+ if (
+ !MatchFulfillmentLib.aggregatableConsiderationExists(
+ token,
+ layout
+ )
+ ) {
+ layout.considerationEnumeration.push(token);
+ }
+ // update mapping with this component
+ layout
+ .considerationMap[token.recipient][token.contractAddress][
+ token.tokenId
+ ].push(component);
+ }
+ }
+
+ function postProcessReceivedItems(
+ ReceivedItem[] memory consideration,
+ MatchFulfillmentStorageLayout storage layout
+ )
+ private
+ view
+ returns (MatchComponent[] memory remainingConsiderationComponents)
+ {
+ // iterate over each consideration item
+ for (uint256 j; j < consideration.length; ++j) {
+ // grab consideration item
+ ReceivedItem memory item = consideration[j];
+
+ remainingConsiderationComponents = MatchArrays.extend(
+ remainingConsiderationComponents,
+ layout.considerationMap[item.recipient][item.token][
+ item.identifier
+ ]
+ );
+ }
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/match/MatchFulfillmentLayout.sol b/contracts/helpers/sol/fulfillments/match/MatchFulfillmentLayout.sol
new file mode 100644
index 000000000..c0d06ed68
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/match/MatchFulfillmentLayout.sol
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ MatchComponent,
+ MatchComponentType
+} from "../../lib/types/MatchComponentType.sol";
+import {
+ MatchFulfillmentStorageLayout,
+ FulfillmentHelperCounterLayout,
+ AggregatableConsideration
+} from "../lib/Structs.sol";
+import {
+ MATCH_FULFILLMENT_COUNTER_KEY,
+ MATCH_FULFILLMENT_STORAGE_BASE_KEY
+} from "../lib/Constants.sol";
+
+library MatchFulfillmentLayout {
+ /**
+ * @notice load storage layout for the current fulfillmentCounter
+ */
+ function getStorageLayout()
+ internal
+ view
+ returns (MatchFulfillmentStorageLayout storage layout)
+ {
+ FulfillmentHelperCounterLayout
+ storage counterLayout = getCounterLayout();
+ uint256 counter = counterLayout.fulfillmentCounter;
+ bytes32 storageLayoutKey = MATCH_FULFILLMENT_STORAGE_BASE_KEY;
+ assembly {
+ mstore(0, counter)
+ mstore(0x20, storageLayoutKey)
+ layout.slot := keccak256(0, 0x40)
+ }
+ }
+
+ /**
+ * @notice load storage layout for the counter itself
+ */
+ function getCounterLayout()
+ internal
+ pure
+ returns (FulfillmentHelperCounterLayout storage layout)
+ {
+ bytes32 counterLayoutKey = MATCH_FULFILLMENT_COUNTER_KEY;
+ assembly {
+ layout.slot := counterLayoutKey
+ }
+ }
+
+ /**
+ * @notice increment the fulfillmentCounter to effectively clear the mappings and enumerations between calls
+ */
+ function incrementFulfillmentCounter() internal {
+ FulfillmentHelperCounterLayout
+ storage counterLayout = getCounterLayout();
+ counterLayout.fulfillmentCounter += 1;
+ }
+
+ /**
+ * @notice Get the mapping of tokens for a given key (offer or consideration), derived from the hash of the key and the current fulfillmentCounter value
+ * @param key Original key used to derive the slot of the enumeration
+ */
+ function getMap(
+ bytes32 key
+ )
+ internal
+ view
+ returns (
+ mapping(address /*offererOrRecipient*/ => mapping(address /*tokenContract*/ => mapping(uint256 /*identifier*/ => MatchComponent[] /*components*/)))
+ storage map
+ )
+ {
+ bytes32 counterKey = MATCH_FULFILLMENT_COUNTER_KEY;
+ assembly {
+ mstore(0, key)
+ mstore(0x20, sload(counterKey))
+ map.slot := keccak256(0, 0x40)
+ }
+ }
+
+ /**
+ * @notice Get the enumeration of AggregatableConsiderations for a given key (offer or consideration), derived from the hash of the key and the current fulfillmentCounter value
+ * @param key Original key used to derive the slot of the enumeration
+ */
+ function getEnumeration(
+ bytes32 key
+ ) internal view returns (AggregatableConsideration[] storage tokens) {
+ bytes32 counterKey = MATCH_FULFILLMENT_COUNTER_KEY;
+ assembly {
+ mstore(0, key)
+ mstore(0x20, sload(counterKey))
+ tokens.slot := keccak256(0, 0x40)
+ }
+ }
+}
diff --git a/contracts/helpers/sol/fulfillments/match/MatchFulfillmentLib.sol b/contracts/helpers/sol/fulfillments/match/MatchFulfillmentLib.sol
new file mode 100644
index 000000000..e220f6894
--- /dev/null
+++ b/contracts/helpers/sol/fulfillments/match/MatchFulfillmentLib.sol
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AggregatableConsideration,
+ ProcessComponentParams,
+ MatchFulfillmentStorageLayout,
+ AggregatableOfferer
+} from "../lib/Structs.sol";
+import {
+ MatchComponent,
+ MatchComponentType
+} from "../../lib/types/MatchComponentType.sol";
+import { FulfillmentComponent, Fulfillment } from "../../SeaportStructs.sol";
+import { LibSort } from "solady/src/utils/LibSort.sol";
+import { MatchArrays } from "../lib/MatchArrays.sol";
+
+library MatchFulfillmentLib {
+ using MatchComponentType for MatchComponent[];
+ using MatchComponentType for MatchComponent;
+
+ /**
+ * @notice Check if a token already exists in a mapping by checking the length of the array at that slot
+ * @param token token to check
+ * @param layout storage layout
+ */
+ function aggregatableConsiderationExists(
+ AggregatableConsideration memory token,
+ MatchFulfillmentStorageLayout storage layout
+ ) internal view returns (bool) {
+ return
+ layout
+ .considerationMap[token.recipient][token.contractAddress][
+ token.tokenId
+ ].length > 0;
+ }
+
+ /**
+ * @notice Check if an entry into the offer component mapping already exists by checking its length
+ */
+ function aggregatableOffererExists(
+ address token,
+ uint256 tokenId,
+ AggregatableOfferer memory offerer,
+ MatchFulfillmentStorageLayout storage layout
+ ) internal view returns (bool) {
+ return
+ layout
+ .offerMap[token][tokenId][offerer.offerer][offerer.conduitKey]
+ .length > 0;
+ }
+
+ function processConsiderationComponent(
+ MatchComponent[] storage offerComponents,
+ MatchComponent[] storage considerationComponents,
+ ProcessComponentParams memory params
+ ) internal {
+ while (params.offerItemIndex < offerComponents.length) {
+ MatchComponent
+ memory considerationComponent = considerationComponents[
+ params.considerationItemIndex
+ ];
+
+ // if consideration has been completely credited, break to next consideration component
+ if (considerationComponent.getAmount() == 0) {
+ break;
+ }
+ processOfferComponent({
+ offerComponents: offerComponents,
+ considerationComponents: considerationComponents,
+ params: params
+ });
+ }
+
+ MatchArrays.appendUnsafe(
+ params.considerationFulfillmentComponents,
+ considerationComponents[params.considerationItemIndex]
+ .toFulfillmentComponent()
+ );
+ }
+
+ function processOfferComponent(
+ MatchComponent[] storage offerComponents,
+ MatchComponent[] storage considerationComponents,
+ ProcessComponentParams memory params
+ ) internal {
+ // re-load components each iteration as they may have been modified
+ MatchComponent memory offerComponent = offerComponents[
+ params.offerItemIndex
+ ];
+ MatchComponent memory considerationComponent = considerationComponents[
+ params.considerationItemIndex
+ ];
+
+ if (offerComponent.getAmount() > considerationComponent.getAmount()) {
+ // if offer amount is greater than consideration amount, set consideration to zero and credit from offer amount
+ offerComponent = offerComponent.subtractAmount(
+ considerationComponent
+ );
+ considerationComponent = considerationComponent.setAmount(0);
+ offerComponents[params.offerItemIndex] = offerComponent;
+ considerationComponents[
+ params.considerationItemIndex
+ ] = considerationComponent;
+ // note that this offerItemIndex should be included when consolidating
+ params.midCredit = true;
+ } else {
+ // otherwise deplete offer amount and credit consideration amount
+
+ considerationComponent = considerationComponent.subtractAmount(
+ offerComponent
+ );
+ offerComponent = offerComponent.setAmount(0);
+
+ considerationComponents[
+ params.considerationItemIndex
+ ] = considerationComponent;
+
+ offerComponents[params.offerItemIndex] = offerComponent;
+ ++params.offerItemIndex;
+ // note that this offerItemIndex should not be included when consolidating
+ params.midCredit = false;
+ }
+ // an offer component may have already been added if it was not depleted by an earlier consideration item
+ if (
+ !previouslyAdded(
+ params.offerFulfillmentComponents,
+ offerComponent.toFulfillmentComponent()
+ )
+ ) {
+ MatchArrays.appendUnsafe(
+ params.offerFulfillmentComponents,
+ offerComponent.toFulfillmentComponent()
+ );
+ }
+ }
+
+ function scuffLength(
+ FulfillmentComponent[] memory components,
+ uint256 newLength
+ ) internal pure {
+ assembly {
+ mstore(components, newLength)
+ }
+ }
+
+ function previouslyAdded(
+ FulfillmentComponent[] memory components,
+ FulfillmentComponent memory fulfillmentComponent
+ ) internal pure returns (bool) {
+ if (components.length == 0) {
+ return false;
+ }
+
+ FulfillmentComponent memory lastComponent = components[
+ components.length - 1
+ ];
+ return
+ lastComponent.orderIndex == fulfillmentComponent.orderIndex &&
+ lastComponent.itemIndex == fulfillmentComponent.itemIndex;
+ }
+
+ /**
+ * Credit offer components to consideration components until either or both are exhausted
+ * Updates arrays in storage to remove 0-item components after credits
+ * @param offerComponents Aggregatable offer components
+ * @param considerationComponents Aggregatable consideration components
+ */
+ function createFulfillment(
+ MatchComponent[] storage offerComponents,
+ MatchComponent[] storage considerationComponents
+ ) internal returns (Fulfillment memory) {
+ // optimistically allocate arrays of fulfillment components
+ FulfillmentComponent[] memory offerFulfillmentComponents = MatchArrays
+ .allocateFulfillmentComponents(offerComponents.length);
+ FulfillmentComponent[]
+ memory considerationFulfillmentComponents = MatchArrays
+ .allocateFulfillmentComponents(considerationComponents.length);
+ // iterate over consideration components
+ ProcessComponentParams memory params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: considerationFulfillmentComponents,
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+
+ // iterate over all consideration components eligible to be fulfilled
+ // in a single transfer; this means that any uncredited amounts will be
+ // consolidated into the first component for later fulfillments
+ // TODO: this may not be optimal in some cases with partial
+ // fulfillments
+ for (
+ uint256 considerationItemIndex;
+ considerationItemIndex < considerationComponents.length;
+ ++considerationItemIndex
+ ) {
+ // params will be updated directly by called functions except for considerationItemIndex
+ params.considerationItemIndex = considerationItemIndex;
+ processConsiderationComponent({
+ offerComponents: offerComponents,
+ considerationComponents: considerationComponents,
+ params: params
+ });
+ }
+
+ // remove any zero-amount components so they are skipped in future
+ // fulfillments, and consolidate any remaining offer amounts used
+ // in this fulfillment into the first component.
+ consolidateComponents(
+ offerComponents,
+ // if mid-credit, offerItemIndex should be included in consolidation
+ (params.midCredit)
+ ? params.offerItemIndex + 1
+ : params.offerItemIndex
+ );
+ // all eligible consideration components will be processed when matched
+ // with the first eligible offer components, whether or not there are
+ // enough offer items to credit each consideration item. This means
+ // that all remaining amounts will be consolidated into the first
+ // consideration component for later fulfillments.
+ consolidateComponents(
+ considerationComponents,
+ considerationComponents.length
+ );
+
+ // return a discrete fulfillment since either or both of the sets of components have been exhausted
+ // if offer or consideration items remain, they will be revisited in subsequent calls
+ return
+ Fulfillment({
+ offerComponents: offerFulfillmentComponents,
+ considerationComponents: considerationFulfillmentComponents
+ });
+ }
+
+ /**
+ * @dev Consolidate any remaining amounts
+ * @param components Components to consolidate
+ * @param excludeIndex First index to exclude from consolidation. For
+ * offerComponents this is the index after the last credited item,
+ * for considerationComponents, this is the length of the array
+ */
+ function consolidateComponents(
+ MatchComponent[] storage components,
+ uint256 excludeIndex
+ ) internal {
+ // cache components in memory
+ MatchComponent[] memory cachedComponents = components;
+ if (cachedComponents.length == 0) {
+ return;
+ } else if (cachedComponents.length == 1) {
+ // check if there is only one component
+ // if it is zero, remove it
+ if (cachedComponents[0].getAmount() == 0) {
+ components.pop();
+ }
+ // otherwise do nothing
+ return;
+ }
+ // otherwise clear the storage array
+ while (components.length > 0) {
+ components.pop();
+ }
+
+ // consolidate the amounts of credited non-zero components into the
+ // first component. This is what Seaport does internally when a
+ // fulfillment is credited.
+ MatchComponent memory first = cachedComponents[0];
+
+ // consolidate all non-zero components used in this fulfillment into the
+ // first component
+ for (uint256 i = 1; i < excludeIndex; ++i) {
+ first = first.addAmount(cachedComponents[i]);
+ }
+
+ // push the first component back into storage if it is non-zero
+ if (first.getAmount() > 0) {
+ components.push(first);
+ }
+ // push any remaining non-zero components back into storage
+ for (uint256 i = excludeIndex; i < cachedComponents.length; ++i) {
+ MatchComponent memory component = cachedComponents[i];
+ if (component.getAmount() > 0) {
+ components.push(component);
+ }
+ }
+ }
+
+ function dedupe(
+ MatchComponent[] memory components
+ ) internal pure returns (MatchComponent[] memory dedupedComponents) {
+ if (components.length == 0 || components.length == 1) {
+ return components;
+ }
+ // sort components
+ // uint256[] memory cast = components.toUints();
+ // LibSort.sort(cast);
+ // components = MatchComponentType.fromUints(cast);
+ // create a new array of same size; it will be truncated if necessary
+ MatchArrays.sortByIndex(components);
+ dedupedComponents = new MatchComponent[](components.length);
+ dedupedComponents[0] = components[0];
+ uint256 dedupedIndex = 1;
+ for (uint256 i = 1; i < components.length; i++) {
+ // compare current component to last deduped component
+ if (!components[i].equals(dedupedComponents[dedupedIndex - 1])) {
+ // if it is different, add it to the deduped array and increment the index
+ dedupedComponents[dedupedIndex] = components[i];
+ ++dedupedIndex;
+ }
+ }
+ return MatchArrays.truncate(dedupedComponents, dedupedIndex);
+ }
+}
diff --git a/contracts/helpers/sol/lib/AdditionalRecipientLib.sol b/contracts/helpers/sol/lib/AdditionalRecipientLib.sol
index 18161efd9..b86ac4230 100644
--- a/contracts/helpers/sol/lib/AdditionalRecipientLib.sol
+++ b/contracts/helpers/sol/lib/AdditionalRecipientLib.sol
@@ -2,16 +2,34 @@
pragma solidity ^0.8.17;
import { AdditionalRecipient } from "../../../lib/ConsiderationStructs.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title AdditionalRecipientLib
+ * @author James Wenzel (emo.eth)
+ * @notice AdditionalRecipientLib is a library for managing AdditionalRecipient
+ * structs and arrays. It allows chaining of functions to make
+ * struct creation more readable.
+ */
library AdditionalRecipientLib {
bytes32 private constant ADDITIONAL_RECIPIENT_MAP_POSITION =
keccak256("seaport.AdditionalRecipientDefaults");
bytes32 private constant ADDITIONAL_RECIPIENTS_MAP_POSITION =
keccak256("seaport.AdditionalRecipientsDefaults");
+ bytes32 private constant EMPTY_ADDITIONAL_RECIPIENT =
+ keccak256(
+ abi.encode(
+ AdditionalRecipient({
+ amount: 0,
+ recipient: payable(address(0))
+ })
+ )
+ );
/**
- * @notice clears a default AdditionalRecipient from storage
+ * @dev Clears a default AdditionalRecipient from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -21,22 +39,35 @@ library AdditionalRecipientLib {
clear(item);
}
+ /**
+ * @dev Clears all fields on an AdditionalRecipient.
+ *
+ * @param item the AdditionalRecipient to clear
+ */
function clear(AdditionalRecipient storage item) internal {
// clear all fields
item.amount = 0;
item.recipient = payable(address(0));
}
- function clear(AdditionalRecipient[] storage item) internal {
- while (item.length > 0) {
- clear(item[item.length - 1]);
- item.pop();
+ /**
+ * @dev Clears an array of AdditionalRecipients from storage.
+ *
+ * @param items the name of the default to clear
+ */
+ function clear(AdditionalRecipient[] storage items) internal {
+ while (items.length > 0) {
+ clear(items[items.length - 1]);
+ items.pop();
}
}
/**
- * @notice gets a default AdditionalRecipient from storage
+ * @dev Gets a default AdditionalRecipient from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the AdditionalRecipient retrieved from storage
*/
function fromDefault(
string memory defaultName
@@ -44,20 +75,38 @@ library AdditionalRecipientLib {
mapping(string => AdditionalRecipient)
storage additionalRecipientMap = _additionalRecipientMap();
item = additionalRecipientMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_ADDITIONAL_RECIPIENT) {
+ revert("Empty AdditionalRecipient selected.");
+ }
}
+ /**
+ * @dev Gets an array of default AdditionalRecipients from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the AdditionalRecipients retrieved from storage
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (AdditionalRecipient[] memory items) {
mapping(string => AdditionalRecipient[])
storage additionalRecipientsMap = _additionalRecipientsMap();
items = additionalRecipientsMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty AdditionalRecipient array selected.");
+ }
}
/**
- * @notice saves an AdditionalRecipient as a named default
+ * @dev Saves an AdditionalRecipient as a named default.
+ *
* @param additionalRecipient the AdditionalRecipient to save as a default
- * @param defaultName the name of the default for retrieval
+ * @param defaultName the name of the new default
+ *
+ * @return _additionalRecipient the AdditionalRecipient saved as a default
*/
function saveDefault(
AdditionalRecipient memory additionalRecipient,
@@ -69,6 +118,14 @@ library AdditionalRecipientLib {
return additionalRecipient;
}
+ /**
+ * @dev Saves an array of AdditionalRecipients as a named default.
+ *
+ * @param additionalRecipients the AdditionalRecipients to save as a default
+ * @param defaultName the name of the new default
+ *
+ * @return _additionalRecipients the AdditionalRecipients saved as a default
+ */
function saveDefaultMany(
AdditionalRecipient[] memory additionalRecipients,
string memory defaultName
@@ -83,8 +140,11 @@ library AdditionalRecipientLib {
}
/**
- * @notice makes a copy of an AdditionalRecipient in-memory
+ * @dev Makes a copy of an AdditionalRecipient in-memory.
+ *
* @param item the AdditionalRecipient to make a copy of in-memory
+ *
+ * @custom:return additionalRecipient the copy of the AdditionalRecipient
*/
function copy(
AdditionalRecipient memory item
@@ -96,6 +156,13 @@ library AdditionalRecipientLib {
});
}
+ /**
+ * @dev Makes a copy of an array of AdditionalRecipients in-memory.
+ *
+ * @param items the AdditionalRecipients to make a copy of in-memory
+ *
+ * @custom:return additionalRecipients the copy of the AdditionalRecipients
+ */
function copy(
AdditionalRecipient[] memory items
) internal pure returns (AdditionalRecipient[] memory) {
@@ -108,13 +175,21 @@ library AdditionalRecipientLib {
return copiedItems;
}
+ /**
+ * @dev Returns an empty AdditionalRecipient.
+ *
+ * @custom:return item the empty AdditionalRecipient
+ */
function empty() internal pure returns (AdditionalRecipient memory) {
return
AdditionalRecipient({ amount: 0, recipient: payable(address(0)) });
}
/**
- * @notice gets the storage position of the default AdditionalRecipient map
+ * @dev Gets the storage position of the default AdditionalRecipient map.
+ *
+ * @custom:return additionalRecipientMap the storage position of the default
+ * AdditionalRecipient map
*/
function _additionalRecipientMap()
private
@@ -130,6 +205,14 @@ library AdditionalRecipientLib {
}
}
+ /**
+ * @dev Gets the storage position of the default AdditionalRecipients array
+ * map.
+ *
+ * @custom:return additionalRecipientsMap the storage position of the
+ * default AdditionalRecipient array
+ * map
+ */
function _additionalRecipientsMap()
private
pure
@@ -144,10 +227,17 @@ library AdditionalRecipientLib {
}
}
- // methods for configuring a single of each of an AdditionalRecipient's fields, which modifies the
- // AdditionalRecipient in-place and
- // returns it
+ // Methods for configuring a single of each of an AdditionalRecipient's
+ // fields, which modify the AdditionalRecipient in-place and return it.
+ /**
+ * @dev Sets the amount field of an AdditionalRecipient.
+ *
+ * @param item the AdditionalRecipient to modify
+ * @param amount the amount to set
+ *
+ * @custom:return _item the modified AdditionalRecipient
+ */
function withAmount(
AdditionalRecipient memory item,
uint256 amount
@@ -156,6 +246,14 @@ library AdditionalRecipientLib {
return item;
}
+ /**
+ * @dev Sets the recipient field of an AdditionalRecipient.
+ *
+ * @param item the AdditionalRecipient to modify
+ * @param recipient the recipient to set
+ *
+ * @custom:return _item the modified AdditionalRecipient
+ */
function withRecipient(
AdditionalRecipient memory item,
address recipient
diff --git a/contracts/helpers/sol/lib/AdvancedOrderLib.sol b/contracts/helpers/sol/lib/AdvancedOrderLib.sol
index ee820c1e2..68abf444e 100644
--- a/contracts/helpers/sol/lib/AdvancedOrderLib.sol
+++ b/contracts/helpers/sol/lib/AdvancedOrderLib.sol
@@ -2,23 +2,80 @@
pragma solidity ^0.8.17;
import {
+ AdditionalRecipient,
AdvancedOrder,
+ BasicOrderParameters,
+ ConsiderationItem,
+ CriteriaResolver,
+ OfferItem,
Order,
- OrderParameters
+ OrderComponents,
+ OrderParameters,
+ OrderType,
+ ReceivedItem,
+ SpentItem
} from "../../../lib/ConsiderationStructs.sol";
+
+import { BasicOrderType, ItemType } from "../../../lib/ConsiderationEnums.sol";
+
+import { UnavailableReason } from "../SpaceEnums.sol";
+
import { OrderParametersLib } from "./OrderParametersLib.sol";
+
import { StructCopier } from "./StructCopier.sol";
+import { SeaportInterface } from "../SeaportInterface.sol";
+
+import { OrderDetails } from "../fulfillments/lib/Structs.sol";
+
+struct ContractNonceDetails {
+ bool set;
+ address offerer;
+ uint256 currentNonce;
+}
+
+/**
+ * @title AdvancedOrderLib
+ * @author James Wenzel (emo.eth)
+ * @notice AdditionalRecipientLib is a library for managing AdvancedOrder
+ * structs and arrays. It allows chaining of functions to make struct
+ * creation more readable.
+ */
library AdvancedOrderLib {
bytes32 private constant ADVANCED_ORDER_MAP_POSITION =
keccak256("seaport.AdvancedOrderDefaults");
bytes32 private constant ADVANCED_ORDERS_MAP_POSITION =
keccak256("seaport.AdvancedOrdersDefaults");
+ bytes32 private constant EMPTY_ADVANCED_ORDER =
+ keccak256(
+ abi.encode(
+ AdvancedOrder({
+ parameters: OrderParameters({
+ offerer: address(0),
+ zone: address(0),
+ offer: new OfferItem[](0),
+ consideration: new ConsiderationItem[](0),
+ orderType: OrderType(0),
+ startTime: 0,
+ endTime: 0,
+ zoneHash: bytes32(0),
+ salt: 0,
+ conduitKey: bytes32(0),
+ totalOriginalConsiderationItems: 0
+ }),
+ numerator: 0,
+ denominator: 0,
+ signature: new bytes(0),
+ extraData: new bytes(0)
+ })
+ )
+ );
using OrderParametersLib for OrderParameters;
/**
- * @notice clears a default AdvancedOrder from storage
+ * @dev Clears a default AdvancedOrder from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -28,6 +85,11 @@ library AdvancedOrderLib {
clear(item);
}
+ /**
+ * @dev Clears all fields on an AdvancedOrder.
+ *
+ * @param item the AdvancedOrder to clear
+ */
function clear(AdvancedOrder storage item) internal {
// clear all fields
item.parameters.clear();
@@ -37,6 +99,11 @@ library AdvancedOrderLib {
item.extraData = "";
}
+ /**
+ * @dev Clears an array of AdvancedOrders from storage.
+ *
+ * @param items the AdvancedOrders to clear
+ */
function clear(AdvancedOrder[] storage items) internal {
while (items.length > 0) {
clear(items[items.length - 1]);
@@ -45,8 +112,11 @@ library AdvancedOrderLib {
}
/**
- * @notice gets a default AdvancedOrder from storage
+ * @dev Gets a default AdvancedOrder from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the AdvancedOrder retrieved from storage
*/
function fromDefault(
string memory defaultName
@@ -54,24 +124,47 @@ library AdvancedOrderLib {
mapping(string => AdvancedOrder)
storage advancedOrderMap = _advancedOrderMap();
item = advancedOrderMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_ADVANCED_ORDER) {
+ revert("Empty AdvancedOrder selected.");
+ }
}
+ /**
+ * @dev Gets an array of default AdvancedOrders from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the AdvancedOrders retrieved from storage
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (AdvancedOrder[] memory items) {
mapping(string => AdvancedOrder[])
storage advancedOrdersMap = _advancedOrdersMap();
items = advancedOrdersMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty AdvancedOrder array selected.");
+ }
}
+ /**
+ * @dev Returns an empty AdvancedOrder.
+ *
+ * @custom:return item the empty AdvancedOrder
+ */
function empty() internal pure returns (AdvancedOrder memory) {
return AdvancedOrder(OrderParametersLib.empty(), 0, 0, "", "");
}
/**
- * @notice saves an AdvancedOrder as a named default
+ * @dev Saves an AdvancedOrder as a named default.
+ *
* @param advancedOrder the AdvancedOrder to save as a default
- * @param defaultName the name of the default for retrieval
+ * @param defaultName the name of the new default
+ *
+ * @return _advancedOrder the AdvancedOrder saved as a default
*/
function saveDefault(
AdvancedOrder memory advancedOrder,
@@ -86,6 +179,14 @@ library AdvancedOrderLib {
return advancedOrder;
}
+ /**
+ * @dev Saves an array of AdvancedOrders as a named default.
+ *
+ * @param advancedOrders the AdvancedOrders to save as a default
+ * @param defaultName the name of the new default
+ *
+ * @return _advancedOrders the AdvancedOrders saved as a default
+ */
function saveDefaultMany(
AdvancedOrder[] memory advancedOrders,
string memory defaultName
@@ -100,8 +201,11 @@ library AdvancedOrderLib {
}
/**
- * @notice makes a copy of an AdvancedOrder in-memory
+ * @dev Makes a copy of an AdvancedOrder in-memory.
+ *
* @param item the AdvancedOrder to make a copy of in-memory
+ *
+ * @custom:return item the copied AdvancedOrder
*/
function copy(
AdvancedOrder memory item
@@ -116,6 +220,13 @@ library AdvancedOrderLib {
});
}
+ /**
+ * @dev Makes a copy of an array of AdvancedOrders in-memory.
+ *
+ * @param items the AdvancedOrders to make a copy of in-memory
+ *
+ * @custom:return items the copied AdvancedOrders
+ */
function copy(
AdvancedOrder[] memory items
) internal pure returns (AdvancedOrder[] memory) {
@@ -127,7 +238,10 @@ library AdvancedOrderLib {
}
/**
- * @notice gets the storage position of the default AdvancedOrder map
+ * @dev Gets the storage position of the default AdvancedOrder map.
+ *
+ * @return advancedOrderMap the storage position of the default
+ * AdvancedOrder map
*/
function _advancedOrderMap()
private
@@ -140,6 +254,12 @@ library AdvancedOrderLib {
}
}
+ /**
+ * @dev Gets the storage position of the default AdvancedOrder array map.
+ *
+ * @return advancedOrdersMap the storage position of the default
+ * AdvancedOrder array map
+ */
function _advancedOrdersMap()
private
pure
@@ -151,10 +271,17 @@ library AdvancedOrderLib {
}
}
- // methods for configuring a single of each of an AdvancedOrder's fields, which modifies the AdvancedOrder in-place
- // and
- // returns it
+ // Methods for configuring a single of each of an AdvancedOrder's fields,
+ // which modify the AdvancedOrder in-place and return it.
+ /**
+ * @dev Configures an AdvancedOrder's parameters.
+ *
+ * @param advancedOrder the AdvancedOrder to configure
+ * @param parameters the parameters to set
+ *
+ * @custom:return _advancedOrder the configured AdvancedOrder
+ */
function withParameters(
AdvancedOrder memory advancedOrder,
OrderParameters memory parameters
@@ -163,6 +290,14 @@ library AdvancedOrderLib {
return advancedOrder;
}
+ /**
+ * @dev Configures an AdvancedOrder's numerator.
+ *
+ * @param advancedOrder the AdvancedOrder to configure
+ * @param numerator the numerator to set
+ *
+ * @custom:return _advancedOrder the configured AdvancedOrder
+ */
function withNumerator(
AdvancedOrder memory advancedOrder,
uint120 numerator
@@ -171,6 +306,14 @@ library AdvancedOrderLib {
return advancedOrder;
}
+ /**
+ * @dev Configures an AdvancedOrder's denominator.
+ *
+ * @param advancedOrder the AdvancedOrder to configure
+ * @param denominator the denominator to set
+ *
+ * @custom:return _advancedOrder the configured AdvancedOrder
+ */
function withDenominator(
AdvancedOrder memory advancedOrder,
uint120 denominator
@@ -179,6 +322,14 @@ library AdvancedOrderLib {
return advancedOrder;
}
+ /**
+ * @dev Configures an AdvancedOrder's signature.
+ *
+ * @param advancedOrder the AdvancedOrder to configure
+ * @param signature the signature to set
+ *
+ * @custom:return _advancedOrder the configured AdvancedOrder
+ */
function withSignature(
AdvancedOrder memory advancedOrder,
bytes memory signature
@@ -187,6 +338,14 @@ library AdvancedOrderLib {
return advancedOrder;
}
+ /**
+ * @dev Configures an AdvancedOrder's extra data.
+ *
+ * @param advancedOrder the AdvancedOrder to configure
+ * @param extraData the extra data to set
+ *
+ * @custom:return _advancedOrder the configured AdvancedOrder
+ */
function withExtraData(
AdvancedOrder memory advancedOrder,
bytes memory extraData
@@ -195,10 +354,440 @@ library AdvancedOrderLib {
return advancedOrder;
}
+ /**
+ * @dev Converts an AdvancedOrder to an Order.
+ *
+ * @param advancedOrder the AdvancedOrder to convert
+ *
+ * @return order the converted Order
+ */
function toOrder(
AdvancedOrder memory advancedOrder
) internal pure returns (Order memory order) {
order.parameters = advancedOrder.parameters.copy();
order.signature = advancedOrder.signature;
}
+
+ /**
+ * @dev Converts an AdvancedOrder[] to an Order[].
+ *
+ * @param advancedOrders the AdvancedOrder[] to convert
+ *
+ * @return the converted Order[]
+ */
+ function toOrders(
+ AdvancedOrder[] memory advancedOrders
+ ) internal pure returns (Order[] memory) {
+ Order[] memory orders = new Order[](advancedOrders.length);
+
+ for (uint256 i; i < advancedOrders.length; ++i) {
+ orders[i] = toOrder(advancedOrders[i]);
+ }
+ return orders;
+ }
+
+ /**
+ * @dev Converts an AdvancedOrder to a BasicOrderParameters.
+ *
+ * @param advancedOrder the AdvancedOrder to convert
+ * @param basicOrderType the BasicOrderType to convert to
+ *
+ * @return basicOrderParameters the BasicOrderParameters
+ */
+ function toBasicOrderParameters(
+ AdvancedOrder memory advancedOrder,
+ BasicOrderType basicOrderType
+ ) internal pure returns (BasicOrderParameters memory basicOrderParameters) {
+ basicOrderParameters.considerationToken = advancedOrder
+ .parameters
+ .consideration[0]
+ .token;
+ basicOrderParameters.considerationIdentifier = advancedOrder
+ .parameters
+ .consideration[0]
+ .identifierOrCriteria;
+ basicOrderParameters.considerationAmount = advancedOrder
+ .parameters
+ .consideration[0]
+ .endAmount;
+ basicOrderParameters.offerer = payable(
+ advancedOrder.parameters.offerer
+ );
+ basicOrderParameters.zone = advancedOrder.parameters.zone;
+ basicOrderParameters.offerToken = advancedOrder
+ .parameters
+ .offer[0]
+ .token;
+ basicOrderParameters.offerIdentifier = advancedOrder
+ .parameters
+ .offer[0]
+ .identifierOrCriteria;
+ basicOrderParameters.offerAmount = advancedOrder
+ .parameters
+ .offer[0]
+ .endAmount;
+ basicOrderParameters.basicOrderType = basicOrderType;
+ basicOrderParameters.startTime = advancedOrder.parameters.startTime;
+ basicOrderParameters.endTime = advancedOrder.parameters.endTime;
+ basicOrderParameters.zoneHash = advancedOrder.parameters.zoneHash;
+ basicOrderParameters.salt = advancedOrder.parameters.salt;
+ basicOrderParameters.offererConduitKey = advancedOrder
+ .parameters
+ .conduitKey;
+ basicOrderParameters.fulfillerConduitKey = advancedOrder
+ .parameters
+ .conduitKey;
+ basicOrderParameters.totalOriginalAdditionalRecipients =
+ advancedOrder.parameters.totalOriginalConsiderationItems -
+ 1;
+
+ AdditionalRecipient[]
+ memory additionalRecipients = new AdditionalRecipient[](
+ advancedOrder.parameters.consideration.length - 1
+ );
+ for (
+ uint256 i = 1;
+ i < advancedOrder.parameters.consideration.length;
+ i++
+ ) {
+ additionalRecipients[i - 1] = AdditionalRecipient({
+ recipient: advancedOrder.parameters.consideration[i].recipient,
+ amount: advancedOrder.parameters.consideration[i].startAmount
+ });
+ }
+
+ basicOrderParameters.additionalRecipients = additionalRecipients;
+ basicOrderParameters.signature = advancedOrder.signature;
+
+ return basicOrderParameters;
+ }
+
+ function withCoercedAmountsForPartialFulfillment(
+ AdvancedOrder memory order
+ ) internal pure returns (AdvancedOrder memory) {
+ OrderParameters memory orderParams = order.parameters;
+ for (uint256 i = 0; i < orderParams.offer.length; ++i) {
+ uint256 newStartAmount;
+ uint256 newEndAmount;
+ OfferItem memory item = orderParams.offer[i];
+
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ uint256 amount = uint256(order.denominator / order.numerator);
+ newStartAmount = amount;
+ newEndAmount = amount;
+ } else {
+ (
+ newStartAmount,
+ newEndAmount
+ ) = deriveFractionCompatibleAmounts(
+ item.startAmount,
+ item.endAmount,
+ orderParams.startTime,
+ orderParams.endTime,
+ order.numerator,
+ order.denominator
+ );
+ }
+
+ order.parameters.offer[i].startAmount = newStartAmount;
+ order.parameters.offer[i].endAmount = newEndAmount;
+ }
+
+ // Adjust consideration item amounts based on the fraction
+ for (uint256 i = 0; i < orderParams.consideration.length; ++i) {
+ uint256 newStartAmount;
+ uint256 newEndAmount;
+ ConsiderationItem memory item = orderParams.consideration[i];
+
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ uint256 amount = uint256(order.denominator / order.numerator);
+ newStartAmount = amount;
+ newEndAmount = amount;
+ } else {
+ (
+ newStartAmount,
+ newEndAmount
+ ) = deriveFractionCompatibleAmounts(
+ item.startAmount,
+ item.endAmount,
+ orderParams.startTime,
+ orderParams.endTime,
+ order.numerator,
+ order.denominator
+ );
+ }
+
+ order.parameters.consideration[i].startAmount = newStartAmount;
+ order.parameters.consideration[i].endAmount = newEndAmount;
+ }
+
+ return order;
+ }
+
+ function deriveFractionCompatibleAmounts(
+ uint256 originalStartAmount,
+ uint256 originalEndAmount,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) internal pure returns (uint256 newStartAmount, uint256 newEndAmount) {
+ if (
+ startTime >= endTime ||
+ numerator > denominator ||
+ numerator == 0 ||
+ denominator == 0 ||
+ (originalStartAmount == 0 && originalEndAmount == 0)
+ ) {
+ revert(
+ "AdvancedOrderLib: bad inputs to deriveFractionCompatibleAmounts"
+ );
+ }
+
+ bool ensureNotHuge = originalStartAmount != originalEndAmount;
+
+ newStartAmount = minimalChange(
+ originalStartAmount,
+ numerator,
+ denominator,
+ ensureNotHuge
+ );
+
+ newEndAmount = minimalChange(
+ originalEndAmount,
+ numerator,
+ denominator,
+ ensureNotHuge
+ );
+
+ if (newStartAmount == 0 && newEndAmount == 0) {
+ revert("AdvancedOrderLib: derived amount will always be zero");
+ }
+ }
+
+ // Function to find the minimal change in the value so that it results in a
+ // new value with no remainder when the numerator and the denominator are
+ // applied.
+ function minimalChange(
+ uint256 value,
+ uint256 numerator,
+ uint256 denominator,
+ bool ensureNotHuge
+ ) public pure returns (uint256 newValue) {
+ require(denominator != 0, "AdvancedOrderLib: no denominator supplied.");
+
+ if (ensureNotHuge) {
+ value %= type(uint208).max;
+ }
+
+ uint256 remainder = (value * numerator) % denominator;
+
+ uint256 diffToNextMultiple = denominator - remainder;
+ uint256 diffToPrevMultiple = remainder;
+
+ newValue = 0;
+ if (diffToNextMultiple > diffToPrevMultiple) {
+ newValue = value - (diffToPrevMultiple / numerator);
+ }
+
+ if (newValue == 0) {
+ newValue = value + (diffToNextMultiple / numerator);
+ }
+
+ if ((newValue * numerator) % denominator != 0) {
+ revert("AdvancedOrderLib: minimal change failed");
+ }
+ }
+
+ /**
+ * @dev Get the orderHashes of an array of orders.
+ */
+ function getOrderHashes(
+ AdvancedOrder[] memory orders,
+ address seaport
+ ) internal view returns (bytes32[] memory) {
+ SeaportInterface seaportInterface = SeaportInterface(seaport);
+
+ bytes32[] memory orderHashes = new bytes32[](orders.length);
+
+ // Array of (contract offerer, currentNonce)
+ ContractNonceDetails[] memory detailsArray = new ContractNonceDetails[](
+ orders.length
+ );
+
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderParameters memory order = orders[i].parameters;
+ bytes32 orderHash;
+ if (
+ order.orderType == OrderType.CONTRACT &&
+ _hasValidTime(order.startTime, order.endTime)
+ ) {
+ bool noneYetLocated = false;
+ uint256 j = 0;
+ uint256 currentNonce;
+ for (; j < detailsArray.length; ++j) {
+ ContractNonceDetails memory details = detailsArray[j];
+ if (!details.set) {
+ noneYetLocated = true;
+ break;
+ } else if (details.offerer == order.offerer) {
+ currentNonce = ++(details.currentNonce);
+ break;
+ }
+ }
+
+ if (noneYetLocated) {
+ currentNonce = seaportInterface.getContractOffererNonce(
+ order.offerer
+ );
+
+ detailsArray[j] = ContractNonceDetails({
+ set: true,
+ offerer: order.offerer,
+ currentNonce: currentNonce
+ });
+ }
+
+ uint256 shiftedOfferer = uint256(uint160(order.offerer)) << 96;
+
+ orderHash = bytes32(shiftedOfferer ^ currentNonce);
+ } else {
+ orderHash = getTipNeutralizedOrderHash(
+ orders[i],
+ seaportInterface
+ );
+ }
+
+ orderHashes[i] = orderHash;
+ }
+
+ return orderHashes;
+ }
+
+ function _hasValidTime(
+ uint256 startTime,
+ uint256 endTime
+ ) internal view returns (bool) {
+ return block.timestamp >= startTime && block.timestamp < endTime;
+ }
+
+ /**
+ * @dev Get the orderHash for an AdvancedOrders and return the orderHash.
+ * This function can be treated as a wrapper around Seaport's
+ * getOrderHash function. It is used to get the orderHash of an
+ * AdvancedOrder that has a tip added onto it. Calling it on an
+ * AdvancedOrder that does not have a tip will return the same
+ * orderHash as calling Seaport's getOrderHash function directly.
+ * Seaport handles tips gracefully inside of the top level fulfill and
+ * match functions, but since we're adding tips early in the fuzz test
+ * lifecycle, it's necessary to flip them back and forth when we need
+ * to pass order components into getOrderHash. Note: they're two
+ * different orders, so e.g. cancelling or validating order with a tip
+ * on it is not the same as cancelling the order without a tip on it.
+ */
+ function getTipNeutralizedOrderHash(
+ AdvancedOrder memory order,
+ SeaportInterface seaport
+ ) internal view returns (bytes32 orderHash) {
+ // Get the counter of the order offerer.
+ uint256 counter = seaport.getCounter(order.parameters.offerer);
+
+ return getTipNeutralizedOrderHash(order, seaport, counter);
+ }
+
+ function getTipNeutralizedOrderHash(
+ AdvancedOrder memory order,
+ SeaportInterface seaport,
+ uint256 counter
+ ) internal view returns (bytes32 orderHash) {
+ // Get the OrderComponents from the OrderParameters.
+ OrderComponents memory components = (
+ order.parameters.toOrderComponents(counter)
+ );
+
+ // Get the length of the consideration array (which might have
+ // additional consideration items set as tips).
+ uint256 lengthWithTips = components.consideration.length;
+
+ // Get the length of the consideration array without tips, which is
+ // stored in the totalOriginalConsiderationItems field.
+ uint256 lengthSansTips = (
+ order.parameters.totalOriginalConsiderationItems
+ );
+
+ // Get a reference to the consideration array.
+ ConsiderationItem[] memory considerationSansTips = (
+ components.consideration
+ );
+
+ // Set proper length of the considerationSansTips array.
+ assembly {
+ mstore(considerationSansTips, lengthSansTips)
+ }
+
+ // Get the orderHash using the tweaked OrderComponents.
+ orderHash = seaport.getOrderHash(components);
+
+ // Restore the length of the considerationSansTips array.
+ assembly {
+ mstore(considerationSansTips, lengthWithTips)
+ }
+ }
+
+ function getOrderDetails(
+ AdvancedOrder[] memory advancedOrders,
+ CriteriaResolver[] memory criteriaResolvers,
+ bytes32[] memory orderHashes,
+ UnavailableReason[] memory unavailableReasons
+ ) internal view returns (OrderDetails[] memory) {
+ OrderDetails[] memory orderDetails = new OrderDetails[](
+ advancedOrders.length
+ );
+
+ for (uint256 i = 0; i < advancedOrders.length; i++) {
+ orderDetails[i] = toOrderDetails(
+ advancedOrders[i],
+ i,
+ criteriaResolvers,
+ orderHashes[i],
+ unavailableReasons[i]
+ );
+ }
+
+ return orderDetails;
+ }
+
+ function toOrderDetails(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ CriteriaResolver[] memory resolvers,
+ bytes32 orderHash,
+ UnavailableReason unavailableReason
+ ) internal view returns (OrderDetails memory) {
+ (SpentItem[] memory offer, ReceivedItem[] memory consideration) = order
+ .parameters
+ .getSpentAndReceivedItems(
+ order.numerator,
+ order.denominator,
+ orderIndex,
+ resolvers
+ );
+
+ return
+ OrderDetails({
+ offerer: order.parameters.offerer,
+ conduitKey: order.parameters.conduitKey,
+ offer: offer,
+ consideration: consideration,
+ isContract: order.parameters.orderType == OrderType.CONTRACT,
+ orderHash: orderHash,
+ unavailableReason: unavailableReason
+ });
+ }
}
diff --git a/contracts/helpers/sol/lib/ArrayLib.sol b/contracts/helpers/sol/lib/ArrayLib.sol
index 8c8c25abd..8c74c1fa8 100644
--- a/contracts/helpers/sol/lib/ArrayLib.sol
+++ b/contracts/helpers/sol/lib/ArrayLib.sol
@@ -1,7 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
+/**
+ * @title ArrayLib
+ * @author James Wenzel (emo.eth)
+ * @notice ArrayLib is a library for managing arrays.
+ */
library ArrayLib {
+ /**
+ * @dev Sets the values of an array.
+ *
+ * @param array the array to set
+ * @param values the values to set
+ */
function setBytes32s(
bytes32[] storage array,
bytes32[] memory values
@@ -14,6 +25,13 @@ library ArrayLib {
}
}
+ /**
+ * @dev Makes a copy of an array.
+ *
+ * @param array the array to copy
+ *
+ * @custom:return copiedArray the copied array
+ */
function copy(
bytes32[] memory array
) internal pure returns (bytes32[] memory) {
diff --git a/contracts/helpers/sol/lib/BasicOrderParametersLib.sol b/contracts/helpers/sol/lib/BasicOrderParametersLib.sol
index fb8deb965..4436a3a13 100644
--- a/contracts/helpers/sol/lib/BasicOrderParametersLib.sol
+++ b/contracts/helpers/sol/lib/BasicOrderParametersLib.sol
@@ -2,22 +2,24 @@
pragma solidity ^0.8.17;
import {
+ AdditionalRecipient,
BasicOrderParameters,
- OrderComponents,
- OrderParameters,
- ConsiderationItem,
- OrderParameters,
- OfferItem,
- AdditionalRecipient
+ OrderParameters
} from "../../../lib/ConsiderationStructs.sol";
-import {
- OrderType,
- ItemType,
- BasicOrderType
-} from "../../../lib/ConsiderationEnums.sol";
+
+import { BasicOrderType } from "../../../lib/ConsiderationEnums.sol";
+
import { StructCopier } from "./StructCopier.sol";
+
import { AdditionalRecipientLib } from "./AdditionalRecipientLib.sol";
+/**
+ * @title BasicOrderParametersLib
+ * @author James Wenzel (emo.eth)
+ * @notice BasicOrderParametersLib is a library for managing
+ * BasicOrderParameters structs and arrays. It allows chaining of
+ * functions to make struct creation more readable.
+ */
library BasicOrderParametersLib {
using BasicOrderParametersLib for BasicOrderParameters;
using AdditionalRecipientLib for AdditionalRecipient[];
@@ -26,7 +28,37 @@ library BasicOrderParametersLib {
keccak256("seaport.BasicOrderParametersDefaults");
bytes32 private constant BASIC_ORDER_PARAMETERS_ARRAY_MAP_POSITION =
keccak256("seaport.BasicOrderParametersArrayDefaults");
+ bytes32 private constant EMPTY_BASIC_ORDER_PARAMETERS =
+ keccak256(
+ abi.encode(
+ BasicOrderParameters({
+ considerationToken: address(0),
+ considerationIdentifier: 0,
+ considerationAmount: 0,
+ offerer: payable(address(0)),
+ zone: address(0),
+ offerToken: address(0),
+ offerIdentifier: 0,
+ offerAmount: 0,
+ basicOrderType: BasicOrderType(0),
+ startTime: 0,
+ endTime: 0,
+ zoneHash: bytes32(0),
+ salt: 0,
+ offererConduitKey: bytes32(0),
+ fulfillerConduitKey: bytes32(0),
+ totalOriginalAdditionalRecipients: 0,
+ additionalRecipients: new AdditionalRecipient[](0),
+ signature: ""
+ })
+ )
+ );
+ /**
+ * @dev Clears a default BasicOrderParameters from storage.
+ *
+ * @param basicParameters the BasicOrderParameters to clear
+ */
function clear(BasicOrderParameters storage basicParameters) internal {
// uninitialized pointers take up no new memory (versus one word for initializing length-0)
AdditionalRecipient[] memory additionalRecipients;
@@ -54,6 +86,11 @@ library BasicOrderParametersLib {
basicParameters.signature = new bytes(0);
}
+ /**
+ * @dev Clears an array of BasicOrderParameters from storage.
+ *
+ * @param basicParametersArray the name of the default to clear
+ */
function clear(
BasicOrderParameters[] storage basicParametersArray
) internal {
@@ -64,18 +101,24 @@ library BasicOrderParametersLib {
}
/**
- * @notice clears a default BasicOrderParameters from storage
+ * @dev Clears a default BasicOrderParameters from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
mapping(string => BasicOrderParameters)
- storage orderComponentsMap = _orderComponentsMap();
- BasicOrderParameters storage basicParameters = orderComponentsMap[
+ storage orderParametersMap = _orderParametersMap();
+ BasicOrderParameters storage basicParameters = orderParametersMap[
defaultName
];
basicParameters.clear();
}
+ /**
+ * @dev Creates an empty BasicOrderParameters.
+ *
+ * @return item the default BasicOrderParameters
+ */
function empty() internal pure returns (BasicOrderParameters memory item) {
AdditionalRecipient[] memory additionalRecipients;
item = BasicOrderParameters({
@@ -101,59 +144,91 @@ library BasicOrderParametersLib {
}
/**
- * @notice gets a default BasicOrderParameters from storage
+ * @dev Gets a default BasicOrderParameters from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the selected default BasicOrderParameters
*/
function fromDefault(
string memory defaultName
) internal view returns (BasicOrderParameters memory item) {
mapping(string => BasicOrderParameters)
- storage orderComponentsMap = _orderComponentsMap();
- item = orderComponentsMap[defaultName];
+ storage orderParametersMap = _orderParametersMap();
+ item = orderParametersMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_BASIC_ORDER_PARAMETERS) {
+ revert("Empty BasicOrderParameters selected.");
+ }
}
+ /**
+ * @dev Gets a default BasicOrderParameters array from storage.
+ *
+ * @param defaultName the name of the default array for retrieval
+ *
+ * @return items the selected default BasicOrderParameters array
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (BasicOrderParameters[] memory items) {
mapping(string => BasicOrderParameters[])
- storage orderComponentsArrayMap = _orderComponentsArrayMap();
- items = orderComponentsArrayMap[defaultName];
+ storage orderParametersArrayMap = _orderParametersArrayMap();
+ items = orderParametersArrayMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty BasicOrderParameters array selected.");
+ }
}
/**
- * @notice saves an BasicOrderParameters as a named default
- * @param orderComponents the BasicOrderParameters to save as a default
- * @param defaultName the name of the default for retrieval
+ * @dev Saves a BasicOrderParameters as a named default.
+ *
+ * @param orderParameters the BasicOrderParameters to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _orderParameters the saved BasicOrderParameters
*/
function saveDefault(
- BasicOrderParameters memory orderComponents,
+ BasicOrderParameters memory orderParameters,
string memory defaultName
- ) internal returns (BasicOrderParameters memory _orderComponents) {
+ ) internal returns (BasicOrderParameters memory _orderParameters) {
mapping(string => BasicOrderParameters)
- storage orderComponentsMap = _orderComponentsMap();
- BasicOrderParameters storage destination = orderComponentsMap[
+ storage orderParametersMap = _orderParametersMap();
+ BasicOrderParameters storage destination = orderParametersMap[
defaultName
];
- StructCopier.setBasicOrderParameters(destination, orderComponents);
- return orderComponents;
+ StructCopier.setBasicOrderParameters(destination, orderParameters);
+ return orderParameters;
}
+ /**
+ * @dev Saves an BasicOrderParameters array as a named default.
+ *
+ * @param orderParameters the BasicOrderParameters array to save as a default
+ * @param defaultName the name of the default array for retrieval
+ *
+ * @return _orderParameters the saved BasicOrderParameters array
+ */
function saveDefaultMany(
- BasicOrderParameters[] memory orderComponents,
+ BasicOrderParameters[] memory orderParameters,
string memory defaultName
- ) internal returns (BasicOrderParameters[] memory _orderComponents) {
+ ) internal returns (BasicOrderParameters[] memory _orderParameters) {
mapping(string => BasicOrderParameters[])
- storage orderComponentsArrayMap = _orderComponentsArrayMap();
- BasicOrderParameters[] storage destination = orderComponentsArrayMap[
+ storage orderParametersArrayMap = _orderParametersArrayMap();
+ BasicOrderParameters[] storage destination = orderParametersArrayMap[
defaultName
];
- StructCopier.setBasicOrderParameters(destination, orderComponents);
- return orderComponents;
+ StructCopier.setBasicOrderParameters(destination, orderParameters);
+ return orderParameters;
}
/**
- * @notice makes a copy of an BasicOrderParameters in-memory
+ * @dev Makes a copy of an BasicOrderParameters in-memory.
+ *
* @param item the BasicOrderParameters to make a copy of in-memory
+ *
+ * @return copy the copied BasicOrderParameters
*/
function copy(
BasicOrderParameters memory item
@@ -183,38 +258,60 @@ library BasicOrderParametersLib {
}
/**
- * @notice gets the storage position of the default BasicOrderParameters map
+ * @dev Gets the storage position of the default BasicOrderParameters map.
+ *
+ * @return orderParametersMap the storage position of the default
+ * BasicOrderParameters map
*/
- function _orderComponentsMap()
+ function _orderParametersMap()
private
pure
returns (
- mapping(string => BasicOrderParameters) storage orderComponentsMap
+ mapping(string => BasicOrderParameters) storage orderParametersMap
)
{
bytes32 position = BASIC_ORDER_PARAMETERS_MAP_POSITION;
assembly {
- orderComponentsMap.slot := position
+ orderParametersMap.slot := position
}
}
- function _orderComponentsArrayMap()
+ /**
+ * @dev Gets the storage position of the default BasicOrderParameters array
+ * map.
+ *
+ * @return orderParametersArrayMap the storage position of the default
+ * BasicOrderParameters array map
+ */
+ function _orderParametersArrayMap()
private
pure
returns (
mapping(string => BasicOrderParameters[])
- storage orderComponentsArrayMap
+ storage orderParametersArrayMap
)
{
bytes32 position = BASIC_ORDER_PARAMETERS_ARRAY_MAP_POSITION;
assembly {
- orderComponentsArrayMap.slot := position
+ orderParametersArrayMap.slot := position
}
}
- // methods for configuring a single of each of an in-memory BasicOrderParameters's fields, which modifies the
- // BasicOrderParameters in-memory and returns it
+ // Methods for configuring a single of each of an in-memory
+ // BasicOrderParameters's fields, which modify the BasicOrderParameters
+ // struct in-memory and return it.
+ /**
+ * @dev Sets the considerationToken field of a BasicOrderParameters
+ * in-memory.
+ *
+ * @param item the BasicOrderParameters to set the considerationToken field
+ * of in-memory.
+ * @param value the value to set the considerationToken field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withConsiderationToken(
BasicOrderParameters memory item,
address value
@@ -223,6 +320,17 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the considerationIdentifier field of a BasicOrderParameters
+ * in-memory.
+ *
+ * @param item the BasicOrderParameters to set the considerationIdentifier
+ * field of in-memory.
+ * @param value the value to set the considerationIdentifier field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withConsiderationIdentifier(
BasicOrderParameters memory item,
uint256 value
@@ -231,6 +339,17 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the considerationAmount field of a BasicOrderParameters
+ * in-memory.
+ *
+ * @param item the BasicOrderParameters to set the considerationAmount field
+ * of in-memory.
+ * @param value the value to set the considerationAmount field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withConsiderationAmount(
BasicOrderParameters memory item,
uint256 value
@@ -239,6 +358,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the offerer field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the offerer field of
+ * in-memory.
+ * @param value the value to set the offerer field of the BasicOrderParameters
+ * to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withOfferer(
BasicOrderParameters memory item,
address value
@@ -247,6 +376,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the zone field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the zone field of
+ * in-memory.
+ * @param value the value to set the zone field of the BasicOrderParameters
+ * to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withZone(
BasicOrderParameters memory item,
address value
@@ -255,6 +394,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the offerToken field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the offerToken field of
+ * in-memory.
+ * @param value the value to set the offerToken field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withOfferToken(
BasicOrderParameters memory item,
address value
@@ -263,6 +412,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the offerIdentifier field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the offerIdentifier field of
+ * in-memory.
+ * @param value the value to set the offerIdentifier field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withOfferIdentifier(
BasicOrderParameters memory item,
uint256 value
@@ -271,6 +430,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the offerAmount field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the offerAmount field of
+ * in-memory.
+ * @param value the value to set the offerAmount field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withOfferAmount(
BasicOrderParameters memory item,
uint256 value
@@ -279,6 +448,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the basicOrderType field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the basicOrderType field of
+ * in-memory.
+ * @param value the value to set the basicOrderType field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withBasicOrderType(
BasicOrderParameters memory item,
BasicOrderType value
@@ -287,6 +466,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the startTime field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the startTime field of
+ * in-memory.
+ * @param value the value to set the startTime field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withStartTime(
BasicOrderParameters memory item,
uint256 value
@@ -295,6 +484,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the endTime field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the endTime field of
+ * in-memory.
+ * @param value the value to set the endTime field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withEndTime(
BasicOrderParameters memory item,
uint256 value
@@ -303,6 +502,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the zoneHash field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the zoneHash field of
+ * in-memory.
+ * @param value the value to set the zoneHash field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withZoneHash(
BasicOrderParameters memory item,
bytes32 value
@@ -311,6 +520,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the salt field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the salt field of
+ * in-memory.
+ * @param value the value to set the salt field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withSalt(
BasicOrderParameters memory item,
uint256 value
@@ -319,6 +538,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the offererConduitKey field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the offererConduitKey field of
+ * in-memory.
+ * @param value the value to set the offererConduitKey field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withOffererConduitKey(
BasicOrderParameters memory item,
bytes32 value
@@ -327,6 +556,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the fulfillerConduitKey field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the fulfillerConduitKey field of
+ * in-memory.
+ * @param value the value to set the fulfillerConduitKey field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withFulfillerConduitKey(
BasicOrderParameters memory item,
bytes32 value
@@ -335,6 +574,17 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the totalOriginalAdditionalRecipients field of a
+ * BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the
+ * totalOriginalAdditionalRecipients field of in-memory.
+ * @param value the value to set the totalOriginalAdditionalRecipients field
+ * of the BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withTotalOriginalAdditionalRecipients(
BasicOrderParameters memory item,
uint256 value
@@ -343,6 +593,17 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the additionalRecipients field of a BasicOrderParameters
+ * in-memory.
+ *
+ * @param item the BasicOrderParameters to set the additionalRecipients
+ * field of in-memory.
+ * @param value the value to set the additionalRecipients field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withAdditionalRecipients(
BasicOrderParameters memory item,
AdditionalRecipient[] memory value
@@ -351,6 +612,16 @@ library BasicOrderParametersLib {
return item;
}
+ /**
+ * @dev Sets the signature field of a BasicOrderParameters in-memory.
+ *
+ * @param item the BasicOrderParameters to set the signature field of
+ * in-memory.
+ * @param value the value to set the signature field of the
+ * BasicOrderParameters to set in-memory.
+ *
+ * @custom:return item the modified BasicOrderParameters
+ */
function withSignature(
BasicOrderParameters memory item,
bytes memory value
diff --git a/contracts/helpers/sol/lib/ConsiderationItemLib.sol b/contracts/helpers/sol/lib/ConsiderationItemLib.sol
index dbce2fb99..5f08dc4bd 100644
--- a/contracts/helpers/sol/lib/ConsiderationItemLib.sol
+++ b/contracts/helpers/sol/lib/ConsiderationItemLib.sol
@@ -3,17 +3,46 @@ pragma solidity ^0.8.17;
import {
ConsiderationItem,
- ReceivedItem
+ OfferItem,
+ ReceivedItem,
+ SpentItem
} from "../../../lib/ConsiderationStructs.sol";
+
import { ItemType } from "../../../lib/ConsiderationEnums.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title ConsiderationItemLib
+ * @author James Wenzel (emo.eth)
+ * @notice ConsiderationItemLib is a library for managing ConsiderationItem
+ * structs and arrays. It allows chaining of functions to make
+ * struct creation more readable.
+ */
library ConsiderationItemLib {
bytes32 private constant CONSIDERATION_ITEM_MAP_POSITION =
keccak256("seaport.ConsiderationItemDefaults");
bytes32 private constant CONSIDERATION_ITEMS_MAP_POSITION =
keccak256("seaport.ConsiderationItemsDefaults");
+ bytes32 private constant EMPTY_CONSIDERATION_ITEM =
+ keccak256(
+ abi.encode(
+ ConsiderationItem({
+ itemType: ItemType(0),
+ token: address(0),
+ identifierOrCriteria: 0,
+ startAmount: 0,
+ endAmount: 0,
+ recipient: payable(address(0))
+ })
+ )
+ );
+ /**
+ * @dev Clears a ConsiderationItem from storage.
+ *
+ * @param item the ConsiderationItem to clear.
+ */
function _clear(ConsiderationItem storage item) internal {
// clear all fields
item.itemType = ItemType.NATIVE;
@@ -25,10 +54,10 @@ library ConsiderationItemLib {
}
/**
- * @notice clears a default ConsiderationItem from storage
+ * @dev Clears a named default ConsiderationItem from storage.
+ *
* @param defaultName the name of the default to clear
*/
-
function clear(string memory defaultName) internal {
mapping(string => ConsiderationItem)
storage considerationItemMap = _considerationItemMap();
@@ -36,6 +65,11 @@ library ConsiderationItemLib {
_clear(item);
}
+ /**
+ * @dev Clears an array of ConsiderationItems from storage.
+ *
+ * @param defaultsName the name of the array to clear
+ */
function clearMany(string memory defaultsName) internal {
mapping(string => ConsiderationItem[])
storage considerationItemsMap = _considerationItemsMap();
@@ -47,8 +81,11 @@ library ConsiderationItemLib {
}
/**
- * @notice gets a default ConsiderationItem from storage
+ * @dev Gets a default ConsiderationItem from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the ConsiderationItem retrieved from storage
*/
function fromDefault(
string memory defaultName
@@ -56,16 +93,37 @@ library ConsiderationItemLib {
mapping(string => ConsiderationItem)
storage considerationItemMap = _considerationItemMap();
item = considerationItemMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_CONSIDERATION_ITEM) {
+ revert("Empty ConsiderationItem selected.");
+ }
}
+ /**
+ * @dev Gets an array of default ConsiderationItems from storage.
+ *
+ * @param defaultsName the name of the array for retrieval
+ *
+ * @return items the array of ConsiderationItems retrieved from storage
+ */
function fromDefaultMany(
string memory defaultsName
) internal view returns (ConsiderationItem[] memory items) {
mapping(string => ConsiderationItem[])
storage considerationItemsMap = _considerationItemsMap();
items = considerationItemsMap[defaultsName];
+
+ if (items.length == 0) {
+ revert("Empty ConsiderationItem array selected.");
+ }
}
+ /**
+ * @dev Creates an empty ConsiderationItem.
+ *
+ * @custom:return considerationItemMap the storage location of the default
+ * ConsiderationItem map
+ */
function empty() internal pure returns (ConsiderationItem memory) {
return
ConsiderationItem({
@@ -79,9 +137,12 @@ library ConsiderationItemLib {
}
/**
- * @notice saves an ConsiderationItem as a named default
+ * @dev Saves a ConsiderationItem as a named default.
+ *
* @param considerationItem the ConsiderationItem to save as a default
- * @param defaultName the name of the default for retrieval
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _considerationItem the saved ConsiderationItem
*/
function saveDefault(
ConsiderationItem memory considerationItem,
@@ -93,6 +154,15 @@ library ConsiderationItemLib {
return considerationItem;
}
+ /**
+ * @dev Saves an array of ConsiderationItems as a named default.
+ *
+ * @param considerationItems the array of ConsiderationItems to save as a
+ * default
+ * @param defaultsName the name of the default array for retrieval
+ *
+ * @return _considerationItems the saved array of ConsiderationItems
+ */
function saveDefaultMany(
ConsiderationItem[] memory considerationItems,
string memory defaultsName
@@ -106,8 +176,11 @@ library ConsiderationItemLib {
}
/**
- * @notice makes a copy of an ConsiderationItem in-memory
+ * @dev Makes a copy of an ConsiderationItem in-memory.
+ *
* @param item the ConsiderationItem to make a copy of in-memory
+ *
+ * @custom:return copy the copy of the ConsiderationItem
*/
function copy(
ConsiderationItem memory item
@@ -123,27 +196,37 @@ library ConsiderationItemLib {
});
}
+ /**
+ * @dev Makes a copy of an array of ConsiderationItems in-memory.
+ *
+ * @param items the array of ConsiderationItems to make a copy of in-memory
+ *
+ * @custom:return copy the copy of the array of ConsiderationItems
+ */
function copy(
- ConsiderationItem[] memory item
+ ConsiderationItem[] memory items
) internal pure returns (ConsiderationItem[] memory) {
ConsiderationItem[] memory copies = new ConsiderationItem[](
- item.length
+ items.length
);
- for (uint256 i = 0; i < item.length; i++) {
+ for (uint256 i = 0; i < items.length; i++) {
copies[i] = ConsiderationItem({
- itemType: item[i].itemType,
- token: item[i].token,
- identifierOrCriteria: item[i].identifierOrCriteria,
- startAmount: item[i].startAmount,
- endAmount: item[i].endAmount,
- recipient: item[i].recipient
+ itemType: items[i].itemType,
+ token: items[i].token,
+ identifierOrCriteria: items[i].identifierOrCriteria,
+ startAmount: items[i].startAmount,
+ endAmount: items[i].endAmount,
+ recipient: items[i].recipient
});
}
return copies;
}
/**
- * @notice gets the storage position of the default ConsiderationItem map
+ * @dev Gets the storage position of the default ConsiderationItem map.
+ *
+ * @custom:return considerationItemMap the storage location of the default
+ * ConsiderationItem map
*/
function _considerationItemMap()
private
@@ -158,6 +241,13 @@ library ConsiderationItemLib {
}
}
+ /**
+ * @dev Gets the storage position of the default array of ConsiderationItems
+ * map.
+ *
+ * @custom:return considerationItemsMap the storage location of the default
+ * array of ConsiderationItems map
+ */
function _considerationItemsMap()
private
pure
@@ -171,15 +261,16 @@ library ConsiderationItemLib {
}
}
- // methods for configuring a single of each of an ConsiderationItem's fields, which modifies the ConsiderationItem
- // in-place and
- // returns it
+ // Methods for configuring a single of each of an ConsiderationItem's
+ // fields, which modify the ConsiderationItem in-place and return it.
/**
- * @notice sets the item type
- * @param item the ConsiderationItem to modify
+ * @dev Sets the item type.
+ *
+ * @param item the ConsiderationItem to modify
* @param itemType the item type to set
- * @return the modified ConsiderationItem
+ *
+ * @custom:return item the modified ConsiderationItem
*/
function withItemType(
ConsiderationItem memory item,
@@ -190,10 +281,12 @@ library ConsiderationItemLib {
}
/**
- * @notice sets the token address
- * @param item the ConsiderationItem to modify
+ * @dev Sets the token address.
+ *
+ * @param item the ConsiderationItem to modify
* @param token the token address to set
- * @return the modified ConsiderationItem
+ *
+ * @custom:return item the modified ConsiderationItem
*/
function withToken(
ConsiderationItem memory item,
@@ -204,10 +297,12 @@ library ConsiderationItemLib {
}
/**
- * @notice sets the identifier or criteria
+ * @dev Sets the identifier or criteria.
+ *
* @param item the ConsiderationItem to modify
* @param identifierOrCriteria the identifier or criteria to set
- * @return the modified ConsiderationItem
+ *
+ * @custom:return item the modified ConsiderationItem
*/
function withIdentifierOrCriteria(
ConsiderationItem memory item,
@@ -218,10 +313,12 @@ library ConsiderationItemLib {
}
/**
- * @notice sets the start amount
+ * @dev Sets the start amount.
+ *
* @param item the ConsiderationItem to modify
* @param startAmount the start amount to set
- * @return the modified ConsiderationItem
+ *
+ * @custom:return item the modified ConsiderationItem
*/
function withStartAmount(
ConsiderationItem memory item,
@@ -232,10 +329,12 @@ library ConsiderationItemLib {
}
/**
- * @notice sets the end amount
+ * @dev Sets the end amount.
+ *
* @param item the ConsiderationItem to modify
* @param endAmount the end amount to set
- * @return the modified ConsiderationItem
+ *
+ * @custom:return item the modified ConsiderationItem
*/
function withEndAmount(
ConsiderationItem memory item,
@@ -246,10 +345,29 @@ library ConsiderationItemLib {
}
/**
- * @notice sets the recipient
+ * @dev Sets the startAmount and endAmount of an ConsiderationItem.
+ *
+ * @param item the ConsiderationItem to modify
+ * @param amount the amount to set for the start and end amounts
+ *
+ * @custom:return item the modified ConsiderationItem
+ */
+ function withAmount(
+ ConsiderationItem memory item,
+ uint256 amount
+ ) internal pure returns (ConsiderationItem memory) {
+ item.startAmount = amount;
+ item.endAmount = amount;
+ return item;
+ }
+
+ /**
+ * @dev Sets the recipient.
+ *
* @param item the ConsiderationItem to modify
* @param recipient the recipient to set
- * @return the modified ConsiderationItem
+ *
+ * @custom:return item the modified ConsiderationItem
*/
function withRecipient(
ConsiderationItem memory item,
@@ -259,6 +377,13 @@ library ConsiderationItemLib {
return item;
}
+ /**
+ * @dev Converts an ConsiderationItem to a ReceivedItem.
+ *
+ * @param item the ConsiderationItem to convert
+ *
+ * @custom:return receivedItem the converted ReceivedItem
+ */
function toReceivedItem(
ConsiderationItem memory item
) internal pure returns (ReceivedItem memory) {
@@ -271,4 +396,49 @@ library ConsiderationItemLib {
recipient: item.recipient
});
}
+
+ function toReceivedItemArray(
+ ConsiderationItem[] memory items
+ ) internal pure returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](items.length);
+ for (uint256 i = 0; i < items.length; i++) {
+ receivedItems[i] = toReceivedItem(items[i]);
+ }
+ return receivedItems;
+ }
+
+ function toSpentItem(
+ ConsiderationItem memory item
+ ) internal pure returns (SpentItem memory) {
+ return
+ SpentItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifierOrCriteria,
+ amount: item.startAmount
+ });
+ }
+
+ function toSpentItemArray(
+ ConsiderationItem[] memory items
+ ) internal pure returns (SpentItem[] memory) {
+ SpentItem[] memory spentItems = new SpentItem[](items.length);
+ for (uint256 i = 0; i < items.length; i++) {
+ spentItems[i] = toSpentItem(items[i]);
+ }
+ return spentItems;
+ }
+
+ function toOfferItem(
+ ConsiderationItem memory item
+ ) internal pure returns (OfferItem memory) {
+ return
+ OfferItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifierOrCriteria: item.identifierOrCriteria,
+ startAmount: item.startAmount,
+ endAmount: item.endAmount
+ });
+ }
}
diff --git a/contracts/helpers/sol/lib/CriteriaResolverLib.sol b/contracts/helpers/sol/lib/CriteriaResolverLib.sol
index e2dd1c840..31cacce5a 100644
--- a/contracts/helpers/sol/lib/CriteriaResolverLib.sol
+++ b/contracts/helpers/sol/lib/CriteriaResolverLib.sol
@@ -1,27 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
-import {
- CriteriaResolver,
- OfferItem
-} from "../../../lib/ConsiderationStructs.sol";
+import { CriteriaResolver } from "../../../lib/ConsiderationStructs.sol";
+
import { Side } from "../../../lib/ConsiderationEnums.sol";
+
import { ArrayLib } from "./ArrayLib.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title CriteriaResolverLib
+ * @author James Wenzel (emo.eth)
+ * @notice CriteriaResolverLib is a library for managing CriteriaResolver
+ * structs and arrays. It allows chaining of functions to make
+ * struct creation more readable.
+ */
library CriteriaResolverLib {
bytes32 private constant CRITERIA_RESOLVER_MAP_POSITION =
keccak256("seaport.CriteriaResolverDefaults");
bytes32 private constant CRITERIA_RESOLVERS_MAP_POSITION =
keccak256("seaport.CriteriaResolversDefaults");
+ bytes32 private constant EMPTY_CRITERIA_RESOLVER =
+ keccak256(
+ abi.encode(
+ CriteriaResolver({
+ orderIndex: 0,
+ side: Side(0),
+ index: 0,
+ identifier: 0,
+ criteriaProof: new bytes32[](0)
+ })
+ )
+ );
using ArrayLib for bytes32[];
/**
- * @notice clears a default CriteriaResolver from storage
+ * @dev Clears a default CriteriaResolver from storage.
+ *
* @param defaultName the name of the default to clear
*/
-
function clear(string memory defaultName) internal {
mapping(string => CriteriaResolver)
storage criteriaResolverMap = _criteriaResolverMap();
@@ -30,6 +49,11 @@ library CriteriaResolverLib {
clear(resolver);
}
+ /**
+ * @dev Clears all fields on a CriteriaResolver.
+ *
+ * @param resolver the CriteriaResolver to clear
+ */
function clear(CriteriaResolver storage resolver) internal {
bytes32[] memory criteriaProof;
resolver.orderIndex = 0;
@@ -39,6 +63,11 @@ library CriteriaResolverLib {
ArrayLib.setBytes32s(resolver.criteriaProof, criteriaProof);
}
+ /**
+ * @dev Clears an array of CriteriaResolvers from storage.
+ *
+ * @param resolvers the CriteriaResolvers to clear
+ */
function clear(CriteriaResolver[] storage resolvers) internal {
while (resolvers.length > 0) {
clear(resolvers[resolvers.length - 1]);
@@ -47,29 +76,48 @@ library CriteriaResolverLib {
}
/**
- * @notice gets a default CriteriaResolver from storage
- * @param defaultName the name of the default for retrieval
+ * @dev Gets a default CriteriaResolver from storage.
+ *
+ * @param item the name of the default for retrieval
*/
function fromDefault(
string memory defaultName
- ) internal view returns (CriteriaResolver memory resolver) {
+ ) internal view returns (CriteriaResolver memory item) {
mapping(string => CriteriaResolver)
storage criteriaResolverMap = _criteriaResolverMap();
- resolver = criteriaResolverMap[defaultName];
+ item = criteriaResolverMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_CRITERIA_RESOLVER) {
+ revert("Empty CriteriaResolver selected.");
+ }
}
+ /**
+ * @dev Gets an array of CriteriaResolvers from storage.
+ *
+ * @param defaultsName the name of the default array for retrieval
+ *
+ * @return items the CriteriaResolvers retrieved from storage
+ */
function fromDefaultMany(
string memory defaultsName
- ) internal view returns (CriteriaResolver[] memory resolvers) {
+ ) internal view returns (CriteriaResolver[] memory items) {
mapping(string => CriteriaResolver[])
storage criteriaResolversMap = _criteriaResolversMap();
- resolvers = criteriaResolversMap[defaultsName];
+ items = criteriaResolversMap[defaultsName];
+
+ if (items.length == 0) {
+ revert("Empty CriteriaResolver array selected.");
+ }
}
/**
- * @notice saves an CriteriaResolver as a named default
+ * @dev Saves an CriteriaResolver as a named default.
+ *
* @param criteriaResolver the CriteriaResolver to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _criteriaResolver the CriteriaResolver that was saved
*/
function saveDefault(
CriteriaResolver memory criteriaResolver,
@@ -89,6 +137,14 @@ library CriteriaResolverLib {
return criteriaResolver;
}
+ /**
+ * @dev Saves an array of CriteriaResolvers as a named default.
+ *
+ * @param criteriaResolvers the CriteriaResolvers to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _criteriaResolvers the CriteriaResolvers that were saved
+ */
function saveDefaultMany(
CriteriaResolver[] memory criteriaResolvers,
string memory defaultName
@@ -105,8 +161,11 @@ library CriteriaResolverLib {
}
/**
- * @notice makes a copy of an CriteriaResolver in-memory
+ * @dev Makes a copy of a CriteriaResolver in-memory.
+ *
* @param resolver the CriteriaResolver to make a copy of in-memory
+ *
+ * @custom:return copiedItem the copied CriteriaResolver
*/
function copy(
CriteriaResolver memory resolver
@@ -121,6 +180,13 @@ library CriteriaResolverLib {
});
}
+ /**
+ * @dev Makes a copy of an array of CriteriaResolvers in-memory.
+ *
+ * @param resolvers the CriteriaResolvers to make a copy of in-memory
+ *
+ * @custom:return copiedItems the copied CriteriaResolvers
+ */
function copy(
CriteriaResolver[] memory resolvers
) internal pure returns (CriteriaResolver[] memory) {
@@ -133,6 +199,11 @@ library CriteriaResolverLib {
return copiedItems;
}
+ /**
+ * @dev Creates an empty CriteriaResolver.
+ *
+ * @custom:return emptyResolver the empty CriteriaResolver
+ */
function empty() internal pure returns (CriteriaResolver memory) {
bytes32[] memory proof;
return
@@ -146,7 +217,11 @@ library CriteriaResolverLib {
}
/**
- * @notice gets the storage position of the default CriteriaResolver map
+ * @dev Gets the storage position of the default CriteriaResolver map.
+ *
+ * @custom:return position the storage position of the default
+ * CriteriaResolver map.
+ *
*/
function _criteriaResolverMap()
private
@@ -161,6 +236,13 @@ library CriteriaResolverLib {
}
}
+ /**
+ * @dev Gets the storage position of the default CriteriaResolver array map.
+ *
+ * @custom:return position the storage position of the default
+ * CriteriaResolver array map.
+ *
+ */
function _criteriaResolversMap()
private
pure
@@ -174,10 +256,17 @@ library CriteriaResolverLib {
}
}
- // methods for configuring a single of each of an CriteriaResolver's fields, which modifies the CriteriaResolver
- // in-place and
- // returns it
+ // Methods for configuring a single of each of an CriteriaResolver's fields,
+ // which modify the CriteriaResolver in-place and return it.
+ /**
+ * @dev Sets the orderIndex of a CriteriaResolver.
+ *
+ * @param resolver the CriteriaResolver to set the orderIndex of
+ * @param orderIndex the orderIndex to set
+ *
+ * @return _resolver the CriteriaResolver with the orderIndex set
+ */
function withOrderIndex(
CriteriaResolver memory resolver,
uint256 orderIndex
@@ -186,6 +275,14 @@ library CriteriaResolverLib {
return resolver;
}
+ /**
+ * @dev Sets the side of a CriteriaResolver.
+ *
+ * @param resolver the CriteriaResolver to set the side of
+ * @param side the side to set
+ *
+ * @return _resolver the CriteriaResolver with the side set
+ */
function withSide(
CriteriaResolver memory resolver,
Side side
@@ -194,6 +291,14 @@ library CriteriaResolverLib {
return resolver;
}
+ /**
+ * @dev Sets the index of a CriteriaResolver.
+ *
+ * @param resolver the CriteriaResolver to set the index of
+ * @param index the index to set
+ *
+ * @return _resolver the CriteriaResolver with the index set
+ */
function withIndex(
CriteriaResolver memory resolver,
uint256 index
@@ -202,6 +307,14 @@ library CriteriaResolverLib {
return resolver;
}
+ /**
+ * @dev Sets the identifier of a CriteriaResolver.
+ *
+ * @param resolver the CriteriaResolver to set the identifier of
+ * @param identifier the identifier to set
+ *
+ * @return _resolver the CriteriaResolver with the identifier set
+ */
function withIdentifier(
CriteriaResolver memory resolver,
uint256 identifier
@@ -210,6 +323,14 @@ library CriteriaResolverLib {
return resolver;
}
+ /**
+ * @dev Sets the criteriaProof of a CriteriaResolver.
+ *
+ * @param resolver the CriteriaResolver to set the criteriaProof of
+ * @param criteriaProof the criteriaProof to set
+ *
+ * @return _resolver the CriteriaResolver with the criteriaProof set
+ */
function withCriteriaProof(
CriteriaResolver memory resolver,
bytes32[] memory criteriaProof
diff --git a/contracts/helpers/sol/lib/ExecutionLib.sol b/contracts/helpers/sol/lib/ExecutionLib.sol
index a77356237..142eba411 100644
--- a/contracts/helpers/sol/lib/ExecutionLib.sol
+++ b/contracts/helpers/sol/lib/ExecutionLib.sol
@@ -1,22 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
-import { Execution, ReceivedItem } from "../../../lib/ConsiderationStructs.sol";
+import {
+ Execution,
+ ItemType,
+ ReceivedItem
+} from "../../../lib/ConsiderationStructs.sol";
+
import { ReceivedItemLib } from "./ReceivedItemLib.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title ExecutionLib
+ * @author James Wenzel (emo.eth)
+ * @notice ExecutionLib is a library for managing Execution structs and arrays.
+ * It allows chaining of functions to make struct creation more
+ * readable.
+ */
library ExecutionLib {
bytes32 private constant EXECUTION_MAP_POSITION =
keccak256("seaport.ExecutionDefaults");
bytes32 private constant EXECUTIONS_MAP_POSITION =
keccak256("seaport.ExecutionsDefaults");
+ bytes32 private constant EMPTY_EXECUTION =
+ keccak256(
+ abi.encode(
+ Execution({
+ item: ReceivedItem({
+ itemType: ItemType(0),
+ token: address(0),
+ identifier: 0,
+ amount: 0,
+ recipient: payable(address(0))
+ }),
+ offerer: address(0),
+ conduitKey: bytes32(0)
+ })
+ )
+ );
using ReceivedItemLib for ReceivedItem;
using ReceivedItemLib for ReceivedItem[];
/**
- * @notice clears a default Execution from storage
- * @param defaultName the name of the default to clear
+ * @dev Clears a default Execution from storage.
+ *
+ * @param defaultName the name of the default to clear.
*/
function clear(string memory defaultName) internal {
mapping(string => Execution) storage executionMap = _executionMap();
@@ -24,6 +54,11 @@ library ExecutionLib {
clear(item);
}
+ /**
+ * @dev Clears all fields on an Execution.
+ *
+ * @param execution the Execution to clear
+ */
function clear(Execution storage execution) internal {
// clear all fields
execution.item = ReceivedItemLib.empty();
@@ -31,6 +66,11 @@ library ExecutionLib {
execution.conduitKey = bytes32(0);
}
+ /**
+ * @dev Clears an array of Executions from storage.
+ *
+ * @param executions the name of the default to clear
+ */
function clear(Execution[] storage executions) internal {
while (executions.length > 0) {
clear(executions[executions.length - 1]);
@@ -39,27 +79,48 @@ library ExecutionLib {
}
/**
- * @notice gets a default Execution from storage
+ * @dev Gets a default Execution from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the Execution retrieved from storage
*/
function fromDefault(
string memory defaultName
) internal view returns (Execution memory item) {
mapping(string => Execution) storage executionMap = _executionMap();
item = executionMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_EXECUTION) {
+ revert("Empty Execution selected.");
+ }
}
+ /**
+ * @dev Gets an array of default Executions from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the Executions retrieved from storage
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (Execution[] memory items) {
mapping(string => Execution[]) storage executionsMap = _executionsMap();
items = executionsMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty Execution array selected.");
+ }
}
/**
- * @notice saves an Execution as a named default
- * @param execution the Execution to save as a default
+ * @dev Saves an Execution as a named default.
+ *
+ * @param execution the Execution to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _execution the Execution saved as a default
*/
function saveDefault(
Execution memory execution,
@@ -70,6 +131,14 @@ library ExecutionLib {
return execution;
}
+ /**
+ * @dev Saves an array of Executions as a named default.
+ *
+ * @param executions the Executions to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _executions the Executions saved as a default
+ */
function saveDefaultMany(
Execution[] memory executions,
string memory defaultName
@@ -80,8 +149,11 @@ library ExecutionLib {
}
/**
- * @notice makes a copy of an Execution in-memory
+ * @dev Makes a copy of an Execution in-memory.
+ *
* @param item the Execution to make a copy of in-memory
+ *
+ * @custom:return copy the copy of the Execution in-memory
*/
function copy(
Execution memory item
@@ -94,16 +166,28 @@ library ExecutionLib {
});
}
+ /**
+ * @dev Makes a copy of an array of Executions in-memory.
+ *
+ * @param items the array of Executions to make a copy of in-memory
+ *
+ * @custom:return copy the copy of the array of Executions in-memory
+ */
function copy(
- Execution[] memory item
+ Execution[] memory items
) internal pure returns (Execution[] memory) {
- Execution[] memory copies = new Execution[](item.length);
- for (uint256 i = 0; i < item.length; i++) {
- copies[i] = copy(item[i]);
+ Execution[] memory copies = new Execution[](items.length);
+ for (uint256 i = 0; i < items.length; i++) {
+ copies[i] = copy(items[i]);
}
return copies;
}
+ /**
+ * @dev Creates an empty Execution.
+ *
+ * @custom:return empty the empty Execution
+ */
function empty() internal pure returns (Execution memory) {
return
Execution({
@@ -114,7 +198,9 @@ library ExecutionLib {
}
/**
- * @notice gets the storage position of the default Execution map
+ * @dev Gets the storage position of the default Execution map.
+ *
+ * @return executionMap the storage position of the default Execution map
*/
function _executionMap()
private
@@ -127,6 +213,11 @@ library ExecutionLib {
}
}
+ /**
+ * @dev Gets the storage position of the default array of Executions map.
+ *
+ * @return executionsMap the storage position of the default Executions map
+ */
function _executionsMap()
private
pure
@@ -138,10 +229,17 @@ library ExecutionLib {
}
}
- // methods for configuring a single of each of an Execution's fields, which modifies the Execution
- // in-place and
- // returns it
+ // Methods for configuring a single of each of an Execution's fields, which
+ // modify the Execution in-place and return it.
+ /**
+ * @dev Configures an Execution's item field.
+ *
+ * @param execution the Execution to configure
+ * @param item the value to set the Execution's item field to
+ *
+ * @return _execution the configured Execution
+ */
function withItem(
Execution memory execution,
ReceivedItem memory item
@@ -150,6 +248,14 @@ library ExecutionLib {
return execution;
}
+ /**
+ * @dev Configures an Execution's offerer field.
+ *
+ * @param execution the Execution to configure
+ * @param offerer the value to set the Execution's offerer field to
+ *
+ * @return _execution the configured Execution
+ */
function withOfferer(
Execution memory execution,
address offerer
@@ -158,6 +264,14 @@ library ExecutionLib {
return execution;
}
+ /**
+ * @dev Configures an Execution's conduitKey field.
+ *
+ * @param execution the Execution to configure
+ * @param conduitKey the value to set the Execution's conduitKey field to
+ *
+ * @return _execution the configured Execution
+ */
function withConduitKey(
Execution memory execution,
bytes32 conduitKey
diff --git a/contracts/helpers/sol/lib/FulfillmentComponentLib.sol b/contracts/helpers/sol/lib/FulfillmentComponentLib.sol
index e5577a0d3..160ae03c4 100644
--- a/contracts/helpers/sol/lib/FulfillmentComponentLib.sol
+++ b/contracts/helpers/sol/lib/FulfillmentComponentLib.sol
@@ -1,14 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
-import {
- FulfillmentComponent,
- OfferItem
-} from "../../../lib/ConsiderationStructs.sol";
-import { Side } from "../../../lib/ConsiderationEnums.sol";
+import { FulfillmentComponent } from "../../../lib/ConsiderationStructs.sol";
+
import { ArrayLib } from "./ArrayLib.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title FulfillmentComponentLib
+ * @author James Wenzel (emo.eth)
+ * @notice FulfillmentComponentLib is a library for managing FulfillmentComponent
+ * structs and arrays. It allows chaining of functions to make
+ * struct creation more readable.
+ */
library FulfillmentComponentLib {
bytes32 private constant FULFILLMENT_COMPONENT_MAP_POSITION =
keccak256("seaport.FulfillmentComponentDefaults");
@@ -18,10 +23,10 @@ library FulfillmentComponentLib {
using ArrayLib for bytes32[];
/**
- * @notice clears a default FulfillmentComponent from storage
+ * @dev Clears a default FulfillmentComponent from storage.
+ *
* @param defaultName the name of the default to clear
*/
-
function clear(string memory defaultName) internal {
mapping(string => FulfillmentComponent)
storage fulfillmentComponentMap = _fulfillmentComponentMap();
@@ -31,11 +36,21 @@ library FulfillmentComponentLib {
clear(component);
}
+ /**
+ * @dev Clears all fields on a FulfillmentComponent.
+ *
+ * @param component the FulfillmentComponent to clear
+ */
function clear(FulfillmentComponent storage component) internal {
component.orderIndex = 0;
component.itemIndex = 0;
}
+ /**
+ * @dev Clears an array of FulfillmentComponents from storage.
+ *
+ * @param components the FulfillmentComponents to clear
+ */
function clear(FulfillmentComponent[] storage components) internal {
while (components.length > 0) {
clear(components[components.length - 1]);
@@ -44,29 +59,42 @@ library FulfillmentComponentLib {
}
/**
- * @notice gets a default FulfillmentComponent from storage
+ * @dev Gets a default FulfillmentComponent from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the FulfillmentComponent retrieved from storage
*/
function fromDefault(
string memory defaultName
- ) internal view returns (FulfillmentComponent memory component) {
+ ) internal view returns (FulfillmentComponent memory item) {
mapping(string => FulfillmentComponent)
storage fulfillmentComponentMap = _fulfillmentComponentMap();
- component = fulfillmentComponentMap[defaultName];
+ item = fulfillmentComponentMap[defaultName];
}
+ /**
+ * @dev Gets an array of default FulfillmentComponents from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the FulfillmentComponents retrieved from storage
+ */
function fromDefaultMany(
string memory defaultName
- ) internal view returns (FulfillmentComponent[] memory components) {
+ ) internal view returns (FulfillmentComponent[] memory items) {
mapping(string => FulfillmentComponent[])
storage fulfillmentComponentMap = _fulfillmentComponentsMap();
- components = fulfillmentComponentMap[defaultName];
+ items = fulfillmentComponentMap[defaultName];
}
/**
- * @notice saves an FulfillmentComponent as a named default
+ * @dev Saves an FulfillmentComponent as a named default.
+ *
* @param fulfillmentComponent the FulfillmentComponent to save as a default
- * @param defaultName the name of the default for retrieval
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _fulfillmentComponent the FulfillmentComponent saved as a default
*/
function saveDefault(
FulfillmentComponent memory fulfillmentComponent,
@@ -82,6 +110,16 @@ library FulfillmentComponentLib {
return fulfillmentComponent;
}
+ /**
+ * @dev Saves an array of FulfillmentComponents as a named default.
+ *
+ * @param fulfillmentComponents the FulfillmentComponents to save as a
+ * default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _fulfillmentComponents the FulfillmentComponents saved as a
+ * default
+ */
function saveDefaultMany(
FulfillmentComponent[] memory fulfillmentComponents,
string memory defaultName
@@ -101,8 +139,11 @@ library FulfillmentComponentLib {
}
/**
- * @notice makes a copy of an FulfillmentComponent in-memory
- * @param component the FulfillmentComponent to make a copy of in-memory
+ * @dev Makes a copy of an FulfillmentComponent in-memory.
+ *
+ * @param component the FulfillmentComponent to make a copy of in-memory.
+ *
+ * @return copiedComponent the copied FulfillmentComponent
*/
function copy(
FulfillmentComponent memory component
@@ -114,6 +155,13 @@ library FulfillmentComponentLib {
});
}
+ /**
+ * @dev Makes a copy of an array of FulfillmentComponents in-memory.
+ *
+ * @param components the FulfillmentComponents to make a copy of in-memory.
+ *
+ * @return copiedComponents the copied FulfillmentComponents
+ */
function copy(
FulfillmentComponent[] memory components
) internal pure returns (FulfillmentComponent[] memory) {
@@ -126,12 +174,22 @@ library FulfillmentComponentLib {
return copiedItems;
}
+ /**
+ * @dev Creates an empty FulfillmentComponent.
+ *
+ * @return component the empty FulfillmentComponent
+ *
+ * @custom:return emptyComponent the empty FulfillmentComponent
+ */
function empty() internal pure returns (FulfillmentComponent memory) {
return FulfillmentComponent({ orderIndex: 0, itemIndex: 0 });
}
/**
- * @notice gets the storage position of the default FulfillmentComponent map
+ * @dev Gets the storage position of the default FulfillmentComponent map.
+ *
+ * @custom:return position the storage position of the default
+ * FulfillmentComponent
*/
function _fulfillmentComponentMap()
private
@@ -147,6 +205,13 @@ library FulfillmentComponentLib {
}
}
+ /**
+ * @dev Gets the storage position of the default FulfillmentComponent array
+ * map.
+ *
+ * @custom:return position the storage position of the default
+ * FulfillmentComponent array
+ */
function _fulfillmentComponentsMap()
private
pure
@@ -161,11 +226,17 @@ library FulfillmentComponentLib {
}
}
- // methods for configuring a single of each of an FulfillmentComponent's fields, which modifies the
- // FulfillmentComponent
- // in-place and
- // returns it
+ // Methods for configuring a single of each of a FulfillmentComponent's
+ // fields, which modify the FulfillmentComponent in-place and return it.
+ /**
+ * @dev Sets the orderIndex of a FulfillmentComponent.
+ *
+ * @param component the FulfillmentComponent to set the orderIndex of
+ * @param orderIndex the orderIndex to set
+ *
+ * @return component the FulfillmentComponent with the orderIndex set
+ */
function withOrderIndex(
FulfillmentComponent memory component,
uint256 orderIndex
@@ -174,6 +245,14 @@ library FulfillmentComponentLib {
return component;
}
+ /**
+ * @dev Sets the itemIndex of a FulfillmentComponent.
+ *
+ * @param component the FulfillmentComponent to set the itemIndex of
+ * @param itemIndex the itemIndex to set
+ *
+ * @return component the FulfillmentComponent with the itemIndex set
+ */
function withItemIndex(
FulfillmentComponent memory component,
uint256 itemIndex
diff --git a/contracts/helpers/sol/lib/FulfillmentLib.sol b/contracts/helpers/sol/lib/FulfillmentLib.sol
index e7c493a70..392a85d0e 100644
--- a/contracts/helpers/sol/lib/FulfillmentLib.sol
+++ b/contracts/helpers/sol/lib/FulfillmentLib.sol
@@ -5,11 +5,18 @@ import {
Fulfillment,
FulfillmentComponent
} from "../../../lib/ConsiderationStructs.sol";
-import { Side } from "../../../lib/ConsiderationEnums.sol";
-import { ArrayLib } from "./ArrayLib.sol";
+
import { FulfillmentComponentLib } from "./FulfillmentComponentLib.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title FulfillmentLib
+ * @author James Wenzel (emo.eth)
+ * @notice FulfillmentLib is a library for managing Fulfillment structs and
+ * arrays. It allows chaining of functions to make struct creation more
+ * readable.
+ */
library FulfillmentLib {
bytes32 private constant FULFILLMENT_MAP_POSITION =
keccak256("seaport.FulfillmentDefaults");
@@ -20,10 +27,10 @@ library FulfillmentLib {
using StructCopier for FulfillmentComponent[];
/**
- * @notice clears a default Fulfillment from storage
+ * @dev Clears a default Fulfillment from storage.
+ *
* @param defaultName the name of the default to clear
*/
-
function clear(string memory defaultName) internal {
mapping(string => Fulfillment)
storage fulfillmentMap = _fulfillmentMap();
@@ -37,29 +44,42 @@ library FulfillmentLib {
}
/**
- * @notice gets a default Fulfillment from storage
+ * @dev Gets a default Fulfillment from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the Fulfillment retrieved from storage
*/
function fromDefault(
string memory defaultName
- ) internal view returns (Fulfillment memory _fulfillment) {
+ ) internal view returns (Fulfillment memory item) {
mapping(string => Fulfillment)
storage fulfillmentMap = _fulfillmentMap();
- _fulfillment = fulfillmentMap[defaultName];
+ item = fulfillmentMap[defaultName];
}
+ /**
+ * @dev Gets a default Fulfillment array from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the Fulfillment array retrieved from storage
+ */
function fromDefaultMany(
string memory defaultName
- ) internal view returns (Fulfillment[] memory _fulfillments) {
+ ) internal view returns (Fulfillment[] memory items) {
mapping(string => Fulfillment[])
storage fulfillmentsMap = _fulfillmentsMap();
- _fulfillments = fulfillmentsMap[defaultName];
+ items = fulfillmentsMap[defaultName];
}
/**
- * @notice saves an Fulfillment as a named default
+ * @dev Saves a Fulfillment as a named default.
+ *
* @param fulfillment the Fulfillment to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _fulfillment the Fulfillment saved as a default
*/
function saveDefault(
Fulfillment memory fulfillment,
@@ -72,6 +92,14 @@ library FulfillmentLib {
return fulfillment;
}
+ /**
+ * @dev Saves a Fulfillment array as a named default.
+ *
+ * @param fulfillments the Fulfillment array to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _fulfillments the Fulfillment array saved as a default
+ */
function saveDefaultMany(
Fulfillment[] memory fulfillments,
string memory defaultName
@@ -86,8 +114,11 @@ library FulfillmentLib {
}
/**
- * @notice makes a copy of an Fulfillment in-memory
+ * @dev Makes a copy of a Fulfillment in-memory.
+ *
* @param _fulfillment the Fulfillment to make a copy of in-memory
+ *
+ * @custom:return copiedFulfillment the copied Fulfillment
*/
function copy(
Fulfillment memory _fulfillment
@@ -101,6 +132,13 @@ library FulfillmentLib {
});
}
+ /**
+ * @dev Makes a copy of a Fulfillment array in-memory.
+ *
+ * @param _fulfillments the Fulfillment array to make a copy of in-memory
+ *
+ * @custom:return copiedFulfillments the copied Fulfillment array
+ */
function copy(
Fulfillment[] memory _fulfillments
) internal pure returns (Fulfillment[] memory) {
@@ -113,6 +151,11 @@ library FulfillmentLib {
return copiedItems;
}
+ /**
+ * @dev Creates an empty Fulfillment in-memory.
+ *
+ * @custom:return emptyFulfillment the empty Fulfillment
+ */
function empty() internal pure returns (Fulfillment memory) {
FulfillmentComponent[] memory components;
return
@@ -123,7 +166,10 @@ library FulfillmentLib {
}
/**
- * @notice gets the storage position of the default Fulfillment map
+ * @dev Gets the storage position of the default Fulfillment map
+ *
+ * @return fulfillmentMap the storage position of the default Fulfillment
+ * map
*/
function _fulfillmentMap()
private
@@ -136,6 +182,12 @@ library FulfillmentLib {
}
}
+ /**
+ * @dev Gets the storage position of the default Fulfillment array map
+ *
+ * @return fulfillmentsMap the storage position of the default Fulfillment
+ * array map
+ */
function _fulfillmentsMap()
private
pure
@@ -147,11 +199,18 @@ library FulfillmentLib {
}
}
- // methods for configuring a single of each of an Fulfillment's fields, which modifies the
- // Fulfillment
- // in-place and
- // returns it
+ // Methods for configuring a single of each of a Fulfillment's fields, which
+ // modify the FulfillmentComponent in-place and return it.
+ /**
+ * @dev Sets the offer components of a Fulfillment in-place.
+ *
+ * @param _fulfillment the Fulfillment to set the offer components of
+ * @param components the FulfillmentComponent array to set as the offer
+ * components
+ *
+ * @custom:return _fulfillment the Fulfillment with the offer components set
+ */
function withOfferComponents(
Fulfillment memory _fulfillment,
FulfillmentComponent[] memory components
@@ -160,6 +219,17 @@ library FulfillmentLib {
return _fulfillment;
}
+ /**
+ * @dev Sets the consideration components of a Fulfillment in-place.
+ *
+ * @param _fulfillment the Fulfillment to set the consideration components
+ * of
+ * @param components the FulfillmentComponent array to set as the
+ * consideration components
+ *
+ * @custom:return _fulfillment the Fulfillment with the consideration
+ * components set
+ */
function withConsiderationComponents(
Fulfillment memory _fulfillment,
FulfillmentComponent[] memory components
diff --git a/contracts/helpers/sol/lib/OfferItemLib.sol b/contracts/helpers/sol/lib/OfferItemLib.sol
index 944445a40..e98fbdc00 100644
--- a/contracts/helpers/sol/lib/OfferItemLib.sol
+++ b/contracts/helpers/sol/lib/OfferItemLib.sol
@@ -1,16 +1,45 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
-import { OfferItem, SpentItem } from "../../../lib/ConsiderationStructs.sol";
+import {
+ ConsiderationItem,
+ OfferItem,
+ SpentItem
+} from "../../../lib/ConsiderationStructs.sol";
+
import { ItemType } from "../../../lib/ConsiderationEnums.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title OfferItemLib
+ * @author James Wenzel (emo.eth)
+ * @notice OfferItemLib is a library for managing OfferItem structs and arrays.
+ * It allows chaining of functions to make struct creation more readable.
+ */
library OfferItemLib {
bytes32 private constant OFFER_ITEM_MAP_POSITION =
keccak256("seaport.OfferItemDefaults");
bytes32 private constant OFFER_ITEMS_MAP_POSITION =
keccak256("seaport.OfferItemsDefaults");
+ bytes32 private constant EMPTY_OFFER_ITEM =
+ keccak256(
+ abi.encode(
+ OfferItem({
+ itemType: ItemType(0),
+ token: address(0),
+ identifierOrCriteria: 0,
+ startAmount: 0,
+ endAmount: 0
+ })
+ )
+ );
+ /**
+ * @dev Clears an OfferItem from storage.
+ *
+ * @param item the item to clear
+ */
function _clear(OfferItem storage item) internal {
// clear all fields
item.itemType = ItemType.NATIVE;
@@ -21,7 +50,8 @@ library OfferItemLib {
}
/**
- * @notice clears a default OfferItem from storage
+ * @dev Clears an OfferItem from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -30,6 +60,11 @@ library OfferItemLib {
_clear(item);
}
+ /**
+ * @dev Clears an array of OfferItems from storage.
+ *
+ * @param defaultsName the name of the default to clear
+ */
function clearMany(string memory defaultsName) internal {
mapping(string => OfferItem[]) storage offerItemsMap = _offerItemsMap();
OfferItem[] storage items = offerItemsMap[defaultsName];
@@ -40,27 +75,48 @@ library OfferItemLib {
}
/**
- * @notice gets a default OfferItem from storage
+ * @dev Gets a default OfferItem from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the OfferItem retrieved from storage
*/
function fromDefault(
string memory defaultName
) internal view returns (OfferItem memory item) {
mapping(string => OfferItem) storage offerItemMap = _offerItemMap();
item = offerItemMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_OFFER_ITEM) {
+ revert("Empty OfferItem selected.");
+ }
}
+ /**
+ * @dev Gets a default OfferItem from storage.
+ *
+ * @param defaultsName the name of the default for retrieval
+ *
+ * @return items the OfferItems retrieved from storage
+ */
function fromDefaultMany(
string memory defaultsName
) internal view returns (OfferItem[] memory items) {
mapping(string => OfferItem[]) storage offerItemsMap = _offerItemsMap();
items = offerItemsMap[defaultsName];
+
+ if (items.length == 0) {
+ revert("Empty OfferItem array selected.");
+ }
}
/**
- * @notice saves an OfferItem as a named default
- * @param offerItem the OfferItem to save as a default
+ * @dev Saves an OfferItem as a named default.
+ *
+ * @param offerItem the OfferItem to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _offerItem the OfferItem saved as a default
*/
function saveDefault(
OfferItem memory offerItem,
@@ -71,6 +127,14 @@ library OfferItemLib {
return offerItem;
}
+ /**
+ * @dev Saves an array of OfferItems as a named default.
+ *
+ * @param offerItems the OfferItems to save as a default
+ * @param defaultsName the name of the default for retrieval
+ *
+ * @return _offerItems the OfferItems saved as a default
+ */
function saveDefaultMany(
OfferItem[] memory offerItems,
string memory defaultsName
@@ -83,8 +147,11 @@ library OfferItemLib {
}
/**
- * @notice makes a copy of an OfferItem in-memory
+ * @dev Makes a copy of an OfferItem in-memory.
+ *
* @param item the OfferItem to make a copy of in-memory
+ *
+ * @custom:return copiedItem the copied OfferItem
*/
function copy(
OfferItem memory item
@@ -99,6 +166,13 @@ library OfferItemLib {
});
}
+ /**
+ * @dev Makes a copy of an array of OfferItems in-memory.
+ *
+ * @param items the OfferItems to make a copy of in-memory
+ *
+ * @custom:return copiedItems the copied OfferItems
+ */
function copy(
OfferItem[] memory items
) internal pure returns (OfferItem[] memory) {
@@ -109,6 +183,11 @@ library OfferItemLib {
return copiedItems;
}
+ /**
+ * @dev Creates an empty OfferItem.
+ *
+ * @custom:return emptyItem the empty OfferItem
+ */
function empty() internal pure returns (OfferItem memory) {
return
OfferItem({
@@ -121,7 +200,9 @@ library OfferItemLib {
}
/**
- * @notice gets the storage position of the default OfferItem map
+ * @dev Gets the storage position of the default OfferItem map.
+ *
+ * @custom:return offerItemMap the default OfferItem map position
*/
function _offerItemMap()
private
@@ -135,7 +216,9 @@ library OfferItemLib {
}
/**
- * @notice gets the storage position of the default OfferItem[] map
+ * @dev Gets the storage position of the default OfferItem array map
+ *
+ * @custom:return offerItemMap the default OfferItem array map position
*/
function _offerItemsMap()
private
@@ -148,14 +231,16 @@ library OfferItemLib {
}
}
- // methods for configuring a single of each of an OfferItem's fields, which modifies the OfferItem in-place and
- // returns it
+ // Methods for configuring a single of each of a OfferItem's fields, which
+ // modify the OfferItem in-place and return it.
/**
- * @notice sets the item type
+ * @dev Sets the item type of an OfferItem.
+ *
* @param item the OfferItem to modify
* @param itemType the item type to set
- * @return the modified OfferItem
+ *
+ * @custom:return _offerItem the modified OfferItem
*/
function withItemType(
OfferItem memory item,
@@ -166,10 +251,12 @@ library OfferItemLib {
}
/**
- * @notice sets the token address
+ * @dev Sets the token of an OfferItem.
+ *
* @param item the OfferItem to modify
- * @param token the token address to set
- * @return the modified OfferItem
+ * @param token the token to set
+ *
+ * @custom:return _offerItem the modified OfferItem
*/
function withToken(
OfferItem memory item,
@@ -180,10 +267,12 @@ library OfferItemLib {
}
/**
- * @notice sets the identifier or criteria
+ * @dev Sets the identifierOrCriteria of an OfferItem.
+ *
* @param item the OfferItem to modify
* @param identifierOrCriteria the identifier or criteria to set
- * @return the modified OfferItem
+ *
+ * @custom:return _offerItem the modified OfferItem
*/
function withIdentifierOrCriteria(
OfferItem memory item,
@@ -194,10 +283,12 @@ library OfferItemLib {
}
/**
- * @notice sets the start amount
+ * @dev Sets the startAmount of an OfferItem.
+ *
* @param item the OfferItem to modify
* @param startAmount the start amount to set
- * @return the modified OfferItem
+ *
+ * @custom:return _offerItem the modified OfferItem
*/
function withStartAmount(
OfferItem memory item,
@@ -208,10 +299,12 @@ library OfferItemLib {
}
/**
- * @notice sets the end amount
+ * @dev Sets the endAmount of an OfferItem.
+ *
* @param item the OfferItem to modify
* @param endAmount the end amount to set
- * @return the modified OfferItem
+ *
+ * @custom:return _offerItem the modified OfferItem
*/
function withEndAmount(
OfferItem memory item,
@@ -221,6 +314,30 @@ library OfferItemLib {
return item;
}
+ /**
+ * @dev Sets the startAmount and endAmount of an OfferItem.
+ *
+ * @param item the OfferItem to modify
+ * @param amount the amount to set for the start and end amounts
+ *
+ * @custom:return _offerItem the modified OfferItem
+ */
+ function withAmount(
+ OfferItem memory item,
+ uint256 amount
+ ) internal pure returns (OfferItem memory) {
+ item.startAmount = amount;
+ item.endAmount = amount;
+ return item;
+ }
+
+ /**
+ * @dev Converts an OfferItem to a SpentItem.
+ *
+ * @param item the OfferItem to convert
+ *
+ * @custom:return spentItem the converted SpentItem
+ */
function toSpentItem(
OfferItem memory item
) internal pure returns (SpentItem memory) {
@@ -232,4 +349,44 @@ library OfferItemLib {
amount: item.startAmount
});
}
+
+ /**
+ * @dev Converts an OfferItem[] to a SpentItem[].
+ *
+ * @param items the OfferItem[] to convert
+ *
+ * @custom:return spentItems the converted SpentItem[]
+ */
+ function toSpentItemArray(
+ OfferItem[] memory items
+ ) internal pure returns (SpentItem[] memory) {
+ SpentItem[] memory spentItems = new SpentItem[](items.length);
+ for (uint256 i = 0; i < items.length; i++) {
+ spentItems[i] = toSpentItem(items[i]);
+ }
+
+ return spentItems;
+ }
+
+ /**
+ * @dev Converts an OfferItem to a ConsiderationItem.
+ *
+ * @param item the OfferItem to convert
+ *
+ * @custom:return considerationItem the converted ConsiderationItem
+ */
+ function toConsiderationItem(
+ OfferItem memory item,
+ address recipient
+ ) internal pure returns (ConsiderationItem memory) {
+ return
+ ConsiderationItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifierOrCriteria: item.identifierOrCriteria,
+ startAmount: item.startAmount,
+ endAmount: item.endAmount,
+ recipient: payable(recipient)
+ });
+ }
}
diff --git a/contracts/helpers/sol/lib/OrderComponentsLib.sol b/contracts/helpers/sol/lib/OrderComponentsLib.sol
index 375c01cd5..98b0779a0 100644
--- a/contracts/helpers/sol/lib/OrderComponentsLib.sol
+++ b/contracts/helpers/sol/lib/OrderComponentsLib.sol
@@ -2,22 +2,31 @@
pragma solidity ^0.8.17;
import {
- BasicOrderParameters,
- OrderComponents,
ConsiderationItem,
- OrderParameters,
OfferItem,
- AdditionalRecipient
+ OrderComponents,
+ OrderParameters
} from "../../../lib/ConsiderationStructs.sol";
+
import {
- OrderType,
+ BasicOrderType,
ItemType,
- BasicOrderType
+ OrderType
} from "../../../lib/ConsiderationEnums.sol";
+
import { StructCopier } from "./StructCopier.sol";
+
import { OfferItemLib } from "./OfferItemLib.sol";
+
import { ConsiderationItemLib } from "./ConsiderationItemLib.sol";
+/**
+ * @title OrderComponentsLib
+ * @author James Wenzel (emo.eth)
+ * @notice OrderComponentsLib is a library for managing OrderComponents structs
+ * and arrays. It allows chaining of functions to make struct creation
+ * more readable.
+ */
library OrderComponentsLib {
using OrderComponentsLib for OrderComponents;
using OfferItemLib for OfferItem[];
@@ -27,7 +36,30 @@ library OrderComponentsLib {
keccak256("seaport.OrderComponentsDefaults");
bytes32 private constant ORDER_COMPONENTS_ARRAY_MAP_POSITION =
keccak256("seaport.OrderComponentsArrayDefaults");
+ bytes32 private constant EMPTY_ORDER_COMPONENTS =
+ keccak256(
+ abi.encode(
+ OrderComponents({
+ offerer: address(0),
+ zone: address(0),
+ offer: new OfferItem[](0),
+ consideration: new ConsiderationItem[](0),
+ orderType: OrderType(0),
+ startTime: 0,
+ endTime: 0,
+ zoneHash: bytes32(0),
+ salt: 0,
+ conduitKey: bytes32(0),
+ counter: 0
+ })
+ )
+ );
+ /**
+ * @dev Clears anOrderComponents from storage.
+ *
+ * @param components the OrderComponents to clear
+ */
function clear(OrderComponents storage components) internal {
// uninitialized pointers take up no new memory (versus one word for initializing length-0)
OfferItem[] memory offer;
@@ -50,6 +82,11 @@ library OrderComponentsLib {
components.counter = 0;
}
+ /**
+ * @dev Clears an array of OrderComponents from storage.
+ *
+ * @param components the OrderComponents to clear
+ */
function clear(OrderComponents[] storage components) internal {
while (components.length > 0) {
clear(components[components.length - 1]);
@@ -58,7 +95,8 @@ library OrderComponentsLib {
}
/**
- * @notice clears a default OrderComponents from storage
+ * @dev Clears a default OrderComponents from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -68,6 +106,11 @@ library OrderComponentsLib {
components.clear();
}
+ /**
+ * @dev Creates a new OrderComponents struct.
+ *
+ * @return item the new OrderComponents struct
+ */
function empty() internal pure returns (OrderComponents memory item) {
OfferItem[] memory offer;
ConsiderationItem[] memory consideration;
@@ -87,8 +130,11 @@ library OrderComponentsLib {
}
/**
- * @notice gets a default OrderComponents from storage
+ * @dev Gets a default OrderComponents from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the default OrderComponents
*/
function fromDefault(
string memory defaultName
@@ -96,20 +142,38 @@ library OrderComponentsLib {
mapping(string => OrderComponents)
storage orderComponentsMap = _orderComponentsMap();
item = orderComponentsMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_ORDER_COMPONENTS) {
+ revert("Empty OrderComponents selected.");
+ }
}
+ /**
+ * @dev Gets a default OrderComponents array from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the default OrderComponents array
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (OrderComponents[] memory items) {
mapping(string => OrderComponents[])
storage orderComponentsArrayMap = _orderComponentsArrayMap();
items = orderComponentsArrayMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty OrderComponents array selected.");
+ }
}
/**
- * @notice saves an OrderComponents as a named default
+ * @dev Saves an OrderComponents as a named default.
+ *
* @param orderComponents the OrderComponents to save as a default
- * @param defaultName the name of the default for retrieval
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _orderComponents the OrderComponents that was saved
*/
function saveDefault(
OrderComponents memory orderComponents,
@@ -122,6 +186,14 @@ library OrderComponentsLib {
return orderComponents;
}
+ /**
+ * @dev Saves an OrderComponents array as a named default.
+ *
+ * @param orderComponents the OrderComponents array to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _orderComponents the OrderComponents array that was saved
+ */
function saveDefaultMany(
OrderComponents[] memory orderComponents,
string memory defaultName
@@ -136,8 +208,11 @@ library OrderComponentsLib {
}
/**
- * @notice makes a copy of an OrderComponents in-memory
+ * @dev Makes a copy of an OrderComponents in-memory.
+ *
* @param item the OrderComponents to make a copy of in-memory
+ *
+ * @return the copy of the OrderComponents
*/
function copy(
OrderComponents memory item
@@ -159,7 +234,10 @@ library OrderComponentsLib {
}
/**
- * @notice gets the storage position of the default OrderComponents map
+ * @dev Gets the storage position of the default OrderComponents map.
+ *
+ * @custom:return position the storage position of the default
+ * OrderComponents map
*/
function _orderComponentsMap()
private
@@ -172,6 +250,12 @@ library OrderComponentsLib {
}
}
+ /**
+ * @dev Gets the storage position of the default OrderComponents array map.
+ *
+ * @custom:return position the storage position of the default
+ * OrderComponents array map
+ */
function _orderComponentsArrayMap()
private
pure
@@ -185,9 +269,17 @@ library OrderComponentsLib {
}
}
- // methods for configuring a single of each of an in-memory OrderComponents's fields, which modifies the
- // OrderComponents in-memory and returns it
+ // Methods for configuring a single of each of a OrderComponents's fields,
+ // which modify the OrderComponents struct in-place and return it.
+ /**
+ * @dev Sets the offerer field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param offerer the new value for the offerer field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withOfferer(
OrderComponents memory components,
address offerer
@@ -196,6 +288,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the zone field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param zone the new value for the zone field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withZone(
OrderComponents memory components,
address zone
@@ -204,6 +304,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the offer field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param offer the new value for the offer field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withOffer(
OrderComponents memory components,
OfferItem[] memory offer
@@ -212,6 +320,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the consideration field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param consideration the new value for the consideration field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withConsideration(
OrderComponents memory components,
ConsiderationItem[] memory consideration
@@ -220,6 +336,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the orderType field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param orderType the new value for the orderType field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withOrderType(
OrderComponents memory components,
OrderType orderType
@@ -228,6 +352,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the startTime field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param startTime the new value for the startTime field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withStartTime(
OrderComponents memory components,
uint256 startTime
@@ -236,6 +368,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the endTime field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param endTime the new value for the endTime field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withEndTime(
OrderComponents memory components,
uint256 endTime
@@ -244,6 +384,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the zoneHash field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param zoneHash the new value for the zoneHash field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withZoneHash(
OrderComponents memory components,
bytes32 zoneHash
@@ -252,6 +400,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the salt field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param salt the new value for the salt field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withSalt(
OrderComponents memory components,
uint256 salt
@@ -260,6 +416,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the conduitKey field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param conduitKey the new value for the conduitKey field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withConduitKey(
OrderComponents memory components,
bytes32 conduitKey
@@ -268,6 +432,14 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Sets the counter field of an OrderComponents struct in-place.
+ *
+ * @param components the OrderComponents struct to modify
+ * @param counter the new value for the counter field
+ *
+ * @custom:return _orderComponents the modified OrderComponents struct
+ */
function withCounter(
OrderComponents memory components,
uint256 counter
@@ -276,6 +448,13 @@ library OrderComponentsLib {
return components;
}
+ /**
+ * @dev Converts an OrderComponents struct into an OrderParameters struct.
+ *
+ * @param components the OrderComponents struct to convert
+ *
+ * @custom:return _orderParameters the converted OrderParameters struct
+ */
function toOrderParameters(
OrderComponents memory components
) internal pure returns (OrderParameters memory parameters) {
diff --git a/contracts/helpers/sol/lib/OrderLib.sol b/contracts/helpers/sol/lib/OrderLib.sol
index 59dfd56a4..e7551adc9 100644
--- a/contracts/helpers/sol/lib/OrderLib.sol
+++ b/contracts/helpers/sol/lib/OrderLib.sol
@@ -2,23 +2,61 @@
pragma solidity ^0.8.17;
import {
- Order,
+ AdditionalRecipient,
AdvancedOrder,
- OrderParameters
+ BasicOrderParameters,
+ ConsiderationItem,
+ OfferItem,
+ Order,
+ OrderParameters,
+ OrderType
} from "../../../lib/ConsiderationStructs.sol";
+
+import { BasicOrderType } from "../../../lib/ConsiderationEnums.sol";
+
import { OrderParametersLib } from "./OrderParametersLib.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title AdvancedOrderLib
+ * @author James Wenzel (emo.eth)
+ * @notice AdvancedOrderLib is a library for managing AdvancedOrder
+ * structs and arrays. It allows chaining of functions to make struct
+ * creation more readable.
+ */
library OrderLib {
bytes32 private constant ORDER_MAP_POSITION =
keccak256("seaport.OrderDefaults");
bytes32 private constant ORDERS_MAP_POSITION =
keccak256("seaport.OrdersDefaults");
+ bytes32 private constant EMPTY_ORDER =
+ keccak256(
+ abi.encode(
+ Order({
+ parameters: OrderParameters({
+ offerer: address(0),
+ zone: address(0),
+ offer: new OfferItem[](0),
+ consideration: new ConsiderationItem[](0),
+ orderType: OrderType(0),
+ startTime: 0,
+ endTime: 0,
+ zoneHash: bytes32(0),
+ salt: 0,
+ conduitKey: bytes32(0),
+ totalOriginalConsiderationItems: 0
+ }),
+ signature: ""
+ })
+ )
+ );
using OrderParametersLib for OrderParameters;
/**
- * @notice clears a default Order from storage
+ * @dev Clears a default Order from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -27,12 +65,22 @@ library OrderLib {
clear(item);
}
+ /**
+ * @dev Clears all fields on an Order.
+ *
+ * @param order the Order to clear
+ */
function clear(Order storage order) internal {
// clear all fields
order.parameters.clear();
order.signature = "";
}
+ /**
+ * @dev Clears an array of Orders from storage.
+ *
+ * @param order the Orders to clear
+ */
function clear(Order[] storage order) internal {
while (order.length > 0) {
clear(order[order.length - 1]);
@@ -41,28 +89,50 @@ library OrderLib {
}
/**
- * @notice gets a default Order from storage
+ * @dev Gets a default Order from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the default Order
*/
function fromDefault(
string memory defaultName
) internal view returns (Order memory item) {
mapping(string => Order) storage orderMap = _orderMap();
item = orderMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_ORDER) {
+ revert("Empty Order selected.");
+ }
}
+ /**
+ * @dev Gets a default Order array from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the default Order array
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (Order[] memory) {
mapping(string => Order[]) storage ordersMap = _ordersMap();
Order[] memory items = ordersMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty Order array selected.");
+ }
+
return items;
}
/**
- * @notice saves an Order as a named default
+ * @dev Saves an Order as a named default.
+ *
* @param order the Order to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _order the Order saved as a default
*/
function saveDefault(
Order memory order,
@@ -73,6 +143,14 @@ library OrderLib {
return order;
}
+ /**
+ * @dev Saves an Order array as a named default.
+ *
+ * @param orders the Order array to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _orders the Order array saved as a default
+ */
function saveDefaultMany(
Order[] memory orders,
string memory defaultName
@@ -83,8 +161,11 @@ library OrderLib {
}
/**
- * @notice makes a copy of an Order in-memory
+ * @dev Makes a copy of an Order in-memory.
+ *
* @param item the Order to make a copy of in-memory
+ *
+ * @custom:return copiedOrder the copied Order
*/
function copy(Order memory item) internal pure returns (Order memory) {
return
@@ -94,6 +175,13 @@ library OrderLib {
});
}
+ /**
+ * @dev Makes a copy of an Order array in-memory.
+ *
+ * @param items the Order array to make a copy of in-memory
+ *
+ * @custom:return copiedOrders the copied Order array
+ */
function copy(Order[] memory items) internal pure returns (Order[] memory) {
Order[] memory copiedItems = new Order[](items.length);
for (uint256 i = 0; i < items.length; i++) {
@@ -102,12 +190,19 @@ library OrderLib {
return copiedItems;
}
+ /**
+ * @dev Create an empty Order.
+ *
+ * @custom:return emptyOrder the empty Order
+ */
function empty() internal pure returns (Order memory) {
return Order({ parameters: OrderParametersLib.empty(), signature: "" });
}
/**
- * @notice gets the storage position of the default Order map
+ * @dev Gets the storage position of the default Order map.
+ *
+ * @return orderMap the storage position of the default Order map
*/
function _orderMap()
private
@@ -120,6 +215,11 @@ library OrderLib {
}
}
+ /**
+ * @dev Gets the storage position of the default Order array map.
+ *
+ * @return ordersMap the storage position of the default Order array map
+ */
function _ordersMap()
private
pure
@@ -131,9 +231,17 @@ library OrderLib {
}
}
- // methods for configuring a single of each of an Order's fields, which modifies the Order in-place and
- // returns it
+ // Methods for configuring a single of each of an Order's fields, which
+ // modify the Order in-place and return it.
+ /**
+ * @dev Sets the parameters of an Order.
+ *
+ * @param order the Order to set the parameters of
+ * @param parameters the parameters to set
+ *
+ * @return _order the Order with the parameters set
+ */
function withParameters(
Order memory order,
OrderParameters memory parameters
@@ -142,6 +250,14 @@ library OrderLib {
return order;
}
+ /**
+ * @dev Sets the signature of an Order.
+ *
+ * @param order the Order to set the signature of
+ * @param signature the signature to set
+ *
+ * @return _order the Order with the signature set
+ */
function withSignature(
Order memory order,
bytes memory signature
@@ -150,6 +266,16 @@ library OrderLib {
return order;
}
+ /**
+ * @dev Converts an Order to an AdvancedOrder.
+ *
+ * @param order the Order to convert
+ * @param numerator the numerator to set
+ * @param denominator the denominator to set
+ * @param extraData the extra data to set
+ *
+ * @return advancedOrder the AdvancedOrder
+ */
function toAdvancedOrder(
Order memory order,
uint120 numerator,
@@ -162,4 +288,94 @@ library OrderLib {
advancedOrder.signature = order.signature;
advancedOrder.extraData = extraData;
}
+
+ /**
+ * @dev Converts Orders to AdvancedOrders in bulk.
+ *
+ * @param orders the Orders to convert
+ * @param numerator the numerator to set for all
+ * @param denominator the denominator to set for all
+ * @param extraData the extra data to set for all
+ *
+ * @return _advancedOrders the AdvancedOrders
+ */
+ function toAdvancedOrders(
+ Order[] memory orders,
+ uint120 numerator,
+ uint120 denominator,
+ bytes memory extraData
+ ) internal pure returns (AdvancedOrder[] memory _advancedOrders) {
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](
+ orders.length
+ );
+ for (uint256 i = 0; i < orders.length; i++) {
+ advancedOrders[i] = toAdvancedOrder(
+ orders[i],
+ numerator,
+ denominator,
+ extraData
+ );
+ }
+ return advancedOrders;
+ }
+
+ /**
+ * @dev Converts an Order to a BasicOrderParameters.
+ *
+ * @param order the Order to convert
+ * @param basicOrderType the BasicOrderType to set
+ *
+ * @return basicOrderParameters the BasicOrderParameters
+ */
+ function toBasicOrderParameters(
+ Order memory order,
+ BasicOrderType basicOrderType
+ ) internal pure returns (BasicOrderParameters memory basicOrderParameters) {
+ basicOrderParameters.considerationToken = order
+ .parameters
+ .consideration[0]
+ .token;
+ basicOrderParameters.considerationIdentifier = order
+ .parameters
+ .consideration[0]
+ .identifierOrCriteria;
+ basicOrderParameters.considerationAmount = order
+ .parameters
+ .consideration[0]
+ .endAmount;
+ basicOrderParameters.offerer = payable(order.parameters.offerer);
+ basicOrderParameters.zone = order.parameters.zone;
+ basicOrderParameters.offerToken = order.parameters.offer[0].token;
+ basicOrderParameters.offerIdentifier = order
+ .parameters
+ .offer[0]
+ .identifierOrCriteria;
+ basicOrderParameters.offerAmount = order.parameters.offer[0].endAmount;
+ basicOrderParameters.basicOrderType = basicOrderType;
+ basicOrderParameters.startTime = order.parameters.startTime;
+ basicOrderParameters.endTime = order.parameters.endTime;
+ basicOrderParameters.zoneHash = order.parameters.zoneHash;
+ basicOrderParameters.salt = order.parameters.salt;
+ basicOrderParameters.offererConduitKey = order.parameters.conduitKey;
+ basicOrderParameters.fulfillerConduitKey = order.parameters.conduitKey;
+ basicOrderParameters.totalOriginalAdditionalRecipients =
+ order.parameters.totalOriginalConsiderationItems -
+ 1;
+
+ AdditionalRecipient[]
+ memory additionalRecipients = new AdditionalRecipient[](
+ order.parameters.consideration.length - 1
+ );
+ for (uint256 i = 1; i < order.parameters.consideration.length; i++) {
+ additionalRecipients[i - 1] = AdditionalRecipient({
+ recipient: order.parameters.consideration[i].recipient,
+ amount: order.parameters.consideration[i].startAmount
+ });
+ }
+
+ basicOrderParameters.additionalRecipients = additionalRecipients;
+ basicOrderParameters.signature = order.signature;
+
+ return basicOrderParameters;
+ }
}
diff --git a/contracts/helpers/sol/lib/OrderParametersLib.sol b/contracts/helpers/sol/lib/OrderParametersLib.sol
index 9beb886fe..1656707e1 100644
--- a/contracts/helpers/sol/lib/OrderParametersLib.sol
+++ b/contracts/helpers/sol/lib/OrderParametersLib.sol
@@ -1,23 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
+import { ItemType, Side } from "../../../lib/ConsiderationEnums.sol";
+
import {
- BasicOrderParameters,
- OrderComponents,
ConsiderationItem,
+ CriteriaResolver,
+ OrderComponents,
OrderParameters,
OfferItem,
- AdditionalRecipient
+ ReceivedItem,
+ SpentItem
} from "../../../lib/ConsiderationStructs.sol";
-import {
- OrderType,
- ItemType,
- BasicOrderType
-} from "../../../lib/ConsiderationEnums.sol";
+
+import { OrderType } from "../../../lib/ConsiderationEnums.sol";
+
import { StructCopier } from "./StructCopier.sol";
+
import { OfferItemLib } from "./OfferItemLib.sol";
+
import { ConsiderationItemLib } from "./ConsiderationItemLib.sol";
+/**
+ * @title OrderParametersLib
+ * @author James Wenzel (emo.eth)
+ * @notice OrderParametersLib is a library for managing OrderParameters structs
+ * and arrays. It allows chaining of functions to make struct creation
+ * more readable.
+ */
library OrderParametersLib {
using OrderParametersLib for OrderParameters;
using OfferItemLib for OfferItem[];
@@ -29,7 +39,30 @@ library OrderParametersLib {
keccak256("seaport.OrderParametersDefaults");
bytes32 private constant ORDER_PARAMETERS_ARRAY_MAP_POSITION =
keccak256("seaport.OrderParametersArrayDefaults");
+ bytes32 private constant EMPTY_ORDER_PARAMETERS =
+ keccak256(
+ abi.encode(
+ OrderParameters({
+ offerer: address(0),
+ zone: address(0),
+ offer: new OfferItem[](0),
+ consideration: new ConsiderationItem[](0),
+ orderType: OrderType(0),
+ startTime: 0,
+ endTime: 0,
+ zoneHash: bytes32(0),
+ salt: 0,
+ conduitKey: bytes32(0),
+ totalOriginalConsiderationItems: 0
+ })
+ )
+ );
+ /**
+ * @dev Clears an OrderParameters from storage.
+ *
+ * @param parameters the OrderParameters to clear
+ */
function clear(OrderParameters storage parameters) internal {
// uninitialized pointers take up no new memory (versus one word for initializing length-0)
OfferItem[] memory offer;
@@ -52,6 +85,11 @@ library OrderParametersLib {
parameters.totalOriginalConsiderationItems = 0;
}
+ /**
+ * @dev Clears an array of OrderParameters from storage.
+ *
+ * @param parameters the OrderParameters array to clear
+ */
function clear(OrderParameters[] storage parameters) internal {
while (parameters.length > 0) {
clear(parameters[parameters.length - 1]);
@@ -60,7 +98,8 @@ library OrderParametersLib {
}
/**
- * @notice clears a default OrderParameters from storage
+ * @dev Clears a default OrderParameters from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -70,6 +109,11 @@ library OrderParametersLib {
parameters.clear();
}
+ /**
+ * @dev Creates a new empty OrderParameters struct.
+ *
+ * @return item the new OrderParameters
+ */
function empty() internal pure returns (OrderParameters memory item) {
OfferItem[] memory offer;
ConsiderationItem[] memory consideration;
@@ -89,8 +133,11 @@ library OrderParametersLib {
}
/**
- * @notice gets a default OrderParameters from storage
+ * @dev Gets a default OrderParameters from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the default OrderParameters
*/
function fromDefault(
string memory defaultName
@@ -98,20 +145,38 @@ library OrderParametersLib {
mapping(string => OrderParameters)
storage orderParametersMap = _orderParametersMap();
item = orderParametersMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_ORDER_PARAMETERS) {
+ revert("Empty OrderParameters selected.");
+ }
}
+ /**
+ * @dev Gets a default OrderParameters array from storage.
+ *
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return items the default OrderParameters array
+ */
function fromDefaultMany(
string memory defaultName
) internal view returns (OrderParameters[] memory items) {
mapping(string => OrderParameters[])
storage orderParametersArrayMap = _orderParametersArrayMap();
items = orderParametersArrayMap[defaultName];
+
+ if (items.length == 0) {
+ revert("Empty OrderParameters array selected.");
+ }
}
/**
- * @notice saves an OrderParameters as a named default
+ * @dev Saves an OrderParameters as a named default.
+ *
* @param orderParameters the OrderParameters to save as a default
- * @param defaultName the name of the default for retrieval
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _orderParameters the OrderParameters that was saved
*/
function saveDefault(
OrderParameters memory orderParameters,
@@ -124,6 +189,14 @@ library OrderParametersLib {
return orderParameters;
}
+ /**
+ * @dev Saves an OrderParameters array as a named default.
+ *
+ * @param orderParameters the OrderParameters array to save as a default
+ * @param defaultName the name of the default for retrieval
+ *
+ * @return _orderParameters the OrderParameters array that was saved
+ */
function saveDefaultMany(
OrderParameters[] memory orderParameters,
string memory defaultName
@@ -138,8 +211,11 @@ library OrderParametersLib {
}
/**
- * @notice makes a copy of an OrderParameters in-memory
+ * @dev Makes a copy of an OrderParameters in-memory.
+ *
* @param item the OrderParameters to make a copy of in-memory
+ *
+ * @custom:return copiedOrderParameters the copied OrderParameters
*/
function copy(
OrderParameters memory item
@@ -162,7 +238,10 @@ library OrderParametersLib {
}
/**
- * @notice gets the storage position of the default OrderParameters map
+ * @dev Gets the storage position of the default OrderParameters map.
+ *
+ * @custom:return position the storage position of the default
+ * OrderParameters map
*/
function _orderParametersMap()
private
@@ -175,6 +254,12 @@ library OrderParametersLib {
}
}
+ /**
+ * @dev Gets the storage position of the default OrderParameters array map.
+ *
+ * @custom:return position the storage position of the default
+ * OrderParameters array map
+ */
function _orderParametersArrayMap()
private
pure
@@ -188,9 +273,17 @@ library OrderParametersLib {
}
}
- // methods for configuring a single of each of an in-memory OrderParameters's fields, which modifies the
- // OrderParameters in-memory and returns it
+ // Methods for configuring a single of each of a OrderParameters's fields,
+ // which modify the OrderParameters struct in-place and return it.
+ /**
+ * @dev Sets the offerer field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param offerer the new value for the offerer field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withOfferer(
OrderParameters memory parameters,
address offerer
@@ -199,6 +292,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the zone field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param zone the new value for the zone field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withZone(
OrderParameters memory parameters,
address zone
@@ -207,6 +308,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the offer field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param offer the new value for the offer field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withOffer(
OrderParameters memory parameters,
OfferItem[] memory offer
@@ -215,6 +324,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the consideration field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param consideration the new value for the consideration field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withConsideration(
OrderParameters memory parameters,
ConsiderationItem[] memory consideration
@@ -223,6 +340,32 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the consideration field of a OrderParameters struct in-place
+ * and updates the totalOriginalConsiderationItems field accordingly.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param consideration the new value for the consideration field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
+ function withTotalConsideration(
+ OrderParameters memory parameters,
+ ConsiderationItem[] memory consideration
+ ) internal pure returns (OrderParameters memory) {
+ parameters.consideration = consideration;
+ parameters.totalOriginalConsiderationItems = consideration.length;
+ return parameters;
+ }
+
+ /**
+ * @dev Sets the orderType field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param orderType the new value for the orderType field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withOrderType(
OrderParameters memory parameters,
OrderType orderType
@@ -231,6 +374,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the startTime field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param startTime the new value for the startTime field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withStartTime(
OrderParameters memory parameters,
uint256 startTime
@@ -239,6 +390,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the endTime field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param endTime the new value for the endTime field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withEndTime(
OrderParameters memory parameters,
uint256 endTime
@@ -247,6 +406,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the zoneHash field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param zoneHash the new value for the zoneHash field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withZoneHash(
OrderParameters memory parameters,
bytes32 zoneHash
@@ -255,6 +422,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the salt field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param salt the new value for the salt field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withSalt(
OrderParameters memory parameters,
uint256 salt
@@ -263,6 +438,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the conduitKey field of a OrderParameters struct in-place.
+ *
+ * @param parameters the OrderParameters struct to modify
+ * @param conduitKey the new value for the conduitKey field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withConduitKey(
OrderParameters memory parameters,
bytes32 conduitKey
@@ -271,6 +454,18 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Sets the totalOriginalConsiderationItems field of a OrderParameters
+ * struct in-place.
+ *
+ * @param parameters the OrderParameters struct to
+ * modify
+ * @param totalOriginalConsiderationItems the new value for the
+ * totalOriginalConsiderationItems
+ * field
+ *
+ * @custom:return _parameters the modified OrderParameters struct
+ */
function withTotalOriginalConsiderationItems(
OrderParameters memory parameters,
uint256 totalOriginalConsiderationItems
@@ -280,6 +475,14 @@ library OrderParametersLib {
return parameters;
}
+ /**
+ * @dev Converts an OrderParameters struct into an OrderComponents struct.
+ *
+ * @param parameters the OrderParameters struct to convert
+ * @param counter the counter to use for the OrderComponents struct
+ *
+ * @return components the OrderComponents struct
+ */
function toOrderComponents(
OrderParameters memory parameters,
uint256 counter
@@ -296,4 +499,309 @@ library OrderParametersLib {
components.conduitKey = parameters.conduitKey;
components.counter = counter;
}
+
+ function isAvailable(
+ OrderParameters memory parameters
+ ) internal view returns (bool) {
+ return
+ block.timestamp >= parameters.startTime &&
+ block.timestamp < parameters.endTime;
+ }
+
+ function getSpentAndReceivedItems(
+ OrderParameters memory parameters,
+ uint256 numerator,
+ uint256 denominator,
+ uint256 orderIndex,
+ CriteriaResolver[] memory criteriaResolvers
+ )
+ internal
+ view
+ returns (SpentItem[] memory spent, ReceivedItem[] memory received)
+ {
+ if (isAvailable(parameters)) {
+ spent = getSpentItems(parameters, numerator, denominator);
+ received = getReceivedItems(parameters, numerator, denominator);
+
+ applyCriteriaResolvers(
+ spent,
+ received,
+ orderIndex,
+ criteriaResolvers
+ );
+ }
+ }
+
+ function applyCriteriaResolvers(
+ SpentItem[] memory spentItems,
+ ReceivedItem[] memory receivedItems,
+ uint256 orderIndex,
+ CriteriaResolver[] memory criteriaResolvers
+ ) internal pure {
+ for (uint256 i = 0; i < criteriaResolvers.length; i++) {
+ CriteriaResolver memory resolver = criteriaResolvers[i];
+ if (resolver.orderIndex != orderIndex) {
+ continue;
+ }
+ if (resolver.side == Side.OFFER) {
+ SpentItem memory item = spentItems[resolver.index];
+ item.itemType = convertCriteriaItemType(item.itemType);
+ item.identifier = resolver.identifier;
+ } else {
+ ReceivedItem memory item = receivedItems[resolver.index];
+ item.itemType = convertCriteriaItemType(item.itemType);
+ item.identifier = resolver.identifier;
+ }
+ }
+ }
+
+ function convertCriteriaItemType(
+ ItemType itemType
+ ) internal pure returns (ItemType) {
+ if (itemType == ItemType.ERC721_WITH_CRITERIA) {
+ return ItemType.ERC721;
+ } else if (itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ return ItemType.ERC1155;
+ } else {
+ revert(
+ "OrderParametersLib: amount deriver helper resolving non criteria item type"
+ );
+ }
+ }
+
+ function getSpentItems(
+ OrderParameters memory parameters,
+ uint256 numerator,
+ uint256 denominator
+ ) internal view returns (SpentItem[] memory) {
+ return
+ getSpentItems(
+ parameters.offer,
+ parameters.startTime,
+ parameters.endTime,
+ numerator,
+ denominator
+ );
+ }
+
+ function getReceivedItems(
+ OrderParameters memory parameters,
+ uint256 numerator,
+ uint256 denominator
+ ) internal view returns (ReceivedItem[] memory) {
+ return
+ getReceivedItems(
+ parameters.consideration,
+ parameters.startTime,
+ parameters.endTime,
+ numerator,
+ denominator
+ );
+ }
+
+ function getSpentItems(
+ OfferItem[] memory items,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) internal view returns (SpentItem[] memory) {
+ SpentItem[] memory spentItems = new SpentItem[](items.length);
+ for (uint256 i = 0; i < items.length; i++) {
+ spentItems[i] = getSpentItem(
+ items[i],
+ startTime,
+ endTime,
+ numerator,
+ denominator
+ );
+ }
+ return spentItems;
+ }
+
+ function getReceivedItems(
+ ConsiderationItem[] memory considerationItems,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) internal view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ considerationItems.length
+ );
+ for (uint256 i = 0; i < considerationItems.length; i++) {
+ receivedItems[i] = getReceivedItem(
+ considerationItems[i],
+ startTime,
+ endTime,
+ numerator,
+ denominator
+ );
+ }
+ return receivedItems;
+ }
+
+ function getSpentItem(
+ OfferItem memory item,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) internal view returns (SpentItem memory spent) {
+ spent = SpentItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifierOrCriteria,
+ amount: _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startAmount: item.startAmount,
+ endAmount: item.endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: false
+ })
+ });
+ }
+
+ function getReceivedItem(
+ ConsiderationItem memory considerationItem,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) internal view returns (ReceivedItem memory received) {
+ received = ReceivedItem({
+ itemType: considerationItem.itemType,
+ token: considerationItem.token,
+ identifier: considerationItem.identifierOrCriteria,
+ amount: _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startAmount: considerationItem.startAmount,
+ endAmount: considerationItem.endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: true
+ }),
+ recipient: considerationItem.recipient
+ });
+ }
+
+ function _applyFraction(
+ uint256 startAmount,
+ uint256 endAmount,
+ uint256 numerator,
+ uint256 denominator,
+ uint256 startTime,
+ uint256 endTime,
+ bool roundUp
+ ) internal view returns (uint256 amount) {
+ // If start amount equals end amount, apply fraction to end amount.
+ if (startAmount == endAmount) {
+ // Apply fraction to end amount.
+ amount = _getFraction(numerator, denominator, endAmount);
+ } else {
+ // Otherwise, apply fraction to both and interpolated final amount.
+ amount = _locateCurrentAmount(
+ _getFraction(numerator, denominator, startAmount),
+ _getFraction(numerator, denominator, endAmount),
+ startTime,
+ endTime,
+ roundUp
+ );
+ }
+ }
+
+ function _getFraction(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 value
+ ) internal pure returns (uint256 newValue) {
+ // Return value early in cases where the fraction resolves to 1.
+ if (numerator == denominator) {
+ return value;
+ }
+
+ bool failure = false;
+
+ // Ensure fraction can be applied to the value with no remainder. Note
+ // that the denominator cannot be zero.
+ assembly {
+ // Ensure new value contains no remainder via mulmod operator.
+ // Credit to @hrkrshnn + @axic for proposing this optimal solution.
+ if mulmod(value, numerator, denominator) {
+ failure := true
+ }
+ }
+
+ if (failure) {
+ revert("OrderParametersLib: bad fraction");
+ }
+
+ // Multiply the numerator by the value and ensure no overflow occurs.
+ uint256 valueTimesNumerator = value * numerator;
+
+ // Divide and check for remainder. Note that denominator cannot be zero.
+ assembly {
+ // Perform division without zero check.
+ newValue := div(valueTimesNumerator, denominator)
+ }
+ }
+
+ function _locateCurrentAmount(
+ uint256 startAmount,
+ uint256 endAmount,
+ uint256 startTime,
+ uint256 endTime,
+ bool roundUp
+ ) internal view returns (uint256 amount) {
+ // Only modify end amount if it doesn't already equal start amount.
+ if (startAmount != endAmount) {
+ // Declare variables to derive in the subsequent unchecked scope.
+ uint256 duration;
+ uint256 elapsed;
+ uint256 remaining;
+
+ // Skip underflow checks as startTime <= block.timestamp < endTime.
+ unchecked {
+ // Derive the duration for the order and place it on the stack.
+ duration = endTime - startTime;
+
+ // Derive time elapsed since the order started & place on stack.
+ elapsed = block.timestamp - startTime;
+
+ // Derive time remaining until order expires and place on stack.
+ remaining = duration - elapsed;
+ }
+
+ // Aggregate new amounts weighted by time with rounding factor.
+ uint256 totalBeforeDivision = ((startAmount * remaining) +
+ (endAmount * elapsed));
+
+ // Use assembly to combine operations and skip divide-by-zero check.
+ assembly {
+ // Multiply by iszero(iszero(totalBeforeDivision)) to ensure
+ // amount is set to zero if totalBeforeDivision is zero,
+ // as intermediate overflow can occur if it is zero.
+ amount := mul(
+ iszero(iszero(totalBeforeDivision)),
+ // Subtract 1 from the numerator and add 1 to the result if
+ // roundUp is true to get the proper rounding direction.
+ // Division is performed with no zero check as duration
+ // cannot be zero as long as startTime < endTime.
+ add(
+ div(sub(totalBeforeDivision, roundUp), duration),
+ roundUp
+ )
+ )
+ }
+
+ // Return the current amount.
+ return amount;
+ }
+
+ // Return the original amount as startAmount == endAmount.
+ return endAmount;
+ }
}
diff --git a/contracts/helpers/sol/lib/ReceivedItemLib.sol b/contracts/helpers/sol/lib/ReceivedItemLib.sol
index 49dee2fe3..ce7c72d3d 100644
--- a/contracts/helpers/sol/lib/ReceivedItemLib.sol
+++ b/contracts/helpers/sol/lib/ReceivedItemLib.sol
@@ -2,20 +2,42 @@
pragma solidity ^0.8.17;
import {
- ReceivedItem,
- ConsiderationItem
+ ConsiderationItem,
+ ReceivedItem
} from "../../../lib/ConsiderationStructs.sol";
+
import { ItemType } from "../../../lib/ConsiderationEnums.sol";
+
import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title ReceivedItemLib
+ * @author James Wenzel (emo.eth)
+ * @notice ReceivedItemLib is a library for managing ReceivedItem structs and
+ * arrays. It allows chaining of functions to make struct creation more
+ * readable.
+ */
library ReceivedItemLib {
bytes32 private constant RECEIVED_ITEM_MAP_POSITION =
keccak256("seaport.ReceivedItemDefaults");
bytes32 private constant RECEIVED_ITEMS_MAP_POSITION =
keccak256("seaport.ReceivedItemsDefaults");
+ bytes32 private constant EMPTY_RECEIVED_ITEM =
+ keccak256(
+ abi.encode(
+ ReceivedItem({
+ itemType: ItemType(0),
+ token: address(0),
+ identifier: 0,
+ amount: 0,
+ recipient: payable(address(0))
+ })
+ )
+ );
/**
- * @notice clears a default ReceivedItem from storage
+ * @dev Clears a default ReceivedItem from storage.
+ *
* @param defaultName the name of the default to clear
*/
function clear(string memory defaultName) internal {
@@ -25,6 +47,11 @@ library ReceivedItemLib {
clear(item);
}
+ /**
+ * @dev Clears all fields on a ReceivedItem.
+ *
+ * @param item the ReceivedItem to clear
+ */
function clear(ReceivedItem storage item) internal {
// clear all fields
item.itemType = ItemType.NATIVE;
@@ -34,6 +61,11 @@ library ReceivedItemLib {
item.recipient = payable(address(0));
}
+ /**
+ * @dev Clears an array of ReceivedItems from storage.
+ *
+ * @param defaultsName the name of the default to clear
+ */
function clearMany(string memory defaultsName) internal {
mapping(string => ReceivedItem[])
storage receivedItemsMap = _receivedItemsMap();
@@ -41,6 +73,11 @@ library ReceivedItemLib {
clearMany(items);
}
+ /**
+ * @dev Clears an array of ReceivedItems from storage.
+ *
+ * @param items the ReceivedItems to clear
+ */
function clearMany(ReceivedItem[] storage items) internal {
while (items.length > 0) {
clear(items[items.length - 1]);
@@ -48,6 +85,11 @@ library ReceivedItemLib {
}
}
+ /**
+ * @dev Creates an empty ReceivedItem.
+ *
+ * @return the empty ReceivedItem
+ */
function empty() internal pure returns (ReceivedItem memory) {
return
ReceivedItem({
@@ -60,8 +102,11 @@ library ReceivedItemLib {
}
/**
- * @notice gets a default ReceivedItem from storage
+ * @dev Gets a default ReceivedItem from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the default ReceivedItem
*/
function fromDefault(
string memory defaultName
@@ -69,20 +114,38 @@ library ReceivedItemLib {
mapping(string => ReceivedItem)
storage receivedItemMap = _receivedItemMap();
item = receivedItemMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_RECEIVED_ITEM) {
+ revert("Empty ReceivedItem selected.");
+ }
}
+ /**
+ * @dev Gets a default ReceivedItem from storage.
+ *
+ * @param defaultsName the name of the default for retrieval
+ *
+ * @return items the default ReceivedItem
+ */
function fromDefaultMany(
string memory defaultsName
) internal view returns (ReceivedItem[] memory items) {
mapping(string => ReceivedItem[])
storage receivedItemsMap = _receivedItemsMap();
items = receivedItemsMap[defaultsName];
+
+ if (items.length == 0) {
+ revert("Empty ReceivedItem array selected.");
+ }
}
/**
- * @notice saves an ReceivedItem as a named default
+ * @dev Saves an ReceivedItem as a named default.
+ *
* @param receivedItem the ReceivedItem to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _receivedItem the saved ReceivedItem
*/
function saveDefault(
ReceivedItem memory receivedItem,
@@ -94,6 +157,14 @@ library ReceivedItemLib {
return receivedItem;
}
+ /**
+ * @dev Saves an ReceivedItem as a named default.
+ *
+ * @param receivedItems the ReceivedItem to save as a default
+ * @param defaultsName the name of the default for retrieval
+ *
+ * @return _receivedItems the saved ReceivedItem
+ */
function saveDefaultMany(
ReceivedItem[] memory receivedItems,
string memory defaultsName
@@ -105,6 +176,14 @@ library ReceivedItemLib {
return receivedItems;
}
+ /**
+ * @dev Sets an array of in-memory ReceivedItems to an array of
+ * ReceivedItems in storage.
+ *
+ * @param items the ReceivedItems array in storage to push to
+ * @param newItems the ReceivedItems array in memory to push onto the items
+ * array
+ */
function setReceivedItems(
ReceivedItem[] storage items,
ReceivedItem[] memory newItems
@@ -116,8 +195,11 @@ library ReceivedItemLib {
}
/**
- * @notice makes a copy of an ReceivedItem in-memory
+ * @dev Makes a copy of an ReceivedItem in-memory.
+ *
* @param item the ReceivedItem to make a copy of in-memory
+ *
+ * @custom:return copiedReceivedItem the copied ReceivedItem
*/
function copy(
ReceivedItem memory item
@@ -132,6 +214,13 @@ library ReceivedItemLib {
});
}
+ /**
+ * @dev Makes a copy of an array of ReceivedItems in-memory.
+ *
+ * @param item the ReceivedItems array to make a copy of in-memory
+ *
+ * @custom:return copiedReceivedItems the copied ReceivedItems
+ */
function copy(
ReceivedItem[] memory item
) internal pure returns (ReceivedItem[] memory) {
@@ -143,7 +232,10 @@ library ReceivedItemLib {
}
/**
- * @notice gets the storage position of the default ReceivedItem map
+ * @dev Gets the storage position of the default ReceivedItem map.
+ *
+ * @custom:return receivedItemMap the storage position of the default
+ * ReceivedItem map
*/
function _receivedItemMap()
private
@@ -157,7 +249,10 @@ library ReceivedItemLib {
}
/**
- * @notice gets the storage position of the default ReceivedItem map
+ * @dev Gets the storage position of the default ReceivedItem array map.
+ *
+ * @custom:return receivedItemsMap the storage position of the default
+ * ReceivedItem array map
*/
function _receivedItemsMap()
private
@@ -170,15 +265,16 @@ library ReceivedItemLib {
}
}
- // methods for configuring a single of each of an ReceivedItem's fields, which modifies the ReceivedItem
- // in-place and
- // returns it
+ // Methods for configuring a single of each of a ReceivedItem's fields,
+ // which modify the ReceivedItem struct in-place and return it.
/**
- * @notice sets the item type
- * @param item the ReceivedItem to modify
- * @param itemType the item type to set
- * @return the modified ReceivedItem
+ * @dev Sets the itemType field of an ReceivedItem.
+ *
+ * @param item the ReceivedItem to set the itemType field of
+ * @param itemType the itemType to set the itemType field to
+ *
+ * @custom:return item the ReceivedItem with the itemType field set
*/
function withItemType(
ReceivedItem memory item,
@@ -189,10 +285,12 @@ library ReceivedItemLib {
}
/**
- * @notice sets the token address
- * @param item the ReceivedItem to modify
- * @param token the token address to set
- * @return the modified ReceivedItem
+ * @dev Sets the token field of an ReceivedItem.
+ *
+ * @param item the ReceivedItem to set the token field of
+ * @param token the token to set the token field to
+ *
+ * @custom:return item the ReceivedItem with the token field set
*/
function withToken(
ReceivedItem memory item,
@@ -203,10 +301,12 @@ library ReceivedItemLib {
}
/**
- * @notice sets the identifier or criteria
- * @param item the ReceivedItem to modify
- * @param identifier the identifier or criteria to set
- * @return the modified ReceivedItem
+ * @dev Sets the identifier field of an ReceivedItem.
+ *
+ * @param item the ReceivedItem to set the identifier field of
+ * @param identifier the identifier to set the identifier field to
+ *
+ * @custom:return item the ReceivedItem with the identifier field set
*/
function withIdentifier(
ReceivedItem memory item,
@@ -217,10 +317,12 @@ library ReceivedItemLib {
}
/**
- * @notice sets the start amount
- * @param item the ReceivedItem to modify
- * @param amount the start amount to set
- * @return the modified ReceivedItem
+ * @dev Sets the amount field of an ReceivedItem.
+ *
+ * @param item the ReceivedItem to set the amount field of
+ * @param amount the amount to set the amount field to
+ *
+ * @custom:return item the ReceivedItem with the amount field set
*/
function withAmount(
ReceivedItem memory item,
@@ -231,10 +333,12 @@ library ReceivedItemLib {
}
/**
- * @notice sets the recipient
- * @param item the ReceivedItem to modify
- * @param recipient the recipient to set
- * @return the modified ReceivedItem
+ * @dev Sets the recipient field of an ReceivedItem.
+ *
+ * @param item the ReceivedItem to set the recipient field of
+ * @param recipient the recipient to set the recipient field to
+ *
+ * @custom:return item the ReceivedItem with the recipient field set
*/
function withRecipient(
ReceivedItem memory item,
@@ -244,6 +348,13 @@ library ReceivedItemLib {
return item;
}
+ /**
+ * @dev Converts an ReceivedItem to a ConsiderationItem.
+ *
+ * @param item the ReceivedItem to convert to a ConsiderationItem
+ *
+ * @custom:return considerationItem the converted ConsiderationItem
+ */
function toConsiderationItem(
ReceivedItem memory item
) internal pure returns (ConsiderationItem memory) {
diff --git a/contracts/helpers/sol/lib/SeaportArrays.sol b/contracts/helpers/sol/lib/SeaportArrays.sol
index 40685a808..7372b2487 100644
--- a/contracts/helpers/sol/lib/SeaportArrays.sol
+++ b/contracts/helpers/sol/lib/SeaportArrays.sol
@@ -2,19 +2,19 @@
pragma solidity ^0.8.17;
import {
- OrderComponents,
- OfferItem,
- ConsiderationItem,
- SpentItem,
- ReceivedItem,
- BasicOrderParameters,
AdditionalRecipient,
- OrderParameters,
- Order,
AdvancedOrder,
+ BasicOrderParameters,
+ ConsiderationItem,
CriteriaResolver,
Fulfillment,
- FulfillmentComponent
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
} from "../../../lib/ConsiderationStructs.sol";
library SeaportArrays {
diff --git a/contracts/helpers/sol/lib/SeaportEnumsLib.sol b/contracts/helpers/sol/lib/SeaportEnumsLib.sol
index da3159957..d53216640 100644
--- a/contracts/helpers/sol/lib/SeaportEnumsLib.sol
+++ b/contracts/helpers/sol/lib/SeaportEnumsLib.sol
@@ -2,17 +2,26 @@
pragma solidity ^0.8.17;
import {
- BasicOrderParameters,
- OrderParameters
-} from "../../../lib/ConsiderationStructs.sol";
-import {
- OrderType,
BasicOrderType,
ItemType,
- BasicOrderRouteType
+ OrderType
} from "../../../lib/ConsiderationEnums.sol";
library SeaportEnumsLib {
+ /**
+ * @dev Parses a BasicOrderType into its constituent parts.
+ *
+ * @param basicOrderType the BasicOrderType to parse
+ *
+ * @return orderType the OrderType
+ * @return offerType the ItemType of the offer
+ * @return considerationType the ItemType of the
+ * consideration
+ * @return additionalRecipientsType the ItemType of the
+ * additional recipients
+ * @return offerTypeIsAdditionalRecipientsType whether the offer type is the
+ * additional recipients type
+ */
function parseBasicOrderType(
BasicOrderType basicOrderType
)
diff --git a/contracts/helpers/sol/lib/SeaportStructLib.sol b/contracts/helpers/sol/lib/SeaportStructLib.sol
index fca841117..08cef330f 100644
--- a/contracts/helpers/sol/lib/SeaportStructLib.sol
+++ b/contracts/helpers/sol/lib/SeaportStructLib.sol
@@ -15,6 +15,7 @@ import { OrderComponentsLib } from "./OrderComponentsLib.sol";
import { OrderLib } from "./OrderLib.sol";
import { OrderParametersLib } from "./OrderParametersLib.sol";
import { ReceivedItemLib } from "./ReceivedItemLib.sol";
+import { SeaportArrays } from "./SeaportArrays.sol";
import { SpentItemLib } from "./SpentItemLib.sol";
import { StructCopier } from "./StructCopier.sol";
-import { SeaportArrays } from "./SeaportArrays.sol";
+import { ZoneParametersLib } from "./ZoneParametersLib.sol";
diff --git a/contracts/helpers/sol/lib/SpentItemLib.sol b/contracts/helpers/sol/lib/SpentItemLib.sol
index 3aa2f2da1..417d996ad 100644
--- a/contracts/helpers/sol/lib/SpentItemLib.sol
+++ b/contracts/helpers/sol/lib/SpentItemLib.sol
@@ -1,20 +1,48 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
-import { SpentItem, OfferItem } from "../../../lib/ConsiderationStructs.sol";
+import { OfferItem, SpentItem } from "../../../lib/ConsiderationStructs.sol";
+
import { ItemType } from "../../../lib/ConsiderationEnums.sol";
-import { StructCopier } from "./StructCopier.sol";
+/**
+ * @title SpentItemLib
+ * @author James Wenzel (emo.eth)
+ * @notice SpentItemLib is a library for managing SpentItem structs and arrays.
+ * It allows chaining of functions to make struct creation more
+ * readable.
+ */
library SpentItemLib {
bytes32 private constant SPENT_ITEM_MAP_POSITION =
keccak256("seaport.SpentItemDefaults");
bytes32 private constant SPENT_ITEMS_MAP_POSITION =
keccak256("seaport.SpentItemsDefaults");
+ bytes32 private constant EMPTY_SPENT_ITEM =
+ keccak256(
+ abi.encode(
+ SpentItem({
+ itemType: ItemType(0),
+ token: address(0),
+ identifier: 0,
+ amount: 0
+ })
+ )
+ );
+ /**
+ * @dev Creates an empty SpentItem.
+ *
+ * @return the empty SpentItem
+ */
function empty() internal pure returns (SpentItem memory) {
return SpentItem(ItemType(0), address(0), 0, 0);
}
+ /**
+ * @dev Clears an SpentItem from storage.
+ *
+ * @param item the item to clear
+ */
function clear(SpentItem storage item) internal {
// clear all fields
item.itemType = ItemType(0);
@@ -23,6 +51,11 @@ library SpentItemLib {
item.amount = 0;
}
+ /**
+ * @dev Clears an array of SpentItems from storage.
+ *
+ * @param items the items to clear
+ */
function clearMany(SpentItem[] storage items) internal {
while (items.length > 0) {
clear(items[items.length - 1]);
@@ -31,16 +64,21 @@ library SpentItemLib {
}
/**
- * @notice clears a default SpentItem from storage
+ * @dev Clears a default SpentItem from storage.
+ *
* @param defaultName the name of the default to clear
*/
-
function clear(string memory defaultName) internal {
mapping(string => SpentItem) storage spentItemMap = _spentItemMap();
SpentItem storage item = spentItemMap[defaultName];
clear(item);
}
+ /**
+ * @dev Clears an array of default SpentItems from storage.
+ *
+ * @param defaultsName the name of the default to clear
+ */
function clearMany(string memory defaultsName) internal {
mapping(string => SpentItem[]) storage spentItemsMap = _spentItemsMap();
SpentItem[] storage items = spentItemsMap[defaultsName];
@@ -48,27 +86,48 @@ library SpentItemLib {
}
/**
- * @notice gets a default SpentItem from storage
+ * @dev Gets a default SpentItem from storage.
+ *
* @param defaultName the name of the default for retrieval
+ *
+ * @return item the SpentItem
*/
function fromDefault(
string memory defaultName
) internal view returns (SpentItem memory item) {
mapping(string => SpentItem) storage spentItemMap = _spentItemMap();
item = spentItemMap[defaultName];
+
+ if (keccak256(abi.encode(item)) == EMPTY_SPENT_ITEM) {
+ revert("Empty SpentItem selected.");
+ }
}
+ /**
+ * @dev Gets an array of default SpentItems from storage.
+ *
+ * @param defaultsName the name of the default for retrieval
+ *
+ * @return items the SpentItems
+ */
function fromDefaultMany(
string memory defaultsName
) internal view returns (SpentItem[] memory items) {
mapping(string => SpentItem[]) storage spentItemsMap = _spentItemsMap();
items = spentItemsMap[defaultsName];
+
+ if (items.length == 0) {
+ revert("Empty SpentItem array selected.");
+ }
}
/**
- * @notice saves an SpentItem as a named default
+ * @dev Saves an SpentItem as a named default.
+ *
* @param spentItem the SpentItem to save as a default
* @param defaultName the name of the default for retrieval
+ *
+ * @return _spentItem the saved SpentItem
*/
function saveDefault(
SpentItem memory spentItem,
@@ -79,6 +138,14 @@ library SpentItemLib {
return spentItem;
}
+ /**
+ * @dev Saves an array of SpentItems as a named default.
+ *
+ * @param spentItems the SpentItems to save as a default
+ * @param defaultsName the name of the default for retrieval
+ *
+ * @return _spentItems the saved SpentItems
+ */
function saveDefaultMany(
SpentItem[] memory spentItems,
string memory defaultsName
@@ -89,6 +156,14 @@ library SpentItemLib {
return spentItems;
}
+ /**
+ * @dev Sets an array of in-memory SpentItems to an array of SpentItems in
+ * storage.
+ *
+ * @param items the SpentItem array in storage to push to
+ * @param newItems the SpentItem array in memory to push onto the items
+ * array
+ */
function setSpentItems(
SpentItem[] storage items,
SpentItem[] memory newItems
@@ -100,8 +175,11 @@ library SpentItemLib {
}
/**
- * @notice makes a copy of an SpentItem in-memory
+ * @dev Makes a copy of an SpentItem in-memory.
+ *
* @param item the SpentItem to make a copy of in-memory
+ *
+ * @custom:return copiedItem the copied SpentItem
*/
function copy(
SpentItem memory item
@@ -115,6 +193,13 @@ library SpentItemLib {
});
}
+ /**
+ * @dev Makes a copy of an array of SpentItems in-memory.
+ *
+ * @param items the SpentItems to make a copy of in-memory
+ *
+ * @custom:return copiedItems the copied SpentItems
+ */
function copy(
SpentItem[] memory items
) internal pure returns (SpentItem[] memory) {
@@ -126,7 +211,9 @@ library SpentItemLib {
}
/**
- * @notice gets the storage position of the default SpentItem map
+ * @dev Gets the storage position of the default SpentItem map.
+ *
+ * @custom:return position the storage position of the default SpentItem map
*/
function _spentItemMap()
private
@@ -139,6 +226,12 @@ library SpentItemLib {
}
}
+ /**
+ * @dev Gets the storage position of the default SpentItem array map.
+ *
+ * @custom:return position the storage position of the default SpentItem
+ * array map
+ */
function _spentItemsMap()
private
pure
@@ -150,14 +243,16 @@ library SpentItemLib {
}
}
- // methods for configuring a single of each of an SpentItem's fields, which modifies the SpentItem in-place and
- // returns it
+ // Methods for configuring a single of each of a SpentItem's fields, which
+ // modify the SpentItem struct in-place and return it.
/**
- * @notice sets the item type
- * @param item the SpentItem to modify
- * @param itemType the item type to set
- * @return the modified SpentItem
+ * @dev Sets the itemType field of a SpentItem.
+ *
+ * @param item the SpentItem to set the itemType field of
+ * @param itemType the itemType to set the itemType field to
+ *
+ * @custom:return item the SpentItem with the itemType field set
*/
function withItemType(
SpentItem memory item,
@@ -168,10 +263,12 @@ library SpentItemLib {
}
/**
- * @notice sets the token address
- * @param item the SpentItem to modify
- * @param token the token address to set
- * @return the modified SpentItem
+ * @dev Sets the token field of a SpentItem.
+ *
+ * @param item the SpentItem to set the token field of
+ * @param token the token to set the token field to
+ *
+ * @custom:return item the SpentItem with the token field set
*/
function withToken(
SpentItem memory item,
@@ -182,10 +279,12 @@ library SpentItemLib {
}
/**
- * @notice sets the identifier or criteria
- * @param item the SpentItem to modify
- * @param identifier the identifier or criteria to set
- * @return the modified SpentItem
+ * @dev Sets the identifier field of a SpentItem.
+ *
+ * @param item the SpentItem to set the identifier field of
+ * @param identifier the identifier to set the identifier field to
+ *
+ * @custom:return item the SpentItem with the identifier field set
*/
function withIdentifier(
SpentItem memory item,
@@ -196,10 +295,12 @@ library SpentItemLib {
}
/**
- * @notice sets the start amount
- * @param item the SpentItem to modify
- * @param amount the start amount to set
- * @return the modified SpentItem
+ * @dev Sets the amount field of a SpentItem.
+ *
+ * @param item the SpentItem to set the amount field of
+ * @param amount the amount to set the amount field to
+ *
+ * @custom:return item the SpentItem with the amount field set
*/
function withAmount(
SpentItem memory item,
@@ -209,6 +310,13 @@ library SpentItemLib {
return item;
}
+ /**
+ * @dev Converts a SpentItem to an OfferItem.
+ *
+ * @param item the SpentItem to convert to an OfferItem
+ *
+ * @custom:return offerItem the converted OfferItem
+ */
function toOfferItem(
SpentItem memory item
) internal pure returns (OfferItem memory) {
diff --git a/contracts/helpers/sol/lib/StructCopier.sol b/contracts/helpers/sol/lib/StructCopier.sol
index 36436af05..55bd466da 100644
--- a/contracts/helpers/sol/lib/StructCopier.sol
+++ b/contracts/helpers/sol/lib/StructCopier.sol
@@ -2,22 +2,20 @@
pragma solidity ^0.8.13;
import {
- BasicOrderParameters,
- CriteriaResolver,
- AdvancedOrder,
AdditionalRecipient,
- OfferItem,
- Order,
+ AdvancedOrder,
+ BasicOrderParameters,
ConsiderationItem,
+ CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
- OrderParameters,
+ OfferItem,
+ Order,
OrderComponents,
- Execution
+ OrderParameters
} from "../../../lib/ConsiderationStructs.sol";
-import {
- ConsiderationInterface
-} from "../../../interfaces/ConsiderationInterface.sol";
+
import { ArrayLib } from "./ArrayLib.sol";
library StructCopier {
diff --git a/contracts/helpers/sol/lib/ZoneParametersLib.sol b/contracts/helpers/sol/lib/ZoneParametersLib.sol
new file mode 100644
index 000000000..09e88fb08
--- /dev/null
+++ b/contracts/helpers/sol/lib/ZoneParametersLib.sol
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ItemType, Side, OrderType } from "../../../lib/ConsiderationEnums.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ SpentItem,
+ ReceivedItem,
+ ZoneParameters,
+ CriteriaResolver
+} from "../../../lib/ConsiderationStructs.sol";
+
+import { SeaportInterface } from "../SeaportInterface.sol";
+
+import { GettersAndDerivers } from "../../../lib/GettersAndDerivers.sol";
+
+import { UnavailableReason } from "../SpaceEnums.sol";
+
+import { AdvancedOrderLib } from "./AdvancedOrderLib.sol";
+
+import { ConsiderationItemLib } from "./ConsiderationItemLib.sol";
+
+import { OfferItemLib } from "./OfferItemLib.sol";
+
+import { ReceivedItemLib } from "./ReceivedItemLib.sol";
+
+import { OrderParametersLib } from "./OrderParametersLib.sol";
+
+import { StructCopier } from "./StructCopier.sol";
+
+import { AmountDeriverHelper } from "./fulfillment/AmountDeriverHelper.sol";
+
+import { OrderDetails } from "../fulfillments/lib/Structs.sol";
+
+interface FailingContractOfferer {
+ function failureReasons(bytes32) external view returns (uint256);
+}
+
+library ZoneParametersLib {
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using OrderParametersLib for OrderParameters;
+
+ struct ZoneParametersStruct {
+ AdvancedOrder[] advancedOrders;
+ address fulfiller;
+ uint256 maximumFulfilled;
+ address seaport;
+ CriteriaResolver[] criteriaResolvers;
+ }
+
+ struct ZoneDetails {
+ AdvancedOrder[] advancedOrders;
+ address fulfiller;
+ uint256 maximumFulfilled;
+ OrderDetails[] orderDetails;
+ bytes32[] orderHashes;
+ }
+
+ function getZoneParameters(
+ AdvancedOrder memory advancedOrder,
+ address fulfiller,
+ uint256 counter,
+ address seaport,
+ CriteriaResolver[] memory criteriaResolvers
+ ) internal view returns (ZoneParameters memory zoneParameters) {
+ SeaportInterface seaportInterface = SeaportInterface(seaport);
+ // Get orderParameters from advancedOrder
+ OrderParameters memory orderParameters = advancedOrder.parameters;
+
+ // Get orderHash
+ bytes32 orderHash = advancedOrder.getTipNeutralizedOrderHash(
+ seaportInterface,
+ counter
+ );
+
+ (
+ SpentItem[] memory spentItems,
+ ReceivedItem[] memory receivedItems
+ ) = orderParameters.getSpentAndReceivedItems(
+ advancedOrder.numerator,
+ advancedOrder.denominator,
+ 0,
+ criteriaResolvers
+ );
+
+ // Store orderHash in orderHashes array to pass into zoneParameters
+ bytes32[] memory orderHashes = new bytes32[](1);
+ orderHashes[0] = orderHash;
+
+ // Create ZoneParameters and add to zoneParameters array
+ zoneParameters = ZoneParameters({
+ orderHash: orderHash,
+ fulfiller: fulfiller,
+ offerer: orderParameters.offerer,
+ offer: spentItems,
+ consideration: receivedItems,
+ extraData: advancedOrder.extraData,
+ orderHashes: orderHashes,
+ startTime: orderParameters.startTime,
+ endTime: orderParameters.endTime,
+ zoneHash: orderParameters.zoneHash
+ });
+ }
+
+ function getZoneParameters(
+ AdvancedOrder[] memory advancedOrders,
+ address fulfiller,
+ uint256 maximumFulfilled,
+ address seaport,
+ CriteriaResolver[] memory criteriaResolvers,
+ UnavailableReason[] memory unavailableReasons
+ ) internal view returns (ZoneParameters[] memory) {
+ return
+ _getZoneParametersFromStruct(
+ _getZoneParametersStruct(
+ advancedOrders,
+ fulfiller,
+ maximumFulfilled,
+ seaport,
+ criteriaResolvers
+ ), unavailableReasons
+ );
+ }
+
+ function _getZoneParametersStruct(
+ AdvancedOrder[] memory advancedOrders,
+ address fulfiller,
+ uint256 maximumFulfilled,
+ address seaport,
+ CriteriaResolver[] memory criteriaResolvers
+ ) internal pure returns (ZoneParametersStruct memory) {
+ return
+ ZoneParametersStruct(
+ advancedOrders,
+ fulfiller,
+ maximumFulfilled,
+ seaport,
+ criteriaResolvers
+ );
+ }
+
+ function _getZoneParametersFromStruct(
+ ZoneParametersStruct memory zoneParametersStruct,
+ UnavailableReason[] memory unavailableReasons
+ ) internal view returns (ZoneParameters[] memory) {
+ // TODO: use testHelpers pattern to use single amount deriver helper
+ ZoneDetails memory details = _getZoneDetails(zoneParametersStruct);
+
+ // Convert offer + consideration to spent + received
+ _applyOrderDetails(details, zoneParametersStruct, unavailableReasons);
+
+ // Iterate over advanced orders to calculate orderHashes
+ _applyOrderHashes(details, zoneParametersStruct.seaport);
+
+ return _finalizeZoneParameters(details);
+ }
+
+ function _getZoneDetails(
+ ZoneParametersStruct memory zoneParametersStruct
+ ) internal pure returns (ZoneDetails memory) {
+ return
+ ZoneDetails({
+ advancedOrders: zoneParametersStruct.advancedOrders,
+ fulfiller: zoneParametersStruct.fulfiller,
+ maximumFulfilled: zoneParametersStruct.maximumFulfilled,
+ orderDetails: new OrderDetails[](
+ zoneParametersStruct.advancedOrders.length
+ ),
+ orderHashes: new bytes32[](
+ zoneParametersStruct.advancedOrders.length
+ )
+ });
+ }
+
+ function _applyOrderDetails(
+ ZoneDetails memory details,
+ ZoneParametersStruct memory zoneParametersStruct,
+ UnavailableReason[] memory unavailableReasons
+ ) internal view {
+ bytes32[] memory orderHashes = details.advancedOrders.getOrderHashes(
+ zoneParametersStruct.seaport
+ );
+
+ details.orderDetails = zoneParametersStruct
+ .advancedOrders
+ .getOrderDetails(
+ zoneParametersStruct.criteriaResolvers,
+ orderHashes,
+ unavailableReasons
+ );
+ }
+
+ function _applyOrderHashes(
+ ZoneDetails memory details,
+ address seaport
+ ) internal view {
+ bytes32[] memory orderHashes = details.advancedOrders.getOrderHashes(
+ seaport
+ );
+
+ uint256 totalFulfilled = 0;
+ // Iterate over advanced orders to calculate orderHashes
+ for (uint256 i = 0; i < details.advancedOrders.length; i++) {
+ bytes32 orderHash = orderHashes[i];
+
+ if (
+ totalFulfilled >= details.maximumFulfilled ||
+ _isUnavailable(
+ details.advancedOrders[i].parameters,
+ orderHash,
+ SeaportInterface(seaport)
+ )
+ ) {
+ // Set orderHash to 0 if order index exceeds maximumFulfilled
+ details.orderHashes[i] = bytes32(0);
+ } else {
+ // Add orderHash to orderHashes and increment totalFulfilled/
+ details.orderHashes[i] = orderHash;
+ ++totalFulfilled;
+ }
+ }
+ }
+
+ function _isUnavailable(
+ OrderParameters memory order,
+ bytes32 orderHash,
+ SeaportInterface seaport
+ ) internal view returns (bool) {
+ (, bool isCancelled, uint256 totalFilled, uint256 totalSize) = seaport
+ .getOrderStatus(orderHash);
+
+ bool isRevertingContractOrder = false;
+ if (order.orderType == OrderType.CONTRACT) {
+ isRevertingContractOrder =
+ FailingContractOfferer(order.offerer).failureReasons(
+ orderHash
+ ) !=
+ 0;
+ }
+
+ return (block.timestamp >= order.endTime ||
+ block.timestamp < order.startTime ||
+ isCancelled ||
+ isRevertingContractOrder ||
+ (totalFilled >= totalSize && totalSize > 0));
+ }
+
+ function _finalizeZoneParameters(
+ ZoneDetails memory zoneDetails
+ ) internal pure returns (ZoneParameters[] memory zoneParameters) {
+ zoneParameters = new ZoneParameters[](
+ zoneDetails.advancedOrders.length
+ );
+
+ // Iterate through advanced orders to create zoneParameters
+ uint256 totalFulfilled = 0;
+
+ for (uint i = 0; i < zoneDetails.advancedOrders.length; i++) {
+ if (totalFulfilled >= zoneDetails.maximumFulfilled) {
+ break;
+ }
+
+ if (zoneDetails.orderHashes[i] != bytes32(0)) {
+ // Create ZoneParameters and add to zoneParameters array
+ zoneParameters[i] = _createZoneParameters(
+ zoneDetails.orderHashes[i],
+ zoneDetails.orderDetails[i],
+ zoneDetails.advancedOrders[i],
+ zoneDetails.fulfiller,
+ zoneDetails.orderHashes
+ );
+ ++totalFulfilled;
+ }
+ }
+
+ return zoneParameters;
+ }
+
+ function _createZoneParameters(
+ bytes32 orderHash,
+ OrderDetails memory orderDetails,
+ AdvancedOrder memory advancedOrder,
+ address fulfiller,
+ bytes32[] memory orderHashes
+ ) internal pure returns (ZoneParameters memory) {
+ return
+ ZoneParameters({
+ orderHash: orderHash,
+ fulfiller: fulfiller,
+ offerer: advancedOrder.parameters.offerer,
+ offer: orderDetails.offer,
+ consideration: orderDetails.consideration,
+ extraData: advancedOrder.extraData,
+ orderHashes: orderHashes,
+ startTime: advancedOrder.parameters.startTime,
+ endTime: advancedOrder.parameters.endTime,
+ zoneHash: advancedOrder.parameters.zoneHash
+ });
+ }
+}
diff --git a/contracts/helpers/sol/lib/fulfillment/AmountDeriverHelper.sol b/contracts/helpers/sol/lib/fulfillment/AmountDeriverHelper.sol
new file mode 100644
index 000000000..9c0287b2e
--- /dev/null
+++ b/contracts/helpers/sol/lib/fulfillment/AmountDeriverHelper.sol
@@ -0,0 +1,560 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { SeaportInterface } from "../../../../interfaces/SeaportInterface.sol";
+import { AmountDeriver } from "../../../../lib/AmountDeriver.sol";
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ OrderType,
+ ReceivedItem,
+ SpentItem
+} from "../../../../lib/ConsiderationStructs.sol";
+import { Side, ItemType } from "../../../../lib/ConsiderationEnums.sol";
+import { OfferItemLib } from "../OfferItemLib.sol";
+import { ConsiderationItemLib } from "../ConsiderationItemLib.sol";
+import { OrderParametersLib } from "../OrderParametersLib.sol";
+import { OrderDetails } from "../../fulfillments/lib/Structs.sol";
+import { UnavailableReason } from "../../SpaceEnums.sol";
+
+/**
+ * @notice Note that this contract relies on current block.timestamp to determine amounts.
+ */
+contract AmountDeriverHelper is AmountDeriver {
+ using OfferItemLib for OfferItem[];
+ using ConsiderationItemLib for ConsiderationItem[];
+ using OrderParametersLib for OrderParameters;
+
+ struct ContractNonceDetails {
+ bool set;
+ address offerer;
+ uint256 currentNonce;
+ }
+
+ function getSpentAndReceivedItems(
+ Order calldata order
+ )
+ external
+ view
+ returns (SpentItem[] memory spent, ReceivedItem[] memory received)
+ {
+ return getSpentAndReceivedItems(order.parameters);
+ }
+
+ function getSpentAndReceivedItems(
+ AdvancedOrder calldata order
+ )
+ external
+ view
+ returns (SpentItem[] memory spent, ReceivedItem[] memory received)
+ {
+ CriteriaResolver[] memory resolvers;
+ return
+ getSpentAndReceivedItems(
+ order.parameters,
+ order.numerator,
+ order.denominator,
+ 0,
+ resolvers
+ );
+ }
+
+ function getSpentAndReceivedItems(
+ AdvancedOrder calldata order,
+ uint256 orderIndex,
+ CriteriaResolver[] calldata criteriaResolvers
+ )
+ external
+ view
+ returns (SpentItem[] memory spent, ReceivedItem[] memory received)
+ {
+ return
+ getSpentAndReceivedItems(
+ order.parameters,
+ order.numerator,
+ order.denominator,
+ orderIndex,
+ criteriaResolvers
+ );
+ }
+
+ function getSpentAndReceivedItems(
+ OrderParameters calldata parameters
+ )
+ public
+ view
+ returns (SpentItem[] memory spent, ReceivedItem[] memory received)
+ {
+ if (parameters.isAvailable()) {
+ spent = getSpentItems(parameters);
+ received = getReceivedItems(parameters);
+ }
+ }
+
+ function toOrderDetails(
+ OrderParameters memory order,
+ bytes32 orderHash,
+ UnavailableReason unavailableReason
+ ) internal view returns (OrderDetails memory) {
+ (SpentItem[] memory offer, ReceivedItem[] memory consideration) = this
+ .getSpentAndReceivedItems(order);
+ return
+ OrderDetails({
+ offerer: order.offerer,
+ conduitKey: order.conduitKey,
+ offer: offer,
+ consideration: consideration,
+ isContract: order.orderType == OrderType.CONTRACT,
+ orderHash: orderHash,
+ unavailableReason: unavailableReason
+ });
+ }
+
+ function toOrderDetails(
+ Order[] memory order,
+ bytes32[] memory orderHashes,
+ UnavailableReason[] memory unavailableReasons
+ ) public view returns (OrderDetails[] memory) {
+ OrderDetails[] memory orderDetails = new OrderDetails[](order.length);
+ for (uint256 i = 0; i < order.length; i++) {
+ orderDetails[i] = toOrderDetails(
+ order[i].parameters,
+ orderHashes[i],
+ unavailableReasons[i]
+ );
+ }
+ return orderDetails;
+ }
+
+ function toOrderDetails(
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ bytes32[] memory orderHashes,
+ UnavailableReason[] memory unavailableReasons
+ ) public view returns (OrderDetails[] memory) {
+ OrderDetails[] memory orderDetails = new OrderDetails[](orders.length);
+ for (uint256 i = 0; i < orders.length; i++) {
+ orderDetails[i] = toOrderDetails(
+ orders[i],
+ i,
+ resolvers,
+ orderHashes[i],
+ unavailableReasons[i]
+ );
+ }
+ return orderDetails;
+ }
+
+ function toOrderDetails(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ CriteriaResolver[] memory resolvers,
+ bytes32 orderHash,
+ UnavailableReason unavailableReason
+ ) internal view returns (OrderDetails memory) {
+ (SpentItem[] memory offer, ReceivedItem[] memory consideration) = this
+ .getSpentAndReceivedItems(order, orderIndex, resolvers);
+
+ return
+ OrderDetails({
+ offerer: order.parameters.offerer,
+ conduitKey: order.parameters.conduitKey,
+ offer: offer,
+ consideration: consideration,
+ isContract: order.parameters.orderType == OrderType.CONTRACT,
+ orderHash: orderHash,
+ unavailableReason: unavailableReason
+ });
+ }
+
+ function getSpentAndReceivedItems(
+ OrderParameters memory parameters,
+ uint256 numerator,
+ uint256 denominator,
+ uint256 orderIndex,
+ CriteriaResolver[] memory criteriaResolvers
+ )
+ private
+ view
+ returns (SpentItem[] memory spent, ReceivedItem[] memory received)
+ {
+ if (parameters.isAvailable()) {
+ spent = getSpentItems(parameters, numerator, denominator);
+ received = getReceivedItems(parameters, numerator, denominator);
+
+ applyCriteriaResolvers(
+ spent,
+ received,
+ orderIndex,
+ criteriaResolvers
+ );
+ }
+ }
+
+ function applyCriteriaResolvers(
+ SpentItem[] memory spentItems,
+ ReceivedItem[] memory receivedItems,
+ uint256 orderIndex,
+ CriteriaResolver[] memory criteriaResolvers
+ ) private pure {
+ for (uint256 i = 0; i < criteriaResolvers.length; i++) {
+ CriteriaResolver memory resolver = criteriaResolvers[i];
+ if (resolver.orderIndex != orderIndex) {
+ continue;
+ }
+ if (resolver.side == Side.OFFER) {
+ SpentItem memory item = spentItems[resolver.index];
+ item.itemType = convertCriteriaItemType(item.itemType);
+ item.identifier = resolver.identifier;
+ } else {
+ ReceivedItem memory item = receivedItems[resolver.index];
+ item.itemType = convertCriteriaItemType(item.itemType);
+ item.identifier = resolver.identifier;
+ }
+ }
+ }
+
+ function convertCriteriaItemType(
+ ItemType itemType
+ ) internal pure returns (ItemType) {
+ if (itemType == ItemType.ERC721_WITH_CRITERIA) {
+ return ItemType.ERC721;
+ } else if (itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ return ItemType.ERC1155;
+ } else {
+ revert("amount deriver helper resolving non criteria item type");
+ }
+ }
+
+ function getSpentItems(
+ OrderParameters memory parameters,
+ uint256 numerator,
+ uint256 denominator
+ ) private view returns (SpentItem[] memory) {
+ return
+ getSpentItems(
+ parameters.offer,
+ parameters.startTime,
+ parameters.endTime,
+ numerator,
+ denominator
+ );
+ }
+
+ function getSpentItems(
+ OrderParameters memory parameters
+ ) private view returns (SpentItem[] memory) {
+ return
+ getSpentItems(
+ parameters.offer,
+ parameters.startTime,
+ parameters.endTime
+ );
+ }
+
+ function getSpentItems(
+ OfferItem[] memory offerItems,
+ uint256 startTime,
+ uint256 endTime
+ ) private view returns (SpentItem[] memory) {
+ SpentItem[] memory spentItems = new SpentItem[](offerItems.length);
+ for (uint256 i = 0; i < offerItems.length; i++) {
+ spentItems[i] = getSpentItem(offerItems[i], startTime, endTime);
+ }
+ return spentItems;
+ }
+
+ function getSpentItems(
+ OfferItem[] memory items,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) private view returns (SpentItem[] memory) {
+ SpentItem[] memory spentItems = new SpentItem[](items.length);
+ for (uint256 i = 0; i < items.length; i++) {
+ spentItems[i] = getSpentItem(
+ items[i],
+ startTime,
+ endTime,
+ numerator,
+ denominator
+ );
+ }
+ return spentItems;
+ }
+
+ function getSpentItem(
+ OfferItem memory offerItem,
+ uint256 startTime,
+ uint256 endTime
+ ) private view returns (SpentItem memory spent) {
+ spent = SpentItem({
+ itemType: offerItem.itemType,
+ token: offerItem.token,
+ identifier: offerItem.identifierOrCriteria,
+ amount: _locateCurrentAmount({
+ item: offerItem,
+ startTime: startTime,
+ endTime: endTime
+ })
+ });
+ }
+
+ function getSpentItem(
+ OfferItem memory item,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) private view returns (SpentItem memory spent) {
+ // Detect if the order has an invalid time;
+ // if so, set amount to zero
+ spent = SpentItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifierOrCriteria,
+ amount: (block.timestamp < startTime || block.timestamp >= endTime)
+ ? 0
+ : _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ item: item,
+ startTime: startTime,
+ endTime: endTime
+ })
+ });
+ }
+
+ function getReceivedItems(
+ OrderParameters memory parameters
+ ) private view returns (ReceivedItem[] memory) {
+ return
+ getReceivedItems(
+ parameters.consideration,
+ parameters.startTime,
+ parameters.endTime
+ );
+ }
+
+ function getReceivedItems(
+ OrderParameters memory parameters,
+ uint256 numerator,
+ uint256 denominator
+ ) private view returns (ReceivedItem[] memory) {
+ return
+ getReceivedItems(
+ parameters.consideration,
+ parameters.startTime,
+ parameters.endTime,
+ numerator,
+ denominator
+ );
+ }
+
+ function getReceivedItems(
+ ConsiderationItem[] memory considerationItems,
+ uint256 startTime,
+ uint256 endTime
+ ) private view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ considerationItems.length
+ );
+ for (uint256 i = 0; i < considerationItems.length; i++) {
+ receivedItems[i] = getReceivedItem(
+ considerationItems[i],
+ startTime,
+ endTime
+ );
+ }
+ return receivedItems;
+ }
+
+ function getReceivedItems(
+ ConsiderationItem[] memory considerationItems,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) private view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ considerationItems.length
+ );
+ for (uint256 i = 0; i < considerationItems.length; i++) {
+ receivedItems[i] = getReceivedItem(
+ considerationItems[i],
+ startTime,
+ endTime,
+ numerator,
+ denominator
+ );
+ }
+ return receivedItems;
+ }
+
+ function getReceivedItem(
+ ConsiderationItem memory considerationItem,
+ uint256 startTime,
+ uint256 endTime
+ ) private view returns (ReceivedItem memory received) {
+ received = ReceivedItem({
+ itemType: considerationItem.itemType,
+ token: considerationItem.token,
+ identifier: considerationItem.identifierOrCriteria,
+ amount: _locateCurrentAmount({
+ item: considerationItem,
+ startTime: startTime,
+ endTime: endTime
+ }),
+ recipient: considerationItem.recipient
+ });
+ }
+
+ function getReceivedItem(
+ ConsiderationItem memory considerationItem,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) private view returns (ReceivedItem memory received) {
+ // Detect if the order has an invalid time;
+ // if so, set amount to zero
+ received = ReceivedItem({
+ itemType: considerationItem.itemType,
+ token: considerationItem.token,
+ identifier: considerationItem.identifierOrCriteria,
+ amount: (block.timestamp < startTime || block.timestamp >= endTime)
+ ? 0
+ : _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ item: considerationItem,
+ startTime: startTime,
+ endTime: endTime
+ }),
+ recipient: considerationItem.recipient
+ });
+ }
+
+ function _locateCurrentAmount(
+ OfferItem memory item,
+ uint256 startTime,
+ uint256 endTime
+ ) private view returns (uint256) {
+ return
+ _locateCurrentAmount({
+ startAmount: item.startAmount,
+ endAmount: item.endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: false
+ });
+ }
+
+ function deriveFractionCompatibleAmounts(
+ uint256 originalStartAmount,
+ uint256 originalEndAmount,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 numerator,
+ uint256 denominator
+ ) public pure returns (uint256 newStartAmount, uint256 newEndAmount) {
+ if (
+ startTime >= endTime ||
+ numerator > denominator ||
+ numerator == 0 ||
+ denominator == 0 ||
+ (originalStartAmount == 0 && originalEndAmount == 0)
+ ) {
+ revert(
+ "AmountDeriverHelper: bad inputs to deriveFractionCompatibleAmounts"
+ );
+ }
+
+ uint256 duration = endTime - startTime;
+
+ // determine if duration or numerator is more likely to overflow when multiplied by value
+ uint256 overflowBottleneck = (numerator > duration)
+ ? numerator
+ : duration;
+
+ uint256 absoluteMax = type(uint256).max / overflowBottleneck;
+ uint256 fractionCompatibleMax = (absoluteMax / denominator) *
+ denominator;
+
+ newStartAmount = originalStartAmount % fractionCompatibleMax;
+ newStartAmount = (newStartAmount / denominator) * denominator;
+ newStartAmount = (newStartAmount == 0) ? denominator : newStartAmount;
+
+ newEndAmount = originalEndAmount % fractionCompatibleMax;
+ newEndAmount = (newEndAmount / denominator) * denominator;
+ newEndAmount = (newEndAmount == 0) ? denominator : newEndAmount;
+
+ if (newStartAmount == 0 && newEndAmount == 0) {
+ revert("AmountDeriverHelper: derived amount will always be zero");
+ }
+ }
+
+ function _locateCurrentAmount(
+ ConsiderationItem memory item,
+ uint256 startTime,
+ uint256 endTime
+ ) private view returns (uint256) {
+ return
+ _locateCurrentAmount({
+ startAmount: item.startAmount,
+ endAmount: item.endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: true
+ });
+ }
+
+ function _applyFraction(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 startTime,
+ uint256 endTime,
+ OfferItem memory item
+ ) internal view returns (uint256) {
+ uint256 startAmount = item.startAmount;
+ uint256 endAmount = item.endAmount;
+ return
+ _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startAmount: startAmount,
+ endAmount: endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: false // don't round up offers
+ });
+ }
+
+ function _applyFraction(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 startTime,
+ uint256 endTime,
+ ConsiderationItem memory item
+ ) internal view returns (uint256) {
+ uint256 startAmount = item.startAmount;
+ uint256 endAmount = item.endAmount;
+
+ return
+ _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startAmount: startAmount,
+ endAmount: endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: true // round up considerations
+ });
+ }
+}
diff --git a/contracts/helpers/sol/lib/types/MatchComponentType.sol b/contracts/helpers/sol/lib/types/MatchComponentType.sol
new file mode 100644
index 000000000..cb8ebe2ed
--- /dev/null
+++ b/contracts/helpers/sol/lib/types/MatchComponentType.sol
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { FulfillmentComponent } from "../../SeaportStructs.sol";
+
+struct MatchComponent {
+ uint256 amount;
+ uint8 orderIndex;
+ uint8 itemIndex;
+}
+
+library MatchComponentType {
+ using MatchComponentType for MatchComponent;
+
+ uint256 private constant AMOUNT_SHL_OFFSET = 16;
+ uint256 private constant ORDER_INDEX_SHL_OFFSET = 8;
+ uint256 private constant BYTE_MASK = 0xFF;
+ uint256 private constant AMOUNT_MASK =
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000;
+ uint256 private constant ORDER_INDEX_MASK = 0xFF00;
+ uint256 private constant ITEM_INDEX_MASK = 0xFF;
+ uint256 private constant NOT_AMOUNT_MASK = 0xFFFF;
+ uint256 private constant NOT_ORDER_INDEX_MASK =
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF;
+ uint256 private constant NOT_ITEM_INDEX_MASK =
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00;
+
+ function createMatchComponent(
+ uint256 amount,
+ uint8 orderIndex,
+ uint8 itemIndex
+ ) internal pure returns (MatchComponent memory component) {
+ component.amount = amount;
+ component.orderIndex = orderIndex;
+ component.itemIndex = itemIndex;
+ // assembly {
+ // component := or(
+ // shl(AMOUNT_SHL_OFFSET, amount),
+ // or(shl(ORDER_INDEX_SHL_OFFSET, orderIndex), itemIndex)
+ // )
+ // }
+ }
+
+ function getAmount(
+ MatchComponent memory component
+ ) internal pure returns (uint256 amount) {
+ return component.amount;
+ }
+
+ function copy(
+ MatchComponent memory component
+ ) internal pure returns (MatchComponent memory copy_) {
+ copy_.amount = component.amount;
+ copy_.orderIndex = component.orderIndex;
+ copy_.itemIndex = component.itemIndex;
+ }
+
+ function setAmount(
+ MatchComponent memory component,
+ uint256 amount
+ ) internal pure returns (MatchComponent memory newComponent) {
+ newComponent = component.copy();
+ newComponent.amount = amount;
+ }
+
+ function getOrderIndex(
+ MatchComponent memory component
+ ) internal pure returns (uint8 orderIndex) {
+ return component.orderIndex;
+ }
+
+ function setOrderIndex(
+ MatchComponent memory component,
+ uint8 orderIndex
+ ) internal pure returns (MatchComponent memory newComponent) {
+ newComponent = component.copy();
+ newComponent.orderIndex = orderIndex;
+ }
+
+ function getItemIndex(
+ MatchComponent memory component
+ ) internal pure returns (uint8 itemIndex) {
+ return component.itemIndex;
+ }
+
+ function setItemIndex(
+ MatchComponent memory component,
+ uint8 itemIndex
+ ) internal pure returns (MatchComponent memory newComponent) {
+ newComponent = component.copy();
+ newComponent.itemIndex = itemIndex;
+ }
+
+ function unpack(
+ MatchComponent memory component
+ )
+ internal
+ pure
+ returns (uint256 amount, uint8 orderIndex, uint8 itemIndex)
+ {
+ return (component.amount, component.orderIndex, component.itemIndex);
+ }
+
+ function subtractAmount(
+ MatchComponent memory minuend,
+ MatchComponent memory subtrahend
+ ) internal pure returns (MatchComponent memory newComponent) {
+ uint256 minuendAmount = minuend.getAmount();
+ uint256 subtrahendAmount = subtrahend.getAmount();
+ uint256 newAmount = uint256(minuendAmount - subtrahendAmount);
+ return minuend.setAmount(newAmount);
+ }
+
+ function addAmount(
+ MatchComponent memory target,
+ MatchComponent memory ref
+ ) internal pure returns (MatchComponent memory) {
+ uint256 targetAmount = target.getAmount();
+ uint256 refAmount = ref.getAmount();
+ uint256 newAmount = uint256(targetAmount + refAmount);
+ return target.setAmount(newAmount);
+ }
+
+ function toFulfillmentComponent(
+ MatchComponent memory component
+ ) internal pure returns (FulfillmentComponent memory) {
+ (, uint8 orderIndex, uint8 itemIndex) = component.unpack();
+ return
+ FulfillmentComponent({
+ orderIndex: orderIndex,
+ itemIndex: itemIndex
+ });
+ }
+
+ function toFulfillmentComponents(
+ MatchComponent[] memory components
+ ) internal pure returns (FulfillmentComponent[] memory) {
+ FulfillmentComponent[]
+ memory fulfillmentComponents = new FulfillmentComponent[](
+ components.length
+ );
+ for (uint256 i = 0; i < components.length; i++) {
+ fulfillmentComponents[i] = components[i].toFulfillmentComponent();
+ }
+ return fulfillmentComponents;
+ }
+
+ function toStruct(
+ MatchComponent memory component
+ ) internal pure returns (MatchComponent memory) {
+ (uint256 amount, uint8 orderIndex, uint8 itemIndex) = component
+ .unpack();
+ return
+ MatchComponent({
+ amount: amount,
+ orderIndex: orderIndex,
+ itemIndex: itemIndex
+ });
+ }
+
+ function toStructs(
+ MatchComponent[] memory components
+ ) internal pure returns (MatchComponent[] memory) {
+ MatchComponent[] memory structs = new MatchComponent[](
+ components.length
+ );
+ for (uint256 i = 0; i < components.length; i++) {
+ structs[i] = components[i].toStruct();
+ }
+ return structs;
+ }
+
+ function equals(
+ MatchComponent memory left,
+ MatchComponent memory right
+ ) internal pure returns (bool) {
+ return
+ left.amount == right.amount &&
+ left.orderIndex == right.orderIndex &&
+ left.itemIndex == right.itemIndex;
+ }
+}
diff --git a/contracts/interfaces/AbridgedTokenInterfaces.sol b/contracts/interfaces/AbridgedTokenInterfaces.sol
index 14d1000f3..9cadced25 100644
--- a/contracts/interfaces/AbridgedTokenInterfaces.sol
+++ b/contracts/interfaces/AbridgedTokenInterfaces.sol
@@ -30,20 +30,34 @@ interface ERC20Interface {
*
* @return success True if the approval was successful.
*/
-
function approve(
address spender,
uint256 value
) external returns (bool success);
/**
- * @dev Returns the amount of tokens owned by `account`.
+ * @dev Returns the balance of a user.
*
- * @param account The address of the account to check the balance of.
+ * @param account The address of the user.
*
- * @return balance The amount of tokens owned by `account`.
+ * @return balance The balance of the user.
*/
function balanceOf(address account) external view returns (uint256);
+
+ /**
+ * @dev Returns the amount which spender is still allowed to withdraw
+ * from owner.
+ *
+ * @param owner The address of the owner.
+ * @param spender The address of the spender.
+ *
+ * @return remaining The amount of tokens that the spender is allowed to
+ * transfer on behalf of the owner.
+ */
+ function allowance(
+ address owner,
+ address spender
+ ) external view returns (uint256 remaining);
}
/**
@@ -69,6 +83,31 @@ interface ERC721Interface {
*/
function setApprovalForAll(address to, bool approved) external;
+ /**
+ * @dev Returns the account approved for tokenId token
+ *
+ * @param tokenId The tokenId to query the approval of.
+ *
+ * @return operator The approved account of the tokenId.
+ */
+ function getApproved(
+ uint256 tokenId
+ ) external view returns (address operator);
+
+ /**
+ * @dev Returns whether an operator is allowed to manage all of
+ * the assets of owner.
+ *
+ * @param owner The address of the owner.
+ * @param operator The address of the operator.
+ *
+ * @return approved True if the operator is approved by the owner.
+ */
+ function isApprovedForAll(
+ address owner,
+ address operator
+ ) external view returns (bool);
+
/**
* @dev Returns the owner of a given token ID.
*
@@ -128,16 +167,29 @@ interface ERC1155Interface {
function setApprovalForAll(address to, bool approved) external;
/**
- * @dev Returns the owner of a given token ID.
+ * @dev Returns the amount of token type id owned by account.
*
- * @param account The address of the account to check the balance of.
- * @param id The token ID.
+ * @param account The address of the account.
+ * @param id The id of the token.
*
- * @return balance The balance of the token.
+ * @return balance The amount of tokens of type id owned by account.
*/
-
function balanceOf(
address account,
uint256 id
) external view returns (uint256);
+
+ /**
+ * @dev Returns true if operator is approved to transfer account's tokens.
+ *
+ * @param account The address of the account.
+ * @param operator The address of the operator.
+ *
+ * @return approved True if the operator is approved to transfer account's
+ * tokens.
+ */
+ function isApprovedForAll(
+ address account,
+ address operator
+ ) external view returns (bool);
}
diff --git a/contracts/interfaces/ConsiderationInterface.sol b/contracts/interfaces/ConsiderationInterface.sol
index 5829952a4..3a3d07d72 100644
--- a/contracts/interfaces/ConsiderationInterface.sol
+++ b/contracts/interfaces/ConsiderationInterface.sol
@@ -15,7 +15,7 @@ import {
/**
* @title ConsiderationInterface
* @author 0age
- * @custom:version 1.4
+ * @custom:version 1.5
* @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
* marketplace. It minimizes external calls to the greatest extent
* possible and provides lightweight methods for common routes as well
diff --git a/contracts/interfaces/ContractOffererInterface.sol b/contracts/interfaces/ContractOffererInterface.sol
index e38823cfa..72b6b1ff5 100644
--- a/contracts/interfaces/ContractOffererInterface.sol
+++ b/contracts/interfaces/ContractOffererInterface.sol
@@ -6,13 +6,14 @@ import {
Schema,
SpentItem
} from "../lib/ConsiderationStructs.sol";
+import { IERC165 } from "../interfaces/IERC165.sol";
/**
* @title ContractOffererInterface
* @notice Contains the minimum interfaces needed to interact with a contract
* offerer.
*/
-interface ContractOffererInterface {
+interface ContractOffererInterface is IERC165 {
/**
* @dev Generates an order with the specified minimum and maximum spent
* items, and optional context (supplied as extraData).
@@ -97,5 +98,9 @@ interface ContractOffererInterface {
Schema[] memory schemas // map to Seaport Improvement Proposal IDs
);
+ function supportsInterface(
+ bytes4 interfaceId
+ ) external view override returns (bool);
+
// Additional functions and/or events based on implemented schemaIDs
}
diff --git a/contracts/interfaces/ERC165.sol b/contracts/interfaces/ERC165.sol
new file mode 100644
index 000000000..2f46133dd
--- /dev/null
+++ b/contracts/interfaces/ERC165.sol
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
+
+pragma solidity ^0.8.7;
+
+import "./IERC165.sol";
+
+/**
+ * @dev Implementation of the {IERC165} interface.
+ *
+ * Contracts that want to implement ERC165 should inherit from this contract
+ * and override {supportsInterface} to check for the additional interface id
+ * that will be supported.
+ *
+ * Alternatively, {ERC165Storage} provides an easier to use but more
+ * expensive implementation.
+ */
+abstract contract ERC165 is IERC165 {
+ /**
+ * @dev See {IERC165-supportsInterface}.
+ */
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override returns (bool) {
+ return interfaceId == type(IERC165).interfaceId;
+ }
+}
diff --git a/contracts/interfaces/IERC1271.sol b/contracts/interfaces/IERC1271.sol
new file mode 100644
index 000000000..32d694bd5
--- /dev/null
+++ b/contracts/interfaces/IERC1271.sol
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
+
+pragma solidity ^0.8.7;
+
+/**
+ * @dev Interface of the ERC1271 standard signature validation method for
+ * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
+ *
+ * _Available since v4.1._
+ */
+interface IERC1271 {
+ /**
+ * @dev Should return whether the signature provided is valid for the
+ * provided data
+ * @param hash Hash of the data to be signed
+ * @param signature Signature byte array associated with _data
+ */
+ function isValidSignature(
+ bytes32 hash,
+ bytes memory signature
+ ) external view returns (bytes4 magicValue);
+}
diff --git a/contracts/interfaces/IERC165.sol b/contracts/interfaces/IERC165.sol
new file mode 100644
index 000000000..4c270d647
--- /dev/null
+++ b/contracts/interfaces/IERC165.sol
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
+
+pragma solidity ^0.8.7;
+
+/**
+ * @dev Interface of the ERC165 standard, as defined in the
+ * https://eips.ethereum.org/EIPS/eip-165[EIP].
+ *
+ * Implementers can declare support of contract interfaces, which can then be
+ * queried by others ({ERC165Checker}).
+ *
+ * For an implementation, see {ERC165}.
+ */
+interface IERC165 {
+ /**
+ * @dev Returns true if this contract implements the interface defined by
+ * `interfaceId`.
+ *
+ * This function call must use less than 30 000 gas.
+ */
+ function supportsInterface(bytes4 interfaceId) external view returns (bool);
+}
diff --git a/contracts/interfaces/IERC2981.sol b/contracts/interfaces/IERC2981.sol
new file mode 100644
index 000000000..75e57ac9a
--- /dev/null
+++ b/contracts/interfaces/IERC2981.sol
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
+
+pragma solidity ^0.8.7;
+
+import "./IERC165.sol";
+
+/**
+ * @dev Interface for the NFT Royalty Standard.
+ *
+ * A standardized way to retrieve royalty payment information for
+ * non-fungible tokens (NFTs) to enable universal support for royalty payments
+ * across all NFT marketplaces and ecosystem participants.
+ *
+ * _Available since v4.5._
+ */
+interface IERC2981 is IERC165 {
+ /**
+ * @dev Returns how much royalty is owed and to whom, based on a sale price
+ * that may be denominated in any unit of exchange. The royalty amount
+ * is denominated and should be paid in that same unit of exchange.
+ */
+ function royaltyInfo(
+ uint256 tokenId,
+ uint256 salePrice
+ ) external view returns (address receiver, uint256 royaltyAmount);
+}
diff --git a/contracts/interfaces/SeaportInterface.sol b/contracts/interfaces/SeaportInterface.sol
index 515c38e5d..e510ed0cb 100644
--- a/contracts/interfaces/SeaportInterface.sol
+++ b/contracts/interfaces/SeaportInterface.sol
@@ -15,7 +15,7 @@ import {
/**
* @title SeaportInterface
* @author 0age
- * @custom:version 1.4
+ * @custom:version 1.5
* @notice Seaport is a generalized native token/ERC20/ERC721/ERC1155
* marketplace. It minimizes external calls to the greatest extent
* possible and provides lightweight methods for common routes as well
diff --git a/contracts/interfaces/SeaportRouterInterface.sol b/contracts/interfaces/SeaportRouterInterface.sol
index 108761496..67423ca31 100644
--- a/contracts/interfaces/SeaportRouterInterface.sol
+++ b/contracts/interfaces/SeaportRouterInterface.sol
@@ -17,6 +17,12 @@ import { Execution } from "../lib/ConsiderationStructs.sol";
* all consideration items across all listings are native tokens.
*/
interface SeaportRouterInterface {
+ /**
+ * @dev Revert with an error when attempting to fulfill any number of
+ * available orders when none are fulfillable.
+ */
+ error NoSpecifiedOrdersAvailable();
+
/**
* @dev Advanced order parameters for use through the
* FulfillAvailableAdvancedOrdersParams struct.
diff --git a/contracts/interfaces/ZoneInterface.sol b/contracts/interfaces/ZoneInterface.sol
index d64a5d419..04234ff20 100644
--- a/contracts/interfaces/ZoneInterface.sol
+++ b/contracts/interfaces/ZoneInterface.sol
@@ -3,11 +3,13 @@ pragma solidity ^0.8.13;
import { ZoneParameters, Schema } from "../lib/ConsiderationStructs.sol";
+import { IERC165 } from "../interfaces/IERC165.sol";
+
/**
* @title ZoneInterface
* @notice Contains functions exposed by a zone.
*/
-interface ZoneInterface {
+interface ZoneInterface is IERC165 {
/**
* @dev Validates an order.
*
@@ -34,4 +36,8 @@ interface ZoneInterface {
string memory name,
Schema[] memory schemas // map to Seaport Improvement Proposal IDs
);
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) external view override returns (bool);
}
diff --git a/contracts/interfaces/legacy/ConsiderationInterface1_1.sol b/contracts/interfaces/legacy/ConsiderationInterface1_1.sol
index 1590472bc..8c20c0c56 100644
--- a/contracts/interfaces/legacy/ConsiderationInterface1_1.sol
+++ b/contracts/interfaces/legacy/ConsiderationInterface1_1.sol
@@ -2,15 +2,14 @@
pragma solidity ^0.8.7;
import {
+ AdvancedOrder,
BasicOrderParameters,
- OrderComponents,
+ CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
- Execution,
Order,
- AdvancedOrder,
- OrderStatus,
- CriteriaResolver
+ OrderComponents
} from "../../lib/ConsiderationStructs.sol";
/**
diff --git a/contracts/lib/Consideration.sol b/contracts/lib/Consideration.sol
index d737a1156..51b87f8f4 100644
--- a/contracts/lib/Consideration.sol
+++ b/contracts/lib/Consideration.sol
@@ -42,7 +42,7 @@ import {
* @custom:coauthor d1ll0n (d1ll0n.eth)
* @custom:coauthor transmissions11 (t11s.eth)
* @custom:coauthor James Wenzel (emo.eth)
- * @custom:version 1.4
+ * @custom:version 1.5
* @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
* marketplace that provides lightweight methods for common routes as
* well as more flexible methods for composing advanced orders or groups
diff --git a/contracts/lib/ConsiderationBase.sol b/contracts/lib/ConsiderationBase.sol
index a83a235fd..891f3ae20 100644
--- a/contracts/lib/ConsiderationBase.sol
+++ b/contracts/lib/ConsiderationBase.sol
@@ -43,13 +43,13 @@ import {
NameLengthPtr,
NameWithLength,
OneWord,
- OneWordShift,
Slot0x80,
ThreeWords,
ZeroSlot
} from "./ConsiderationConstants.sol";
import { ConsiderationDecoder } from "./ConsiderationDecoder.sol";
+
import { ConsiderationEncoder } from "./ConsiderationEncoder.sol";
/**
@@ -224,7 +224,7 @@ contract ConsiderationBase is
nameHash = keccak256(bytes(_nameString()));
// Derive hash of the version string of the contract.
- versionHash = keccak256(bytes("1.4"));
+ versionHash = keccak256(bytes("1.5"));
// Construct the OfferItem type string.
bytes memory offerItemTypeString = bytes(
diff --git a/contracts/lib/ConsiderationConstants.sol b/contracts/lib/ConsiderationConstants.sol
index 796b8d1ef..e0b127685 100644
--- a/contracts/lib/ConsiderationConstants.sol
+++ b/contracts/lib/ConsiderationConstants.sol
@@ -46,7 +46,7 @@ uint256 constant information_version_cd_offset = 0x60;
uint256 constant information_domainSeparator_offset = 0x20;
uint256 constant information_conduitController_offset = 0x40;
uint256 constant information_versionLengthPtr = 0x63;
-uint256 constant information_versionWithLength = 0x03312e34; // 1.4
+uint256 constant information_versionWithLength = 0x03312e35; // 1.5
uint256 constant information_length = 0xa0;
uint256 constant _NOT_ENTERED = 1;
diff --git a/contracts/lib/ConsiderationDecoder.sol b/contracts/lib/ConsiderationDecoder.sol
index bca09a333..76ac604c3 100644
--- a/contracts/lib/ConsiderationDecoder.sol
+++ b/contracts/lib/ConsiderationDecoder.sol
@@ -1331,21 +1331,28 @@ contract ConsiderationDecoder {
}
/**
- * @dev Converts an offer item into a received item, applying a given
- * recipient.
+ * @dev Caches the endAmount in an offer item and replaces it with
+ * a given recipient so that its memory may be reused as a temporary
+ * ReceivedItem.
*
* @param offerItem The offer item.
* @param recipient The recipient.
*
- * @return receivedItem The received item.
+ * @return originalEndAmount The original end amount.
*/
- function _fromOfferItemToReceivedItemWithRecipient(
+ function _replaceEndAmountWithRecipient(
OfferItem memory offerItem,
address recipient
- ) internal pure returns (ReceivedItem memory receivedItem) {
+ ) internal pure returns (uint256 originalEndAmount) {
assembly {
- receivedItem := offerItem
- mstore(add(receivedItem, ReceivedItem_recipient_offset), recipient)
+ // Derive the pointer to the end amount on the offer item.
+ let endAmountPtr := add(offerItem, ReceivedItem_recipient_offset)
+
+ // Retrieve the value of the end amount on the offer item.
+ originalEndAmount := mload(endAmountPtr)
+
+ // Write recipient to received item at the offer end amount pointer.
+ mstore(endAmountPtr, recipient)
}
}
}
diff --git a/contracts/lib/Executor.sol b/contracts/lib/Executor.sol
index 68b207e56..ae2803592 100644
--- a/contracts/lib/Executor.sol
+++ b/contracts/lib/Executor.sol
@@ -243,8 +243,8 @@ contract Executor is Verifiers, TokenTransferrer {
* @param token The ERC721 token to transfer.
* @param from The originator of the transfer.
* @param to The recipient of the transfer.
- * @param identifier The tokenId to transfer (must be 1 for ERC721).
- * @param amount The amount to transfer.
+ * @param identifier The tokenId to transfer.
+ * @param amount The amount to transfer (must be 1 for ERC721).
* @param conduitKey A bytes32 value indicating what corresponding conduit,
* if any, to source token approvals from. The zero hash
* signifies that no conduit should be used, with direct
@@ -480,7 +480,8 @@ contract Executor is Verifiers, TokenTransferrer {
_revertInvalidCallToConduit(conduit);
}
- // Ensure result was extracted and matches EIP-1271 magic value.
+ // Ensure result was extracted and matches the Conduit executor magic
+ // value.
if (result != ConduitInterface.execute.selector) {
_revertInvalidConduit(conduitKey, conduit);
}
diff --git a/contracts/lib/OrderCombiner.sol b/contracts/lib/OrderCombiner.sol
index d2e3b1f81..fb26f83aa 100644
--- a/contracts/lib/OrderCombiner.sol
+++ b/contracts/lib/OrderCombiner.sol
@@ -136,7 +136,7 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
Execution[] memory /* executions */
)
{
- // Validate orders, apply amounts, & determine if they utilize conduits.
+ // Validate orders, apply amounts, & determine if they use conduits.
(
bytes32[] memory orderHashes,
bool containsNonOpen
@@ -800,15 +800,33 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
// Note that the transfer will not be reflected in the
// executions array.
if (offerItem.startAmount != 0) {
- _transfer(
- _fromOfferItemToReceivedItemWithRecipient(
+ // Replace the endAmount parameter with the recipient to
+ // make offerItem compatible with the ReceivedItem input
+ // to _transfer and cache the original endAmount so it
+ // can be restored after the transfer.
+ uint256 originalEndAmount = _replaceEndAmountWithRecipient(
offerItem,
recipient
- ),
+ );
+
+ // Transfer excess offer item amount to recipient.
+ _toOfferItemInput(_transfer)(
+ offerItem,
parameters.offerer,
parameters.conduitKey,
accumulator
);
+
+ // Restore the original endAmount in offerItem.
+ assembly {
+ mstore(
+ add(
+ offerItem,
+ ReceivedItem_recipient_offset
+ ),
+ originalEndAmount
+ )
+ }
}
// Restore original amount on the offer item.
diff --git a/contracts/lib/OrderFulfiller.sol b/contracts/lib/OrderFulfiller.sol
index 405508355..50cc91480 100644
--- a/contracts/lib/OrderFulfiller.sol
+++ b/contracts/lib/OrderFulfiller.sol
@@ -348,7 +348,6 @@ contract OrderFulfiller is
)
}
- // Reduce available value if offer spent ETH or a native token.
if (considerationItem.itemType == ItemType.NATIVE) {
// Get the current available balance of native tokens.
assembly {
diff --git a/contracts/lib/OrderValidator.sol b/contracts/lib/OrderValidator.sol
index 22ac17326..4f9664521 100644
--- a/contracts/lib/OrderValidator.sol
+++ b/contracts/lib/OrderValidator.sol
@@ -251,6 +251,7 @@ contract OrderValidator is Executor, ZoneInteraction {
);
}
+ // Utilize assembly to determine the fraction to fill and update status.
assembly {
let orderStatusSlot := orderStatus.slot
// Read filled amount as numerator and denominator and put on stack.
@@ -260,14 +261,14 @@ contract OrderValidator is Executor, ZoneInteraction {
filledNumerator
)
- for {
-
- } 1 {
-
- } {
+ // "Loop" until the appropriate fill fraction has been determined.
+ for { } 1 { } {
+ // If no portion of the order has been filled yet...
if iszero(filledDenominator) {
+ // fill the full supplied fraction.
filledNumerator := numerator
+ // Exit the "loop" early.
break
}
@@ -279,14 +280,20 @@ contract OrderValidator is Executor, ZoneInteraction {
// If denominator of 1 supplied, fill entire remaining amount.
if eq(denominator, 1) {
+ // Set the amount to fill to the remaining amount.
numerator := sub(filledDenominator, filledNumerator)
+
+ // Set the fill size to the current size.
denominator := filledDenominator
+
+ // Set the filled amount to the current size.
filledNumerator := filledDenominator
+ // Exit the "loop" early.
break
}
- // If supplied denominator equals to the current one:
+ // If supplied denominator is equal to the current one:
if eq(denominator, filledDenominator) {
// Increment the filled numerator by the new numerator.
filledNumerator := add(numerator, filledNumerator)
@@ -298,15 +305,21 @@ contract OrderValidator is Executor, ZoneInteraction {
gt(filledNumerator, denominator)
)
+ // reduce the amount to fill by the excess.
numerator := sub(numerator, carry)
+ // Reduce the filled amount by the excess as well.
filledNumerator := sub(filledNumerator, carry)
+ // Exit the "loop" early.
break
}
// Otherwise, if supplied denominator differs from current one:
+ // Scale the filled amount up by the supplied size.
filledNumerator := mul(filledNumerator, denominator)
+
+ // Scale the supplied amount and size up by the current size.
numerator := mul(numerator, filledDenominator)
denominator := mul(denominator, filledDenominator)
@@ -320,8 +333,10 @@ contract OrderValidator is Executor, ZoneInteraction {
gt(filledNumerator, denominator)
)
+ // reduce the amount to fill by the excess.
numerator := sub(numerator, carry)
+ // Reduce the filled amount by the excess as well.
filledNumerator := sub(filledNumerator, carry)
// Check filledNumerator and denominator for uint120 overflow.
@@ -331,17 +346,23 @@ contract OrderValidator is Executor, ZoneInteraction {
) {
// Derive greatest common divisor using euclidean algorithm.
function gcd(_a, _b) -> out {
- for {
-
- } _b {
-
- } {
+ // "Loop" until only one non-zero value remains.
+ for { } _b { } {
+ // Assign the second value to a temporary variable.
let _c := _b
+
+ // Derive the modulus of the two values.
_b := mod(_a, _c)
+
+ // Set the first value to the temporary value.
_a := _c
}
+
+ // Return the remaining non-zero value.
out := _a
}
+
+ // Determine the amount to scale down the fill fractions.
let scaleDown := gcd(
numerator,
gcd(filledNumerator, denominator)
@@ -372,6 +393,7 @@ contract OrderValidator is Executor, ZoneInteraction {
}
}
+ // Exit the "loop" now that all evaluation is complete.
break
}
diff --git a/contracts/test/ERC2981.sol b/contracts/test/ERC2981.sol
new file mode 100644
index 000000000..dc05853f7
--- /dev/null
+++ b/contracts/test/ERC2981.sol
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)
+
+pragma solidity ^0.8.0;
+
+import "../interfaces/IERC2981.sol";
+import "../interfaces/ERC165.sol";
+
+/**
+ * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
+ *
+ * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
+ * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
+ *
+ * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
+ * fee is specified in basis points by default.
+ *
+ * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
+ * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
+ * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
+ *
+ * _Available since v4.5._
+ */
+abstract contract ERC2981 is IERC2981, ERC165 {
+ struct RoyaltyInfo {
+ address receiver;
+ uint96 royaltyFraction;
+ }
+
+ RoyaltyInfo private _defaultRoyaltyInfo;
+ mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
+
+ /**
+ * @dev See {IERC165-supportsInterface}.
+ */
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override(IERC165, ERC165) returns (bool) {
+ return
+ interfaceId == type(IERC2981).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
+ /**
+ * @inheritdoc IERC2981
+ */
+ function royaltyInfo(
+ uint256 _tokenId,
+ uint256 _salePrice
+ ) public view virtual override returns (address, uint256) {
+ RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
+
+ if (royalty.receiver == address(0)) {
+ royalty = _defaultRoyaltyInfo;
+ }
+
+ uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) /
+ _feeDenominator();
+
+ return (royalty.receiver, royaltyAmount);
+ }
+
+ /**
+ * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
+ * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
+ * override.
+ */
+ function _feeDenominator() internal pure virtual returns (uint96) {
+ return 10000;
+ }
+
+ /**
+ * @dev Sets the royalty information that all ids in this contract will default to.
+ *
+ * Requirements:
+ *
+ * - `receiver` cannot be the zero address.
+ * - `feeNumerator` cannot be greater than the fee denominator.
+ */
+ function _setDefaultRoyalty(
+ address receiver,
+ uint96 feeNumerator
+ ) internal virtual {
+ require(
+ feeNumerator <= _feeDenominator(),
+ "ERC2981: royalty fee will exceed salePrice"
+ );
+ require(receiver != address(0), "ERC2981: invalid receiver");
+
+ _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
+ }
+
+ /**
+ * @dev Removes default royalty information.
+ */
+ function _deleteDefaultRoyalty() internal virtual {
+ delete _defaultRoyaltyInfo;
+ }
+
+ /**
+ * @dev Sets the royalty information for a specific token id, overriding the global default.
+ *
+ * Requirements:
+ *
+ * - `receiver` cannot be the zero address.
+ * - `feeNumerator` cannot be greater than the fee denominator.
+ */
+ function _setTokenRoyalty(
+ uint256 tokenId,
+ address receiver,
+ uint96 feeNumerator
+ ) internal virtual {
+ require(
+ feeNumerator <= _feeDenominator(),
+ "ERC2981: royalty fee will exceed salePrice"
+ );
+ require(receiver != address(0), "ERC2981: Invalid parameters");
+
+ _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
+ }
+
+ /**
+ * @dev Resets royalty information for the token id back to the global default.
+ */
+ function _resetTokenRoyalty(uint256 tokenId) internal virtual {
+ delete _tokenRoyaltyInfo[tokenId];
+ }
+}
diff --git a/contracts/test/HashCalldataContractOfferer.sol b/contracts/test/HashCalldataContractOfferer.sol
new file mode 100644
index 000000000..f6b452ec5
--- /dev/null
+++ b/contracts/test/HashCalldataContractOfferer.sol
@@ -0,0 +1,494 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem,
+ ZoneParameters
+} from "../lib/ConsiderationStructs.sol";
+
+import { ItemType, Side } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ConsiderationInterface
+} from "../interfaces/ConsiderationInterface.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+import { OffererZoneFailureReason } from "./OffererZoneFailureReason.sol";
+
+contract HashCalldataContractOfferer is ContractOffererInterface {
+ error HashCalldataContractOffererGenerateOrderReverts();
+ error HashCalldataContractOffererRatifyOrderReverts();
+
+ error NativeTokenTransferFailed();
+
+ event GenerateOrderDataHash(bytes32 orderHash, bytes32 dataHash);
+ event RatifyOrderDataHash(bytes32 orderHash, bytes32 dataHash);
+
+ struct ItemAmountMutation {
+ Side side;
+ uint256 index;
+ uint256 newAmount;
+ bytes32 orderHash;
+ }
+
+ struct DropItemMutation {
+ Side side;
+ uint256 index;
+ bytes32 orderHash;
+ }
+
+ struct ExtraItemMutation {
+ Side side;
+ ReceivedItem item;
+ bytes32 orderHash;
+ }
+
+ ItemAmountMutation[] public itemAmountMutations;
+ DropItemMutation[] public dropItemMutations;
+ ExtraItemMutation[] public extraItemMutations;
+
+ mapping(bytes32 => OffererZoneFailureReason) public failureReasons;
+
+ address private _SEAPORT;
+ address internal _expectedOfferRecipient;
+
+ mapping(bytes32 => bytes32) public orderHashToGenerateOrderDataHash;
+ mapping(bytes32 => bytes32) public orderHashToRatifyOrderDataHash;
+
+ function setFailureReason(
+ bytes32 orderHash,
+ OffererZoneFailureReason newFailureReason
+ ) external {
+ failureReasons[orderHash] = newFailureReason;
+ }
+
+ function addItemAmountMutation(
+ Side side,
+ uint256 index,
+ uint256 newAmount,
+ bytes32 orderHash
+ ) external {
+ // TODO: add safety checks to ensure that item is in range
+ // and that any failure-inducing mutations have the correct
+ // failure reason appropriately set
+
+ itemAmountMutations.push(
+ ItemAmountMutation(side, index, newAmount, orderHash)
+ );
+ }
+
+ function addDropItemMutation(
+ Side side,
+ uint256 index,
+ bytes32 orderHash
+ ) external {
+ // TODO: add safety checks to ensure that item is in range
+ // and that any failure-inducing mutations have the correct
+ // failure reason appropriately set; also should consider
+ // modifying existing indices in other mutations
+
+ dropItemMutations.push(DropItemMutation(side, index, orderHash));
+ }
+
+ function addExtraItemMutation(
+ Side side,
+ ReceivedItem calldata item,
+ bytes32 orderHash
+ ) external {
+ // TODO: add safety checks to ensure that a failure-inducing
+ // mutation has the correct failure reason appropriately set
+
+ extraItemMutations.push(ExtraItemMutation(side, item, orderHash));
+ }
+
+ function applyItemAmountMutation(
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration,
+ ItemAmountMutation memory mutation
+ ) internal pure returns (SpentItem[] memory, ReceivedItem[] memory) {
+ if (mutation.side == Side.OFFER && offer.length > mutation.index) {
+ offer[mutation.index].amount = mutation.newAmount;
+ } else if (consideration.length > mutation.index) {
+ consideration[mutation.index].amount = mutation.newAmount;
+ }
+ return (offer, consideration);
+ }
+
+ function applyDropItemMutation(
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration,
+ DropItemMutation memory mutation
+ )
+ internal
+ pure
+ returns (
+ SpentItem[] memory _offer,
+ ReceivedItem[] memory _consideration
+ )
+ {
+ if (mutation.side == Side.OFFER) {
+ _offer = dropIndex(offer, mutation.index);
+ _consideration = consideration;
+ } else {
+ _offer = offer;
+ _consideration = _cast(
+ dropIndex(_cast(consideration), mutation.index)
+ );
+ }
+ }
+
+ function dropIndex(
+ SpentItem[] memory items,
+ uint256 index
+ ) internal pure returns (SpentItem[] memory newItems) {
+ newItems = new SpentItem[](items.length - 1);
+ uint256 newIndex = 0;
+ uint256 originalLength = items.length;
+ for (uint256 i = 0; i < originalLength; i++) {
+ if (i != index) {
+ newItems[newIndex] = items[i];
+ newIndex++;
+ }
+ }
+ }
+
+ function _cast(
+ ReceivedItem[] memory items
+ ) internal pure returns (SpentItem[] memory _items) {
+ assembly {
+ _items := items
+ }
+ }
+
+ function _cast(
+ SpentItem[] memory items
+ ) internal pure returns (ReceivedItem[] memory _items) {
+ assembly {
+ _items := items
+ }
+ }
+
+ function applyExtraItemMutation(
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration,
+ ExtraItemMutation memory mutation
+ )
+ internal
+ pure
+ returns (
+ SpentItem[] memory _offer,
+ ReceivedItem[] memory _consideration
+ )
+ {
+ if (mutation.side == Side.OFFER) {
+ _offer = _cast(appendItem(_cast(offer), mutation.item));
+ _consideration = consideration;
+ } else {
+ _offer = offer;
+ _consideration = appendItem(consideration, mutation.item);
+ }
+ }
+
+ function appendItem(
+ ReceivedItem[] memory items,
+ ReceivedItem memory item
+ ) internal pure returns (ReceivedItem[] memory newItems) {
+ newItems = new ReceivedItem[](items.length + 1);
+ for (uint256 i = 0; i < items.length; i++) {
+ newItems[i] = items[i];
+ }
+ newItems[items.length] = item;
+ }
+
+ receive() external payable {}
+
+ constructor(address seaport) {
+ _SEAPORT = seaport;
+ }
+
+ /**
+ * @dev Generates an order with the specified minimum and maximum spent
+ * items. Validates data hash set in activate.
+ */
+ function generateOrder(
+ address fulfiller,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata c
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ uint256 contractOffererNonce = ConsiderationInterface(_SEAPORT)
+ .getContractOffererNonce(address(this));
+
+ bytes32 orderHash = bytes32(
+ contractOffererNonce ^ (uint256(uint160(address(this))) << 96)
+ );
+
+ if (
+ failureReasons[orderHash] ==
+ OffererZoneFailureReason.ContractOfferer_generateReverts
+ ) {
+ revert HashCalldataContractOffererGenerateOrderReverts();
+ } else if (
+ failureReasons[orderHash] ==
+ OffererZoneFailureReason
+ .ContractOfferer_generateReturnsInvalidEncoding
+ ) {
+ assembly {
+ mstore(0, 0x12345678)
+ return(0, 0x20)
+ }
+ }
+
+ {
+ // Create a variable to store msg.data in memory
+ bytes memory data = new bytes(msg.data.length);
+
+ // Copy msg.data to memory
+ assembly {
+ calldatacopy(add(data, 0x20), 0, calldatasize())
+ }
+
+ bytes32 calldataHash = keccak256(data);
+
+ // Store the hash of msg.data
+ orderHashToGenerateOrderDataHash[orderHash] = calldataHash;
+
+ emit GenerateOrderDataHash(orderHash, calldataHash);
+ }
+
+ (offer, consideration) = previewOrder(msg.sender, fulfiller, a, b, c);
+
+ (bool success, ) = payable(_SEAPORT).call{
+ value: _getOfferedNativeTokens(offer)
+ }("");
+
+ if (!success) {
+ revert NativeTokenTransferFailed();
+ }
+ }
+
+ /**
+ * @dev View function to preview an order generated in response to a minimum
+ * set of received items, maximum set of spent items, and context
+ * (supplied as extraData).
+ */
+ function previewOrder(
+ address caller,
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata
+ )
+ public
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ require(
+ caller == _SEAPORT,
+ "HashCalldataContractOfferer: caller not seaport"
+ );
+
+ uint256 contractOffererNonce = ConsiderationInterface(_SEAPORT)
+ .getContractOffererNonce(address(this));
+
+ bytes32 orderHash = bytes32(
+ contractOffererNonce ^ (uint256(uint160(address(this))) << 96)
+ );
+
+ (offer, consideration) = (a, _convertSpentToReceived(b));
+
+ for (uint256 i; i < itemAmountMutations.length; i++) {
+ if (itemAmountMutations[i].orderHash == orderHash) {
+ (offer, consideration) = applyItemAmountMutation(
+ offer,
+ consideration,
+ itemAmountMutations[i]
+ );
+ }
+ }
+ for (uint256 i; i < extraItemMutations.length; i++) {
+ if (extraItemMutations[i].orderHash == orderHash) {
+ (offer, consideration) = applyExtraItemMutation(
+ offer,
+ consideration,
+ extraItemMutations[i]
+ );
+ }
+ }
+ for (uint256 i; i < dropItemMutations.length; i++) {
+ if (dropItemMutations[i].orderHash == orderHash) {
+ (offer, consideration) = applyDropItemMutation(
+ offer,
+ consideration,
+ dropItemMutations[i]
+ );
+ }
+ }
+
+ return (offer, consideration);
+ }
+
+ /**
+ * @dev Ratifies that the parties have received the correct items.
+ *
+ * @custom:param minimumReceived The minimum items that the caller was
+ * willing to receive.
+ * @custom:param maximumSpent The maximum items that the caller was
+ * willing to spend.
+ * @custom:param context The context of the order.
+ * @custom:param orderHashes The order hashes, unused here.
+ * @custom:param contractNonce The contract nonce, unused here.
+ *
+ * @return ratifyOrderMagicValue The magic value to indicate things are OK.
+ */
+ function ratifyOrder(
+ SpentItem[] calldata /* minimumReceived */,
+ ReceivedItem[] calldata /* maximumSpent */,
+ bytes calldata /* context */,
+ bytes32[] calldata /* orderHashes */,
+ uint256 contractNonce
+ ) external override returns (bytes4 /* ratifyOrderMagicValue */) {
+ require(
+ msg.sender == _SEAPORT,
+ "HashCalldataContractOfferer: ratify caller not seaport"
+ );
+
+ bytes32 orderHash = bytes32(
+ contractNonce ^ (uint256(uint160(address(this))) << 96)
+ );
+
+ if (
+ failureReasons[orderHash] ==
+ OffererZoneFailureReason.ContractOfferer_ratifyReverts
+ ) {
+ revert HashCalldataContractOffererRatifyOrderReverts();
+ }
+
+ // Ratify the order.
+ {
+ // Create a variable to store msg.data in memory
+ bytes memory data = new bytes(msg.data.length);
+
+ // Copy msg.data to memory
+ assembly {
+ calldatacopy(add(data, 0x20), 0, calldatasize())
+ }
+
+ bytes32 calldataHash = keccak256(data);
+
+ // Store the hash of msg.data
+ orderHashToRatifyOrderDataHash[orderHash] = calldataHash;
+
+ emit RatifyOrderDataHash(orderHash, calldataHash);
+ }
+
+ if (
+ failureReasons[orderHash] ==
+ OffererZoneFailureReason.ContractOfferer_InvalidMagicValue
+ ) {
+ return bytes4(0x12345678);
+ } else {
+ // Return the selector of ratifyOrder as the magic value.
+ return this.ratifyOrder.selector;
+ }
+ }
+
+ /**
+ * @dev Allows us to set Seaport address following deployment.
+ *
+ * @param seaportAddress The Seaport address.
+ */
+ function setSeaportAddress(address seaportAddress) external {
+ _SEAPORT = seaportAddress;
+ }
+
+ function getSeaportMetadata()
+ external
+ pure
+ override(ContractOffererInterface)
+ returns (string memory name, Schema[] memory schemas)
+ {
+ // Return the metadata.
+ name = "TestCalldataHashContractOfferer";
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+ }
+
+ function _convertSpentToReceived(
+ SpentItem[] calldata spentItems
+ ) internal view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ spentItems.length
+ );
+ for (uint256 i = 0; i < spentItems.length; ++i) {
+ receivedItems[i] = _convertSpentToReceived(spentItems[i]);
+ }
+ return receivedItems;
+ }
+
+ function _convertSpentToReceived(
+ SpentItem calldata spentItem
+ ) internal view returns (ReceivedItem memory) {
+ return
+ ReceivedItem({
+ itemType: spentItem.itemType,
+ token: spentItem.token,
+ identifier: spentItem.identifier,
+ amount: spentItem.amount,
+ recipient: payable(address(this))
+ });
+ }
+
+ function _getOfferedNativeTokens(
+ SpentItem[] memory offer
+ ) internal pure returns (uint256 amount) {
+ for (uint256 i = 0; i < offer.length; ++i) {
+ SpentItem memory item = offer[i];
+ if (item.itemType == ItemType.NATIVE) {
+ amount += item.amount;
+ }
+ }
+ }
+
+ /**
+ * @dev Enable accepting ERC1155 tokens via safeTransfer.
+ */
+ function onERC1155Received(
+ address,
+ address,
+ uint256,
+ uint256,
+ bytes calldata
+ ) external pure returns (bytes4) {
+ return this.onERC1155Received.selector;
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override(ContractOffererInterface) returns (bool) {
+ return interfaceId == type(ContractOffererInterface).interfaceId;
+ }
+
+ function setExpectedOfferRecipient(address expectedOfferRecipient) public {
+ _expectedOfferRecipient = expectedOfferRecipient;
+ }
+}
diff --git a/contracts/test/HashValidationZoneOfferer.sol b/contracts/test/HashValidationZoneOfferer.sol
new file mode 100644
index 000000000..7c250ecfc
--- /dev/null
+++ b/contracts/test/HashValidationZoneOfferer.sol
@@ -0,0 +1,641 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem,
+ ZoneParameters
+} from "../lib/ConsiderationStructs.sol";
+
+import { ItemType, Side } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+import { OffererZoneFailureReason } from "./OffererZoneFailureReason.sol";
+
+/**
+ * @dev This contract is used to validate hashes. Use the
+ * TestTransferValidationZoneOfferer to validate transfers within the
+ * zone/offerer.
+ */
+contract HashValidationZoneOfferer is ContractOffererInterface, ZoneInterface {
+ error InvalidNativeTokenBalance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress
+ );
+ error InvalidERC20Balance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress,
+ address checkedToken
+ );
+ error InvalidERC1155Balance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress,
+ address checkedToken
+ );
+ // 0x38fb386a
+ error InvalidOwner(
+ address expectedOwner,
+ address actualOwner,
+ address checkedToken,
+ uint256 checkedTokenId
+ );
+ error IncorrectSeaportBalance(
+ uint256 expectedBalance,
+ uint256 actualBalance
+ );
+ error HashValidationZoneOffererValidateOrderReverts();
+ error HashValidationZoneOffererRatifyOrderReverts();
+ event ValidateOrderDataHash(bytes32 dataHash);
+
+ struct ItemAmountMutation {
+ Side side;
+ uint256 index;
+ uint256 newAmount;
+ }
+
+ struct DropItemMutation {
+ Side side;
+ uint256 index;
+ }
+
+ struct ExtraItemMutation {
+ Side side;
+ ReceivedItem item;
+ }
+
+ ItemAmountMutation[] public itemAmountMutations;
+ DropItemMutation[] public dropItemMutations;
+ ExtraItemMutation[] public extraItemMutations;
+
+ function addItemAmountMutation(
+ Side side,
+ uint256 index,
+ uint256 newAmount
+ ) external {
+ itemAmountMutations.push(ItemAmountMutation(side, index, newAmount));
+ }
+
+ function addDropItemMutation(Side side, uint256 index) external {
+ dropItemMutations.push(DropItemMutation(side, index));
+ }
+
+ function addExtraItemMutation(
+ Side side,
+ ReceivedItem calldata item
+ ) external {
+ extraItemMutations.push(ExtraItemMutation(side, item));
+ }
+
+ function applyItemAmountMutation(
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration,
+ ItemAmountMutation memory mutation
+ ) internal pure returns (SpentItem[] memory, ReceivedItem[] memory) {
+ if (mutation.side == Side.OFFER) {
+ offer[mutation.index].amount = mutation.newAmount;
+ } else {
+ consideration[mutation.index].amount = mutation.newAmount;
+ }
+ return (offer, consideration);
+ }
+
+ function applyDropItemMutation(
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration,
+ DropItemMutation memory mutation
+ )
+ internal
+ pure
+ returns (
+ SpentItem[] memory _offer,
+ ReceivedItem[] memory _consideration
+ )
+ {
+ if (mutation.side == Side.OFFER) {
+ _offer = dropIndex(offer, mutation.index);
+ _consideration = consideration;
+ } else {
+ _offer = offer;
+ _consideration = _cast(
+ dropIndex(_cast(consideration), mutation.index)
+ );
+ }
+ }
+
+ function dropIndex(
+ SpentItem[] memory items,
+ uint256 index
+ ) internal pure returns (SpentItem[] memory newItems) {
+ newItems = new SpentItem[](items.length - 1);
+ uint256 newIndex = 0;
+ uint256 originalLength = items.length;
+ for (uint256 i = 0; i < originalLength; i++) {
+ if (i != index) {
+ newItems[newIndex] = items[i];
+ newIndex++;
+ }
+ }
+ }
+
+ function _cast(
+ ReceivedItem[] memory items
+ ) internal pure returns (SpentItem[] memory _items) {
+ assembly {
+ _items := items
+ }
+ }
+
+ function _cast(
+ SpentItem[] memory items
+ ) internal pure returns (ReceivedItem[] memory _items) {
+ assembly {
+ _items := items
+ }
+ }
+
+ function applyExtraItemMutation(
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration,
+ ExtraItemMutation memory mutation
+ )
+ internal
+ pure
+ returns (
+ SpentItem[] memory _offer,
+ ReceivedItem[] memory _consideration
+ )
+ {
+ if (mutation.side == Side.OFFER) {
+ _offer = _cast(appendItem(_cast(offer), mutation.item));
+ _consideration = consideration;
+ } else {
+ _offer = offer;
+ _consideration = appendItem(consideration, mutation.item);
+ }
+ }
+
+ function appendItem(
+ ReceivedItem[] memory items,
+ ReceivedItem memory item
+ ) internal pure returns (ReceivedItem[] memory newItems) {
+ newItems = new ReceivedItem[](items.length + 1);
+ for (uint256 i = 0; i < items.length; i++) {
+ newItems[i] = items[i];
+ }
+ newItems[items.length] = item;
+ }
+
+ receive() external payable {}
+
+ address internal _expectedOfferRecipient;
+
+ mapping(bytes32 => bytes32) public orderHashToValidateOrderDataHash;
+
+ // Pass in the null address to expect the fulfiller.
+ constructor(address expectedOfferRecipient) {
+ _expectedOfferRecipient = expectedOfferRecipient;
+ }
+
+ bool public called = false;
+ uint public callCount = 0;
+
+ mapping(bytes32 => OffererZoneFailureReason) public failureReasons;
+
+ function setFailureReason(
+ bytes32 orderHash,
+ OffererZoneFailureReason newFailureReason
+ ) external {
+ failureReasons[orderHash] = newFailureReason;
+ }
+
+ /**
+ * @dev Validates that the parties have received the correct items.
+ *
+ * @param zoneParameters The zone parameters, including the SpentItem and
+ * ReceivedItem arrays.
+ *
+ * @return validOrderMagicValue The magic value to indicate things are OK.
+ */
+ function validateOrder(
+ ZoneParameters calldata zoneParameters
+ ) external override returns (bytes4 validOrderMagicValue) {
+ // Get the orderHash from zoneParameters
+ bytes32 orderHash = zoneParameters.orderHash;
+
+ if (
+ failureReasons[orderHash] == OffererZoneFailureReason.Zone_reverts
+ ) {
+ revert HashValidationZoneOffererValidateOrderReverts();
+ }
+ // Validate the order.
+
+ // Currently assumes that the balances of all tokens of addresses are
+ // zero at the start of the transaction. Accordingly, take care to
+ // use an address in tests that is not pre-populated with tokens.
+
+ // Get the length of msg.data
+ uint256 dataLength = msg.data.length;
+
+ // Create a variable to store msg.data in memory
+ bytes memory data;
+
+ // Copy msg.data to memory
+ assembly {
+ let ptr := mload(0x40)
+ calldatacopy(add(ptr, 0x20), 0, dataLength)
+ mstore(ptr, dataLength)
+ data := ptr
+ }
+
+ // Get the hash of msg.data
+ bytes32 calldataHash = keccak256(data);
+
+ // Store callDataHash in orderHashToValidateOrderDataHash
+ orderHashToValidateOrderDataHash[orderHash] = calldataHash;
+
+ // Emit a DataHash event with the hash of msg.data
+ emit ValidateOrderDataHash(calldataHash);
+
+ // Check if Seaport is empty. This makes sure that we've transferred
+ // all native token balance out of Seaport before we do the validation.
+ uint256 seaportBalance = address(msg.sender).balance;
+
+ if (seaportBalance > 0) {
+ revert IncorrectSeaportBalance(0, seaportBalance);
+ }
+
+ // Set the global called flag to true.
+ called = true;
+ callCount++;
+
+ if (
+ failureReasons[orderHash] ==
+ OffererZoneFailureReason.Zone_InvalidMagicValue
+ ) {
+ validOrderMagicValue = bytes4(0x12345678);
+ } else {
+ // Return the selector of validateOrder as the magic value.
+ validOrderMagicValue = this.validateOrder.selector;
+ }
+ }
+
+ /**
+ * @dev Generates an order with the specified minimum and maximum spent
+ * items.
+ */
+ function generateOrder(
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata c
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ (offer, consideration) = previewOrder(
+ address(this),
+ address(this),
+ a,
+ b,
+ c
+ );
+
+ for (uint256 i; i < itemAmountMutations.length; i++) {
+ (offer, consideration) = applyItemAmountMutation(
+ offer,
+ consideration,
+ itemAmountMutations[i]
+ );
+ }
+ for (uint256 i; i < extraItemMutations.length; i++) {
+ (offer, consideration) = applyExtraItemMutation(
+ offer,
+ consideration,
+ extraItemMutations[i]
+ );
+ }
+ for (uint256 i; i < dropItemMutations.length; i++) {
+ (offer, consideration) = applyDropItemMutation(
+ offer,
+ consideration,
+ dropItemMutations[i]
+ );
+ }
+
+ return (offer, consideration);
+ }
+
+ /**
+ * @dev View function to preview an order generated in response to a minimum
+ * set of received items, maximum set of spent items, and context
+ * (supplied as extraData).
+ */
+ function previewOrder(
+ address,
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata
+ )
+ public
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ return (a, _convertSpentToReceived(b));
+ }
+
+ /**
+ * @dev Ratifies that the parties have received the correct items.
+ *
+ * @param minimumReceived The minimum items that the caller was willing to
+ * receive.
+ * @param maximumSpent The maximum items that the caller was willing to
+ * spend.
+ * @param context The context of the order.
+ * @ param orderHashes The order hashes, unused here.
+ * @ param contractNonce The contract nonce, unused here.
+ *
+ * @return ratifyOrderMagicValue The magic value to indicate things are OK.
+ */
+ function ratifyOrder(
+ SpentItem[] calldata minimumReceived /* offer */,
+ ReceivedItem[] calldata maximumSpent /* consideration */,
+ bytes calldata context /* context */,
+ bytes32[] calldata /* orderHashes */,
+ uint256 /* contractNonce */
+ ) external override returns (bytes4 /* ratifyOrderMagicValue */) {
+ // Ratify the order.
+ // Check if Seaport is empty. This makes sure that we've transferred
+ // all native token balance out of Seaport before we do the validation.
+ uint256 seaportBalance = address(msg.sender).balance;
+
+ if (seaportBalance > 0) {
+ revert IncorrectSeaportBalance(0, seaportBalance);
+ }
+
+ // Ensure that the offerer or recipient has received all consideration
+ // items.
+ _assertValidReceivedItems(maximumSpent);
+
+ // It's necessary to pass in either an expected offerer or an address
+ // in the context. If neither is provided, this ternary will revert
+ // with a generic, hard-to-debug revert when it tries to slice bytes
+ // from the context.
+ address expectedOfferRecipient = _expectedOfferRecipient == address(0)
+ ? address(bytes20(context[0:20]))
+ : _expectedOfferRecipient;
+
+ // Ensure that the expected recipient has received all offer items.
+ _assertValidSpentItems(expectedOfferRecipient, minimumReceived);
+
+ // Set the global called flag to true.
+ called = true;
+ callCount++;
+
+ return this.ratifyOrder.selector;
+ }
+
+ function getSeaportMetadata()
+ external
+ pure
+ override(ContractOffererInterface, ZoneInterface)
+ returns (string memory name, Schema[] memory schemas)
+ {
+ // Return the metadata.
+ name = "TestTransferValidationZoneOfferer";
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+ }
+
+ function _convertSpentToReceived(
+ SpentItem[] calldata spentItems
+ ) internal view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ spentItems.length
+ );
+ for (uint256 i = 0; i < spentItems.length; ++i) {
+ receivedItems[i] = _convertSpentToReceived(spentItems[i]);
+ }
+ return receivedItems;
+ }
+
+ function _convertSpentToReceived(
+ SpentItem calldata spentItem
+ ) internal view returns (ReceivedItem memory) {
+ return
+ ReceivedItem({
+ itemType: spentItem.itemType,
+ token: spentItem.token,
+ identifier: spentItem.identifier,
+ amount: spentItem.amount,
+ recipient: payable(address(this))
+ });
+ }
+
+ function _assertValidReceivedItems(
+ ReceivedItem[] calldata receivedItems
+ ) internal view {
+ address recipient;
+ ItemType itemType;
+ ReceivedItem memory receivedItem;
+
+ // Iterate over all received items.
+ for (uint256 i = 0; i < receivedItems.length; i++) {
+ // Check if the consideration item has been received.
+ receivedItem = receivedItems[i];
+ // Get the recipient of the consideration item.
+ recipient = receivedItem.recipient;
+ // Get item type.
+ itemType = receivedItem.itemType;
+
+ // Check balance/ownerOf depending on item type.
+ if (itemType == ItemType.NATIVE) {
+ // NATIVE Token
+ _assertNativeTokenTransfer(receivedItem.amount, recipient);
+ } else if (itemType == ItemType.ERC20) {
+ // ERC20 Token
+ _assertERC20Transfer(
+ receivedItem.amount,
+ receivedItem.token,
+ recipient
+ );
+ } else if (itemType == ItemType.ERC721) {
+ // ERC721 Token
+ _assertERC721Transfer(
+ receivedItem.identifier,
+ receivedItem.token,
+ recipient
+ );
+ } else if (itemType == ItemType.ERC1155) {
+ // ERC1155 Token
+ _assertERC1155Transfer(
+ receivedItem.amount,
+ receivedItem.identifier,
+ receivedItem.token,
+ recipient
+ );
+ }
+ }
+ }
+
+ function _assertValidSpentItems(
+ address expectedRecipient,
+ SpentItem[] calldata spentItems
+ ) internal view {
+ SpentItem memory spentItem;
+ ItemType itemType;
+
+ // Iterate over all spent items.
+ for (uint256 i = 0; i < spentItems.length; i++) {
+ // Check if the offer item has been spent.
+ spentItem = spentItems[i];
+ // Get item type.
+ itemType = spentItem.itemType;
+
+ // Check balance/ownerOf depending on item type.
+ if (itemType == ItemType.NATIVE) {
+ // NATIVE Token
+ _assertNativeTokenTransfer(spentItem.amount, expectedRecipient);
+ } else if (itemType == ItemType.ERC20) {
+ // ERC20 Token
+ _assertERC20Transfer(
+ spentItem.amount,
+ spentItem.token,
+ expectedRecipient
+ );
+ } else if (itemType == ItemType.ERC721) {
+ // ERC721 Token
+ _assertERC721Transfer(
+ spentItem.identifier,
+ spentItem.token,
+ expectedRecipient
+ );
+ } else if (itemType == ItemType.ERC1155) {
+ // ERC1155 Token
+ _assertERC1155Transfer(
+ spentItem.amount,
+ spentItem.identifier,
+ spentItem.token,
+ expectedRecipient
+ );
+ }
+ }
+ }
+
+ function _assertNativeTokenTransfer(
+ uint256 expectedAmount,
+ address expectedRecipient
+ ) internal view {
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient then revert, because that means the recipient did not
+ // receive the expected amount at the time the order was ratified or
+ // validated.
+ if (expectedAmount > address(expectedRecipient).balance) {
+ revert InvalidNativeTokenBalance(
+ expectedAmount,
+ address(expectedRecipient).balance,
+ expectedRecipient
+ );
+ }
+ }
+
+ function _assertERC20Transfer(
+ uint256 expectedAmount,
+ address token,
+ address expectedRecipient
+ ) internal view {
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient, revert.
+ if (
+ expectedAmount > ERC20Interface(token).balanceOf(expectedRecipient)
+ ) {
+ revert InvalidERC20Balance(
+ expectedAmount,
+ ERC20Interface(token).balanceOf(expectedRecipient),
+ expectedRecipient,
+ token
+ );
+ }
+ }
+
+ function _assertERC721Transfer(
+ uint256 checkedTokenId,
+ address token,
+ address expectedRecipient
+ ) internal view {
+ // If the actual owner of the token is not the expected recipient,
+ // revert.
+ address actualOwner = ERC721Interface(token).ownerOf(checkedTokenId);
+ if (expectedRecipient != actualOwner) {
+ revert InvalidOwner(
+ expectedRecipient,
+ actualOwner,
+ token,
+ checkedTokenId
+ );
+ }
+ }
+
+ function _assertERC1155Transfer(
+ uint256 expectedAmount,
+ uint256 identifier,
+ address token,
+ address expectedRecipient
+ ) internal view {
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient, revert.
+ if (
+ expectedAmount >
+ ERC1155Interface(token).balanceOf(expectedRecipient, identifier)
+ ) {
+ revert InvalidERC1155Balance(
+ expectedAmount,
+ ERC1155Interface(token).balanceOf(
+ expectedRecipient,
+ identifier
+ ),
+ expectedRecipient,
+ token
+ );
+ }
+ }
+
+ function setExpectedOfferRecipient(address expectedOfferRecipient) public {
+ _expectedOfferRecipient = expectedOfferRecipient;
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ContractOffererInterface, ZoneInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ interfaceId == type(ZoneInterface).interfaceId;
+ }
+}
diff --git a/contracts/test/OffererZoneFailureReason.sol b/contracts/test/OffererZoneFailureReason.sol
new file mode 100644
index 000000000..c4ce07d7c
--- /dev/null
+++ b/contracts/test/OffererZoneFailureReason.sol
@@ -0,0 +1,16 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+enum OffererZoneFailureReason {
+ None,
+ ContractOfferer_generateReverts, // Offerer generateOrder reverts
+ ContractOfferer_generateReturnsInvalidEncoding, // Bad encoding
+ ContractOfferer_ratifyReverts, // Offerer ratifyOrder reverts
+ ContractOfferer_InsufficientMinimumReceived, // too few minimum received items
+ ContractOfferer_IncorrectMinimumReceived, // incorrect (insufficient amount, wrong token, etc.) minimum received items
+ ContractOfferer_ExcessMaximumSpent, // too many maximum spent items
+ ContractOfferer_IncorrectMaximumSpent, // incorrect (too many, wrong token, etc.) maximum spent items
+ ContractOfferer_InvalidMagicValue, // Offerer did not return correct magic value
+ Zone_reverts, // Zone validateOrder call reverts
+ Zone_InvalidMagicValue // Zone validateOrder call returns invalid magic value
+}
diff --git a/contracts/test/TestBadContractOfferer.sol b/contracts/test/TestBadContractOfferer.sol
index 8b2272eae..07c7bf1fd 100644
--- a/contracts/test/TestBadContractOfferer.sol
+++ b/contracts/test/TestBadContractOfferer.sol
@@ -122,6 +122,12 @@ contract TestBadContractOfferer is ContractOffererInterface {
return TestBadContractOfferer.ratifyOrder.selector;
}
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override(ContractOffererInterface) returns (bool) {
+ return interfaceId == type(ContractOffererInterface).interfaceId;
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/contracts/test/TestCalldataHashContractOfferer.sol b/contracts/test/TestCalldataHashContractOfferer.sol
new file mode 100644
index 000000000..679da7396
--- /dev/null
+++ b/contracts/test/TestCalldataHashContractOfferer.sol
@@ -0,0 +1,495 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem,
+ ZoneParameters
+} from "../lib/ConsiderationStructs.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ConsiderationInterface
+} from "../interfaces/ConsiderationInterface.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+contract TestCalldataHashContractOfferer is ContractOffererInterface {
+ error InvalidNativeTokenBalance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress
+ );
+ error InvalidERC20Balance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress,
+ address checkedToken
+ );
+ error InvalidERC1155Balance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress,
+ address checkedToken
+ );
+ // 0x38fb386a
+ error InvalidOwner(
+ address expectedOwner,
+ address actualOwner,
+ address checkedToken,
+ uint256 checkedTokenId
+ );
+ error IncorrectSeaportBalance(
+ uint256 expectedBalance,
+ uint256 actualBalance
+ );
+ error InvalidDataHash(bytes32 expectedDataHash, bytes32 actualDataHash);
+ error InvalidEthBalance(uint256 expectedBalance, uint256 actualBalance);
+ error NativeTokenTransferFailed();
+
+ event GenerateOrderDataHash(bytes32 orderHash, bytes32 dataHash);
+ event RatifyOrderDataHash(bytes32 orderHash, bytes32 dataHash);
+
+ address private immutable _SEAPORT;
+ address internal _expectedOfferRecipient;
+
+ mapping(bytes32 => bytes32) public orderHashToGenerateOrderDataHash;
+ mapping(bytes32 => bytes32) public orderHashToRatifyOrderDataHash;
+
+ receive() external payable {}
+
+ constructor(address seaport) {
+ _SEAPORT = seaport;
+ }
+
+ /**
+ * @dev Sets approvals and transfers minimumReceived tokens to contract.
+ * Also stores the expected hash of msg.data to be sent in subsequent
+ * call to generateOrder.
+ */
+ function activate(
+ address,
+ SpentItem[] memory minimumReceived,
+ SpentItem[] memory /* maximumSpent */,
+ bytes calldata /* context */
+ ) public payable {
+ uint256 requiredEthBalance;
+ uint256 minimumReceivedLength = minimumReceived.length;
+
+ for (uint256 i = 0; i < minimumReceivedLength; i++) {
+ SpentItem memory item = minimumReceived[i];
+
+ if (item.itemType == ItemType.ERC721) {
+ ERC721Interface token = ERC721Interface(item.token);
+
+ token.transferFrom(msg.sender, address(this), item.identifier);
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (item.itemType == ItemType.ERC1155) {
+ ERC1155Interface token = ERC1155Interface(item.token);
+
+ token.safeTransferFrom(
+ msg.sender,
+ address(this),
+ item.identifier,
+ item.amount,
+ ""
+ );
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (item.itemType == ItemType.ERC20) {
+ ERC20Interface token = ERC20Interface(item.token);
+
+ token.transferFrom(msg.sender, address(this), item.amount);
+
+ token.approve(_SEAPORT, item.amount);
+ } else if (item.itemType == ItemType.NATIVE) {
+ requiredEthBalance += item.amount;
+ }
+ }
+
+ if (msg.value != requiredEthBalance) {
+ revert InvalidEthBalance(requiredEthBalance, msg.value);
+ }
+ }
+
+ /**
+ * @dev Generates an order with the specified minimum and maximum spent
+ * items. Validates data hash set in activate.
+ */
+ function generateOrder(
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata c
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ {
+ (bool success, ) = payable(_SEAPORT).call{
+ value: address(this).balance
+ }("");
+
+ if (!success) {
+ revert NativeTokenTransferFailed();
+ }
+
+ // Get the length of msg.data
+ uint256 dataLength = msg.data.length;
+
+ // Create a variable to store msg.data in memory
+ bytes memory data;
+
+ // Copy msg.data to memory
+ assembly {
+ let ptr := mload(0x40)
+ calldatacopy(add(ptr, 0x20), 0, dataLength)
+ mstore(ptr, dataLength)
+ data := ptr
+ }
+
+ bytes32 calldataHash = keccak256(data);
+
+ uint256 contractOffererNonce = ConsiderationInterface(_SEAPORT)
+ .getContractOffererNonce(address(this));
+
+ bytes32 orderHash = bytes32(
+ contractOffererNonce ^ (uint256(uint160(address(this))) << 96)
+ );
+
+ // Store the hash of msg.data
+ orderHashToGenerateOrderDataHash[orderHash] = calldataHash;
+
+ emit GenerateOrderDataHash(orderHash, calldataHash);
+ }
+
+ return previewOrder(address(this), address(this), a, b, c);
+ }
+
+ /**
+ * @dev View function to preview an order generated in response to a minimum
+ * set of received items, maximum set of spent items, and context
+ * (supplied as extraData).
+ */
+ function previewOrder(
+ address,
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata
+ )
+ public
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ return (a, _convertSpentToReceived(b));
+ }
+
+ /**
+ * @dev Ratifies that the parties have received the correct items.
+ *
+ * @param minimumReceived The minimum items that the caller was willing to
+ * receive.
+ * @param maximumSpent The maximum items that the caller was willing to
+ * spend.
+ * @param context The context of the order.
+ * @ param orderHashes The order hashes, unused here.
+ * @ param contractNonce The contract nonce, unused here.
+ *
+ * @return ratifyOrderMagicValue The magic value to indicate things are OK.
+ */
+ function ratifyOrder(
+ SpentItem[] calldata minimumReceived /* offer */,
+ ReceivedItem[] calldata maximumSpent /* consideration */,
+ bytes calldata context /* context */,
+ bytes32[] calldata /* orderHashes */,
+ uint256 /* contractNonce */
+ ) external override returns (bytes4 /* ratifyOrderMagicValue */) {
+ // Ratify the order.
+ {
+ // Get the length of msg.data
+ uint256 dataLength = msg.data.length;
+
+ // Create a variable to store msg.data in memory
+ bytes memory data;
+
+ // Copy msg.data to memory
+ assembly {
+ let ptr := mload(0x40)
+ calldatacopy(add(ptr, 0x20), 0, dataLength)
+ mstore(ptr, dataLength)
+ data := ptr
+ }
+
+ bytes32 calldataHash = keccak256(data);
+
+ uint256 contractOffererNonce = ConsiderationInterface(_SEAPORT)
+ .getContractOffererNonce(address(this));
+
+ bytes32 orderHash = bytes32(
+ contractOffererNonce ^ (uint256(uint160(address(this))) << 96)
+ );
+
+ // Store the hash of msg.data
+ orderHashToRatifyOrderDataHash[orderHash] = calldataHash;
+
+ emit RatifyOrderDataHash(orderHash, calldataHash);
+ // Check if Seaport is empty. This makes sure that we've transferred
+ // all native token balance out of Seaport before we do the validation.
+ uint256 seaportBalance = address(msg.sender).balance;
+
+ if (seaportBalance > 0) {
+ revert IncorrectSeaportBalance(0, seaportBalance);
+ }
+ // Ensure that the offerer or recipient has received all consideration
+ // items.
+ _assertValidReceivedItems(maximumSpent);
+ }
+
+ // It's necessary to pass in either an expected offerer or an address
+ // in the context. If neither is provided, this ternary will revert
+ // with a generic, hard-to-debug revert when it tries to slice bytes
+ // from the context.
+ address expectedOfferRecipient = _expectedOfferRecipient == address(0)
+ ? address(bytes20(context[0:20]))
+ : _expectedOfferRecipient;
+
+ // Ensure that the expected recipient has received all offer items.
+ _assertValidSpentItems(expectedOfferRecipient, minimumReceived);
+
+ return this.ratifyOrder.selector;
+ }
+
+ function getSeaportMetadata()
+ external
+ pure
+ override(ContractOffererInterface)
+ returns (string memory name, Schema[] memory schemas)
+ {
+ // Return the metadata.
+ name = "TestCalldataHashContractOfferer";
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+ }
+
+ function _convertSpentToReceived(
+ SpentItem[] calldata spentItems
+ ) internal view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ spentItems.length
+ );
+ for (uint256 i = 0; i < spentItems.length; ++i) {
+ receivedItems[i] = _convertSpentToReceived(spentItems[i]);
+ }
+ return receivedItems;
+ }
+
+ function _convertSpentToReceived(
+ SpentItem calldata spentItem
+ ) internal view returns (ReceivedItem memory) {
+ return
+ ReceivedItem({
+ itemType: spentItem.itemType,
+ token: spentItem.token,
+ identifier: spentItem.identifier,
+ amount: spentItem.amount,
+ recipient: payable(address(this))
+ });
+ }
+
+ function _assertValidReceivedItems(
+ ReceivedItem[] calldata receivedItems
+ ) internal view {
+ address recipient;
+ ItemType itemType;
+ ReceivedItem memory receivedItem;
+
+ // Iterate over all received items.
+ for (uint256 i = 0; i < receivedItems.length; i++) {
+ // Check if the consideration item has been received.
+ receivedItem = receivedItems[i];
+ // Get the recipient of the consideration item.
+ recipient = receivedItem.recipient;
+ // Get item type.
+ itemType = receivedItem.itemType;
+
+ // Check balance/ownerOf depending on item type.
+ if (itemType == ItemType.NATIVE) {
+ // NATIVE Token
+ _assertNativeTokenTransfer(receivedItem.amount, recipient);
+ } else if (itemType == ItemType.ERC20) {
+ // ERC20 Token
+ _assertERC20Transfer(
+ receivedItem.amount,
+ receivedItem.token,
+ recipient
+ );
+ } else if (itemType == ItemType.ERC721) {
+ // ERC721 Token
+ _assertERC721Transfer(
+ receivedItem.identifier,
+ receivedItem.token,
+ recipient
+ );
+ } else if (itemType == ItemType.ERC1155) {
+ // ERC1155 Token
+ _assertERC1155Transfer(
+ receivedItem.amount,
+ receivedItem.identifier,
+ receivedItem.token,
+ recipient
+ );
+ }
+ }
+ }
+
+ function _assertValidSpentItems(
+ address expectedRecipient,
+ SpentItem[] calldata spentItems
+ ) internal view {
+ SpentItem memory spentItem;
+ ItemType itemType;
+
+ // Iterate over all spent items.
+ for (uint256 i = 0; i < spentItems.length; i++) {
+ // Check if the offer item has been spent.
+ spentItem = spentItems[i];
+ // Get item type.
+ itemType = spentItem.itemType;
+
+ // Check balance/ownerOf depending on item type.
+ if (itemType == ItemType.NATIVE) {
+ // NATIVE Token
+ _assertNativeTokenTransfer(spentItem.amount, expectedRecipient);
+ } else if (itemType == ItemType.ERC20) {
+ // ERC20 Token
+ _assertERC20Transfer(
+ spentItem.amount,
+ spentItem.token,
+ expectedRecipient
+ );
+ } else if (itemType == ItemType.ERC721) {
+ // ERC721 Token
+ _assertERC721Transfer(
+ spentItem.identifier,
+ spentItem.token,
+ expectedRecipient
+ );
+ } else if (itemType == ItemType.ERC1155) {
+ // ERC1155 Token
+ _assertERC1155Transfer(
+ spentItem.amount,
+ spentItem.identifier,
+ spentItem.token,
+ expectedRecipient
+ );
+ }
+ }
+ }
+
+ function _assertNativeTokenTransfer(
+ uint256 expectedAmount,
+ address expectedRecipient
+ ) internal view {
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient then revert, because that means the recipient did not
+ // receive the expected amount at the time the order was ratified or
+ // validated.
+ if (expectedAmount > address(expectedRecipient).balance) {
+ revert InvalidNativeTokenBalance(
+ expectedAmount,
+ address(expectedRecipient).balance,
+ expectedRecipient
+ );
+ }
+ }
+
+ function _assertERC20Transfer(
+ uint256 expectedAmount,
+ address token,
+ address expectedRecipient
+ ) internal view {
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient, revert.
+ if (
+ expectedAmount > ERC20Interface(token).balanceOf(expectedRecipient)
+ ) {
+ revert InvalidERC20Balance(
+ expectedAmount,
+ ERC20Interface(token).balanceOf(expectedRecipient),
+ expectedRecipient,
+ token
+ );
+ }
+ }
+
+ function _assertERC721Transfer(
+ uint256 checkedTokenId,
+ address token,
+ address expectedRecipient
+ ) internal view {
+ // If the actual owner of the token is not the expected recipient,
+ // revert.
+ address actualOwner = ERC721Interface(token).ownerOf(checkedTokenId);
+ if (expectedRecipient != actualOwner) {
+ revert InvalidOwner(
+ expectedRecipient,
+ actualOwner,
+ token,
+ checkedTokenId
+ );
+ }
+ }
+
+ function _assertERC1155Transfer(
+ uint256 expectedAmount,
+ uint256 identifier,
+ address token,
+ address expectedRecipient
+ ) internal view {
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient, revert.
+ if (
+ expectedAmount >
+ ERC1155Interface(token).balanceOf(expectedRecipient, identifier)
+ ) {
+ revert InvalidERC1155Balance(
+ expectedAmount,
+ ERC1155Interface(token).balanceOf(
+ expectedRecipient,
+ identifier
+ ),
+ expectedRecipient,
+ token
+ );
+ }
+ }
+
+ function setExpectedOfferRecipient(address expectedOfferRecipient) public {
+ _expectedOfferRecipient = expectedOfferRecipient;
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override(ContractOffererInterface) returns (bool) {
+ return interfaceId == type(ContractOffererInterface).interfaceId;
+ }
+}
diff --git a/contracts/test/TestContractOfferer.sol b/contracts/test/TestContractOfferer.sol
index 4840b16e8..a3f135a0a 100644
--- a/contracts/test/TestContractOfferer.sol
+++ b/contracts/test/TestContractOfferer.sol
@@ -11,6 +11,8 @@ import {
ContractOffererInterface
} from "../interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../interfaces/ERC165.sol";
+
import { ItemType } from "../lib/ConsiderationEnums.sol";
import {
@@ -28,7 +30,7 @@ import {
* an order. The offered item is placed into this contract as part of
* deployment and the corresponding token approvals are set for Seaport.
*/
-contract TestContractOfferer is ContractOffererInterface {
+contract TestContractOfferer is ERC165, ContractOffererInterface {
error OrderUnavailable();
address private immutable _SEAPORT;
@@ -53,6 +55,20 @@ contract TestContractOfferer is ContractOffererInterface {
receive() external payable {}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/// In case of criteria based orders and non-wildcard items, the member
/// `available.identifier` would correspond to the `identifierOrCriteria`
/// i.e., the merkle-root.
diff --git a/contracts/test/TestContractOffererNativeToken.sol b/contracts/test/TestContractOffererNativeToken.sol
index b1955fc6b..760eac52b 100644
--- a/contracts/test/TestContractOffererNativeToken.sol
+++ b/contracts/test/TestContractOffererNativeToken.sol
@@ -10,6 +10,8 @@ import {
ContractOffererInterface
} from "../interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../interfaces/ERC165.sol";
+
import { ItemType } from "../lib/ConsiderationEnums.sol";
import {
@@ -21,7 +23,7 @@ import {
/**
* @title TestContractOffererNativeToken
*/
-contract TestContractOffererNativeToken is ContractOffererInterface {
+contract TestContractOffererNativeToken is ContractOffererInterface, ERC165 {
error OrderUnavailable();
address private immutable _SEAPORT;
@@ -246,6 +248,20 @@ contract TestContractOffererNativeToken is ContractOffererInterface {
return bytes4(0xf23a6e61);
}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/contracts/test/TestERC1271.sol b/contracts/test/TestERC1271.sol
new file mode 100644
index 000000000..ed510e441
--- /dev/null
+++ b/contracts/test/TestERC1271.sol
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import { IERC1271 } from "../interfaces/IERC1271.sol";
+
+contract TestERC1271 is IERC1271 {
+ address public immutable owner;
+
+ constructor(address owner_) {
+ owner = owner_;
+ }
+
+ function isValidSignature(
+ bytes32 digest,
+ bytes memory signature
+ ) external view returns (bytes4) {
+ bytes32 r;
+ bytes32 s;
+ uint8 v;
+
+ assembly {
+ r := mload(add(signature, 0x20))
+ s := mload(add(signature, 0x40))
+ v := byte(0, mload(add(signature, 0x60)))
+ }
+
+ if (
+ uint256(s) >
+ 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
+ ) {
+ revert();
+ }
+
+ if (v != 27 && v != 28) {
+ revert();
+ }
+
+ address signer = ecrecover(digest, v, r, s);
+
+ if (signer == address(0)) {
+ revert();
+ }
+
+ if (signer != owner) {
+ revert();
+ }
+
+ return IERC1271.isValidSignature.selector;
+ }
+}
diff --git a/contracts/test/TestERC20.sol b/contracts/test/TestERC20.sol
index ea4abd901..fc3e5f0e7 100644
--- a/contracts/test/TestERC20.sol
+++ b/contracts/test/TestERC20.sol
@@ -36,6 +36,12 @@ contract TestERC20 is ERC20("Test20", "TST20", 18) {
return false;
}
+ uint256 allowed = allowance[from][msg.sender];
+
+ if (amount > allowed) {
+ revert("NOT_AUTHORIZED");
+ }
+
super.transferFrom(from, to, amount);
if (noReturnData) {
@@ -46,4 +52,17 @@ contract TestERC20 is ERC20("Test20", "TST20", 18) {
ok = true;
}
+
+ function increaseAllowance(
+ address spender,
+ uint256 amount
+ ) external returns (bool) {
+ uint256 current = allowance[msg.sender][spender];
+ uint256 remaining = type(uint256).max - current;
+ if (amount > remaining) {
+ amount = remaining;
+ }
+ approve(spender, current + amount);
+ return true;
+ }
}
diff --git a/contracts/test/TestERC721Fee.sol b/contracts/test/TestERC721Fee.sol
new file mode 100644
index 000000000..952e9e482
--- /dev/null
+++ b/contracts/test/TestERC721Fee.sol
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
+import { ERC2981 } from "./ERC2981.sol";
+
+contract TestERC721Fee is ERC721, ERC2981 {
+ /// @notice When set to false, `royaltyInfo` reverts
+ bool creatorFeeEnabled = false;
+ /// @notice Below the min transaction price, `royaltyInfo` reverts
+ uint256 minTransactionPrice = 0;
+
+ constructor() ERC721("Fee", "FEE") {}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC721, ERC2981) returns (bool) {
+ return
+ ERC721.supportsInterface(interfaceId) ||
+ ERC2981.supportsInterface(interfaceId);
+ }
+
+ function mint(address to, uint256 id) external {
+ _mint(to, id);
+ }
+
+ function burn(uint256 id) external {
+ _burn(id);
+ }
+
+ function tokenURI(uint256) public pure override returns (string memory) {
+ return "tokenURI";
+ }
+
+ function royaltyInfo(
+ uint256,
+ uint256 _salePrice
+ ) public view override returns (address, uint256) {
+ if (!creatorFeeEnabled) {
+ revert("creator fee disabled");
+ }
+ if (_salePrice < minTransactionPrice) {
+ revert("sale price too low");
+ }
+
+ return (
+ 0x000000000000000000000000000000000000fEE2,
+ (_salePrice * (creatorFeeEnabled ? 250 : 0)) / 10000
+ ); // 2.5% fee to 0xFEE2
+ }
+
+ function setCreatorFeeEnabled(bool enabled) public {
+ creatorFeeEnabled = enabled;
+ }
+
+ function setMinTransactionPrice(uint256 minTransactionPrice_) public {
+ minTransactionPrice = minTransactionPrice_;
+ }
+}
diff --git a/contracts/test/TestERC721Funky.sol b/contracts/test/TestERC721Funky.sol
new file mode 100644
index 000000000..ea2b082b5
--- /dev/null
+++ b/contracts/test/TestERC721Funky.sol
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
+
+import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
+
+/**
+ * @notice TestERC721Funky is an ERC721 that implements ERC2981 with an incorrect return type.
+ */
+contract TestERC721Funky is ERC721("TestERC721Funky", "TST721FUNKY") {
+ function mint(address to, uint256 id) external {
+ _mint(to, id);
+ }
+
+ function burn(uint256 id) external {
+ _burn(id);
+ }
+
+ function tokenURI(uint256) public pure override returns (string memory) {
+ return "tokenURI";
+ }
+
+ function royaltyInfo(uint256, uint256) public pure returns (address) {
+ return (0x000000000000000000000000000000000000fEE2); // 2.5% fee to 0xFEE2
+ }
+}
diff --git a/contracts/test/TestERC721Revert.sol b/contracts/test/TestERC721Revert.sol
index 8d820122f..e0e897183 100644
--- a/contracts/test/TestERC721Revert.sol
+++ b/contracts/test/TestERC721Revert.sol
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
-contract TestERC721Revert {
+import { TestERC721 } from "./TestERC721.sol";
+
+contract TestERC721Revert is TestERC721 {
function transferFrom(
address /* from */,
address /* to */,
uint256 /* amount */
- ) public pure {
+ ) public pure override {
revert(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
);
diff --git a/contracts/test/TestInvalidContractOfferer165.sol b/contracts/test/TestInvalidContractOfferer165.sol
new file mode 100644
index 000000000..2661ae0f2
--- /dev/null
+++ b/contracts/test/TestInvalidContractOfferer165.sol
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+import { ERC165 } from "../interfaces/ERC165.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
+
+contract TestInvalidContractOfferer165 {
+ error OrderUnavailable();
+
+ address private immutable _SEAPORT;
+
+ SpentItem private _available;
+ SpentItem private _required;
+
+ bool public ready;
+ bool public fulfilled;
+
+ uint256 public extraAvailable;
+ uint256 public extraRequired;
+
+ constructor(address seaport) {
+ // Set immutable values and storage variables.
+ _SEAPORT = seaport;
+ fulfilled = false;
+ ready = false;
+ extraAvailable = 0;
+ extraRequired = 0;
+ }
+
+ receive() external payable {}
+
+ /// In case of criteria based orders and non-wildcard items, the member
+ /// `available.identifier` would correspond to the `identifierOrCriteria`
+ /// i.e., the merkle-root.
+ /// @param identifier corresponds to the actual token-id that gets transferred.
+ function activateWithCriteria(
+ SpentItem memory available,
+ SpentItem memory required,
+ uint256 identifier
+ ) public {
+ if (ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ if (available.itemType == ItemType.ERC721_WITH_CRITERIA) {
+ ERC721Interface token = ERC721Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), identifier);
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (available.itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ ERC1155Interface token = ERC1155Interface(available.token);
+
+ token.safeTransferFrom(
+ msg.sender,
+ address(this),
+ identifier,
+ available.amount,
+ ""
+ );
+
+ token.setApprovalForAll(_SEAPORT, true);
+ }
+
+ // Set storage variables.
+ _available = available;
+ _required = required;
+ ready = true;
+ }
+
+ function activate(
+ SpentItem memory available,
+ SpentItem memory required
+ ) public payable {
+ if (ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ // Retrieve the offered item and set associated approvals.
+ if (available.itemType == ItemType.NATIVE) {
+ available.amount = address(this).balance;
+ } else if (available.itemType == ItemType.ERC20) {
+ ERC20Interface token = ERC20Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), available.amount);
+
+ token.approve(_SEAPORT, available.amount);
+ } else if (available.itemType == ItemType.ERC721) {
+ ERC721Interface token = ERC721Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), available.identifier);
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (available.itemType == ItemType.ERC1155) {
+ ERC1155Interface token = ERC1155Interface(available.token);
+
+ token.safeTransferFrom(
+ msg.sender,
+ address(this),
+ available.identifier,
+ available.amount,
+ ""
+ );
+
+ token.setApprovalForAll(_SEAPORT, true);
+ }
+
+ // Set storage variables.
+ _available = available;
+ _required = required;
+ ready = true;
+ }
+
+ function extendAvailable() public {
+ if (!ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ extraAvailable++;
+
+ _available.amount /= 2;
+ }
+
+ function extendRequired() public {
+ if (!ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ extraRequired++;
+ }
+
+ function generateOrder(
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata context
+ )
+ external
+ virtual
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Ensure the caller is Seaport & the order has not yet been fulfilled.
+ if (
+ !ready ||
+ fulfilled ||
+ msg.sender != _SEAPORT ||
+ context.length % 32 != 0
+ ) {
+ revert OrderUnavailable();
+ }
+
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1 + extraAvailable);
+ consideration = new ReceivedItem[](1 + extraRequired);
+
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offer[i] = _available;
+ }
+
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ consideration[i] = ReceivedItem({
+ itemType: _required.itemType,
+ token: _required.token,
+ identifier: _required.identifier,
+ amount: _required.amount,
+ recipient: payable(address(this))
+ });
+ }
+
+ // Update storage to reflect that the order has been fulfilled.
+ fulfilled = true;
+ }
+
+ function previewOrder(
+ address caller,
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata context
+ )
+ external
+ view
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Ensure the caller is Seaport & the order has not yet been fulfilled.
+ if (
+ !ready ||
+ fulfilled ||
+ caller != _SEAPORT ||
+ context.length % 32 != 0
+ ) {
+ revert OrderUnavailable();
+ }
+
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1 + extraAvailable);
+ consideration = new ReceivedItem[](1 + extraRequired);
+
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offer[i] = _available;
+ }
+
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ consideration[i] = ReceivedItem({
+ itemType: _required.itemType,
+ token: _required.token,
+ identifier: _required.identifier,
+ amount: _required.amount,
+ recipient: payable(address(this))
+ });
+ }
+ }
+
+ function getInventory()
+ external
+ view
+ returns (SpentItem[] memory offerable, SpentItem[] memory receivable)
+ {
+ // Set offerable and receivable supplied at deployment if unfulfilled.
+ if (!ready || fulfilled) {
+ offerable = new SpentItem[](0);
+
+ receivable = new SpentItem[](0);
+ } else {
+ offerable = new SpentItem[](1 + extraAvailable);
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offerable[i] = _available;
+ }
+
+ receivable = new SpentItem[](1 + extraRequired);
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ receivable[i] = _required;
+ }
+ }
+ }
+
+ function ratifyOrder(
+ SpentItem[] calldata /* offer */,
+ ReceivedItem[] calldata /* consideration */,
+ bytes calldata context,
+ bytes32[] calldata orderHashes,
+ uint256 /* contractNonce */
+ ) external pure virtual returns (bytes4 /* ratifyOrderMagicValue */) {
+ if (context.length > 32 && context.length % 32 == 0) {
+ bytes32[] memory expectedOrderHashes = abi.decode(
+ context,
+ (bytes32[])
+ );
+
+ uint256 expectedLength = expectedOrderHashes.length;
+
+ if (expectedLength != orderHashes.length) {
+ revert("Revert on unexpected order hashes length");
+ }
+
+ for (uint256 i = 0; i < expectedLength; ++i) {
+ if (expectedOrderHashes[i] != orderHashes[i]) {
+ revert("Revert on unexpected order hash");
+ }
+ }
+ }
+
+ return ContractOffererInterface.ratifyOrder.selector;
+ }
+
+ function onERC1155Received(
+ address,
+ address,
+ uint256,
+ uint256,
+ bytes calldata
+ ) external pure returns (bytes4) {
+ return bytes4(0xf23a6e61);
+ }
+
+ /**
+ * @dev Returns the metadata for this contract offerer.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestContractOfferer", schemas);
+ }
+}
diff --git a/contracts/test/TestInvalidZone.sol b/contracts/test/TestInvalidZone.sol
new file mode 100644
index 000000000..50c54cd85
--- /dev/null
+++ b/contracts/test/TestInvalidZone.sol
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { ZoneParameters, Schema } from "../lib/ConsiderationStructs.sol";
+
+import { ERC165 } from "../interfaces/ERC165.sol";
+
+import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+
+contract TestInvalidZone is ERC165, ZoneInterface {
+ // Returns invalid magic value
+ function validateOrder(
+ ZoneParameters calldata
+ ) external pure returns (bytes4 validOrderMagicValue) {
+ return ZoneInterface.getSeaportMetadata.selector;
+ }
+
+ /**
+ * @dev Returns the metadata for this zone.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 3003;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestZone", schemas);
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+}
diff --git a/contracts/test/TestPostExecution.sol b/contracts/test/TestPostExecution.sol
index 34dd469ce..f5669b84a 100644
--- a/contracts/test/TestPostExecution.sol
+++ b/contracts/test/TestPostExecution.sol
@@ -3,6 +3,8 @@ pragma solidity ^0.8.13;
import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+import { ERC165 } from "../interfaces/ERC165.sol";
+
import { ERC721Interface } from "../interfaces/AbridgedTokenInterfaces.sol";
import { ItemType } from "../lib/ConsiderationEnums.sol";
@@ -13,7 +15,7 @@ import {
ZoneParameters
} from "../lib/ConsiderationStructs.sol";
-contract TestPostExecution is ZoneInterface {
+contract TestPostExecution is ERC165, ZoneInterface {
function validateOrder(
ZoneParameters calldata zoneParameters
) external view override returns (bytes4 validOrderMagicValue) {
@@ -68,4 +70,12 @@ contract TestPostExecution is ZoneInterface {
return ("TestPostExecution", schemas);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/contracts/test/TestTransferValidationZoneOfferer.sol b/contracts/test/TestTransferValidationZoneOfferer.sol
index 25f45460a..9427c5c19 100644
--- a/contracts/test/TestTransferValidationZoneOfferer.sol
+++ b/contracts/test/TestTransferValidationZoneOfferer.sol
@@ -7,6 +7,8 @@ import {
ERC1155Interface
} from "../interfaces/AbridgedTokenInterfaces.sol";
+import { ERC165 } from "../interfaces/ERC165.sol";
+
import {
ReceivedItem,
Schema,
@@ -22,14 +24,58 @@ import {
import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+/**
+ * @dev This contract is used to validate transfer within the zone/offerer. Use
+ * the HashValidationZoneOfferer to validate calldata via hashes.
+ */
contract TestTransferValidationZoneOfferer is
ContractOffererInterface,
- ZoneInterface
+ ZoneInterface,
+ ERC165
{
- error InvalidBalance();
- error InvalidOwner();
+ error InvalidNativeTokenBalance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress
+ );
+ error InvalidERC20Balance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress,
+ address checkedToken
+ );
+ error InvalidERC1155Balance(
+ uint256 expectedBalance,
+ uint256 actualBalance,
+ address checkedAddress,
+ address checkedToken
+ );
+ // 0x38fb386a
+ error InvalidOwner(
+ address expectedOwner,
+ address actualOwner,
+ address checkedToken,
+ uint256 checkedTokenId
+ );
+ error IncorrectSeaportBalance(
+ uint256 expectedBalance,
+ uint256 actualBalance
+ );
+ event ValidateOrderDataHash(bytes32 dataHash);
+
+ receive() external payable {}
+
+ address internal _expectedOfferRecipient;
+
+ mapping(bytes32 => bytes32) public orderHashToValidateOrderDataHash;
+
+ // Pass in the null address to expect the fulfiller.
+ constructor(address expectedOfferRecipient) {
+ _expectedOfferRecipient = expectedOfferRecipient;
+ }
- constructor() {}
+ bool public called = false;
+ uint public callCount = 0;
/**
* @dev Validates that the parties have received the correct items.
@@ -39,22 +85,66 @@ contract TestTransferValidationZoneOfferer is
*
* @return validOrderMagicValue The magic value to indicate things are OK.
*/
-
function validateOrder(
ZoneParameters calldata zoneParameters
- ) external view override returns (bytes4 validOrderMagicValue) {
+ ) external override returns (bytes4 validOrderMagicValue) {
// Validate the order.
+
// Currently assumes that the balances of all tokens of addresses are
- // zero at the start of the transaction.
+ // zero at the start of the transaction. Accordingly, take care to
+ // use an address in tests that is not pre-populated with tokens.
+
+ // Get the length of msg.data
+ uint256 dataLength = msg.data.length;
+
+ // Create a variable to store msg.data in memory
+ bytes memory data;
+
+ // Copy msg.data to memory
+ assembly {
+ let ptr := mload(0x40)
+ calldatacopy(add(ptr, 0x20), 0, dataLength)
+ mstore(ptr, dataLength)
+ data := ptr
+ }
+
+ // Get the hash of msg.data
+ bytes32 calldataHash = keccak256(data);
- // Check if all consideration items have been received.
+ // Get the orderHash from zoneParameters
+ bytes32 orderHash = zoneParameters.orderHash;
+
+ // Store callDataHash in orderHashToValidateOrderDataHash
+ orderHashToValidateOrderDataHash[orderHash] = calldataHash;
+
+ // Emit a DataHash event with the hash of msg.data
+ emit ValidateOrderDataHash(calldataHash);
+
+ // Check if Seaport is empty. This makes sure that we've transferred
+ // all native token balance out of Seaport before we do the validation.
+ uint256 seaportBalance = address(msg.sender).balance;
+
+ if (seaportBalance > 0) {
+ revert IncorrectSeaportBalance(0, seaportBalance);
+ }
+
+ // Ensure that the offerer or recipient has received all consideration
+ // items.
_assertValidReceivedItems(zoneParameters.consideration);
- // Check if all offer items have been spent.
- _assertValidSpentItems(zoneParameters.fulfiller, zoneParameters.offer);
+ address expectedOfferRecipient = _expectedOfferRecipient == address(0)
+ ? zoneParameters.fulfiller
+ : _expectedOfferRecipient;
+
+ // Ensure that the expected recipient has received all offer items.
+ _assertValidSpentItems(expectedOfferRecipient, zoneParameters.offer);
+
+ // Set the global called flag to true.
+ called = true;
+ callCount++;
// Return the selector of validateOrder as the magic value.
- validOrderMagicValue = ZoneInterface.validateOrder.selector;
+ validOrderMagicValue = this.validateOrder.selector;
}
/**
@@ -114,18 +204,34 @@ contract TestTransferValidationZoneOfferer is
bytes calldata context /* context */,
bytes32[] calldata /* orderHashes */,
uint256 /* contractNonce */
- ) external view override returns (bytes4 /* ratifyOrderMagicValue */) {
+ ) external override returns (bytes4 /* ratifyOrderMagicValue */) {
// Ratify the order.
+ // Check if Seaport is empty. This makes sure that we've transferred
+ // all native token balance out of Seaport before we do the validation.
+ uint256 seaportBalance = address(msg.sender).balance;
+
+ if (seaportBalance > 0) {
+ revert IncorrectSeaportBalance(0, seaportBalance);
+ }
// Ensure that the offerer or recipient has received all consideration
// items.
_assertValidReceivedItems(maximumSpent);
- // Get the fulfiller address from the context.
- address fulfiller = address(bytes20(context[0:20]));
+ // It's necessary to pass in either an expected offerer or an address
+ // in the context. If neither is provided, this ternary will revert
+ // with a generic, hard-to-debug revert when it tries to slice bytes
+ // from the context.
+ address expectedOfferRecipient = _expectedOfferRecipient == address(0)
+ ? address(bytes20(context[0:20]))
+ : _expectedOfferRecipient;
- // Ensure that the fulfiller has received all offer items.
- _assertValidSpentItems(fulfiller, minimumReceived);
+ // Ensure that the expected recipient has received all offer items.
+ _assertValidSpentItems(expectedOfferRecipient, minimumReceived);
+
+ // Set the global called flag to true.
+ called = true;
+ callCount++;
return this.ratifyOrder.selector;
}
@@ -174,13 +280,13 @@ contract TestTransferValidationZoneOfferer is
address recipient;
ItemType itemType;
ReceivedItem memory receivedItem;
- // Check if all consideration items have been received.
+
+ // Iterate over all received items.
for (uint256 i = 0; i < receivedItems.length; i++) {
// Check if the consideration item has been received.
receivedItem = receivedItems[i];
// Get the recipient of the consideration item.
recipient = receivedItem.recipient;
-
// Get item type.
itemType = receivedItem.itemType;
@@ -215,13 +321,13 @@ contract TestTransferValidationZoneOfferer is
}
function _assertValidSpentItems(
- address fulfiller,
+ address expectedRecipient,
SpentItem[] calldata spentItems
) internal view {
SpentItem memory spentItem;
ItemType itemType;
- // Check if all offer items have been spent.
+ // Iterate over all spent items.
for (uint256 i = 0; i < spentItems.length; i++) {
// Check if the offer item has been spent.
spentItem = spentItems[i];
@@ -231,20 +337,20 @@ contract TestTransferValidationZoneOfferer is
// Check balance/ownerOf depending on item type.
if (itemType == ItemType.NATIVE) {
// NATIVE Token
- _assertNativeTokenTransfer(spentItem.amount, fulfiller);
+ _assertNativeTokenTransfer(spentItem.amount, expectedRecipient);
} else if (itemType == ItemType.ERC20) {
// ERC20 Token
_assertERC20Transfer(
spentItem.amount,
spentItem.token,
- fulfiller
+ expectedRecipient
);
} else if (itemType == ItemType.ERC721) {
// ERC721 Token
_assertERC721Transfer(
spentItem.identifier,
spentItem.token,
- fulfiller
+ expectedRecipient
);
} else if (itemType == ItemType.ERC1155) {
// ERC1155 Token
@@ -252,49 +358,109 @@ contract TestTransferValidationZoneOfferer is
spentItem.amount,
spentItem.identifier,
spentItem.token,
- fulfiller
+ expectedRecipient
);
}
}
}
function _assertNativeTokenTransfer(
- uint256 amount,
- address recipient
+ uint256 expectedAmount,
+ address expectedRecipient
) internal view {
- if (amount > address(recipient).balance) {
- revert InvalidBalance();
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient then revert, because that means the recipient did not
+ // receive the expected amount at the time the order was ratified or
+ // validated.
+ if (expectedAmount > address(expectedRecipient).balance) {
+ revert InvalidNativeTokenBalance(
+ expectedAmount,
+ address(expectedRecipient).balance,
+ expectedRecipient
+ );
}
}
function _assertERC20Transfer(
- uint256 amount,
+ uint256 expectedAmount,
address token,
- address recipient
+ address expectedRecipient
) internal view {
- if (amount > ERC20Interface(token).balanceOf(recipient)) {
- revert InvalidBalance();
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient, revert.
+ if (
+ expectedAmount > ERC20Interface(token).balanceOf(expectedRecipient)
+ ) {
+ revert InvalidERC20Balance(
+ expectedAmount,
+ ERC20Interface(token).balanceOf(expectedRecipient),
+ expectedRecipient,
+ token
+ );
}
}
function _assertERC721Transfer(
- uint256 identifier,
+ uint256 checkedTokenId,
address token,
- address recipient
+ address expectedRecipient
) internal view {
- if (recipient != ERC721Interface(token).ownerOf(identifier)) {
- revert InvalidOwner();
+ // If the actual owner of the token is not the expected recipient,
+ // revert.
+ address actualOwner = ERC721Interface(token).ownerOf(checkedTokenId);
+ if (expectedRecipient != actualOwner) {
+ revert InvalidOwner(
+ expectedRecipient,
+ actualOwner,
+ token,
+ checkedTokenId
+ );
}
}
function _assertERC1155Transfer(
- uint256 amount,
+ uint256 expectedAmount,
uint256 identifier,
address token,
- address recipient
+ address expectedRecipient
) internal view {
- if (amount > ERC1155Interface(token).balanceOf(recipient, identifier)) {
- revert InvalidBalance();
+ // If the amount we read from the spent item or received item (the
+ // expected transfer value) is greater than the balance of the expected
+ // recipient, revert.
+ if (
+ expectedAmount >
+ ERC1155Interface(token).balanceOf(expectedRecipient, identifier)
+ ) {
+ revert InvalidERC1155Balance(
+ expectedAmount,
+ ERC1155Interface(token).balanceOf(
+ expectedRecipient,
+ identifier
+ ),
+ expectedRecipient,
+ token
+ );
}
}
+
+ function setExpectedOfferRecipient(address expectedOfferRecipient) public {
+ _expectedOfferRecipient = expectedOfferRecipient;
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface, ZoneInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/contracts/test/TestZone.sol b/contracts/test/TestZone.sol
index ca78521fc..27db79584 100644
--- a/contracts/test/TestZone.sol
+++ b/contracts/test/TestZone.sol
@@ -3,9 +3,11 @@ pragma solidity ^0.8.13;
import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+import { ERC165 } from "../interfaces/ERC165.sol";
+
import { Schema, ZoneParameters } from "../lib/ConsiderationStructs.sol";
-contract TestZone is ZoneInterface {
+contract TestZone is ERC165, ZoneInterface {
function validateOrder(
ZoneParameters calldata zoneParameters
) external pure override returns (bytes4 validOrderMagicValue) {
@@ -68,4 +70,12 @@ contract TestZone is ZoneInterface {
return ("TestZone", schemas);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/contracts/test/TypehashDirectory.sol b/contracts/test/TypehashDirectory.sol
index 9fd733109..c37eac131 100644
--- a/contracts/test/TypehashDirectory.sol
+++ b/contracts/test/TypehashDirectory.sol
@@ -134,43 +134,43 @@ contract TypehashDirectory {
function getTreeSubTypes() internal pure returns (bytes memory) {
// Construct the OfferItem type string.
bytes memory offerItemTypeString = bytes(
- "OfferItem("
- "uint8 itemType,"
- "address token,"
- "uint256 identifierOrCriteria,"
- "uint256 startAmount,"
- "uint256 endAmount"
- ")"
- );
+ "OfferItem("
+ "uint8 itemType,"
+ "address token,"
+ "uint256 identifierOrCriteria,"
+ "uint256 startAmount,"
+ "uint256 endAmount"
+ ")"
+ );
// Construct the ConsiderationItem type string.
bytes memory considerationItemTypeString = bytes(
- "ConsiderationItem("
- "uint8 itemType,"
- "address token,"
- "uint256 identifierOrCriteria,"
- "uint256 startAmount,"
- "uint256 endAmount,"
- "address recipient"
- ")"
- );
+ "ConsiderationItem("
+ "uint8 itemType,"
+ "address token,"
+ "uint256 identifierOrCriteria,"
+ "uint256 startAmount,"
+ "uint256 endAmount,"
+ "address recipient"
+ ")"
+ );
// Construct the OrderComponents type string, not including the above.
bytes memory orderComponentsPartialTypeString = bytes(
- "OrderComponents("
- "address offerer,"
- "address zone,"
- "OfferItem[] offer,"
- "ConsiderationItem[] consideration,"
- "uint8 orderType,"
- "uint256 startTime,"
- "uint256 endTime,"
- "bytes32 zoneHash,"
- "uint256 salt,"
- "bytes32 conduitKey,"
- "uint256 counter"
- ")"
- );
+ "OrderComponents("
+ "address offerer,"
+ "address zone,"
+ "OfferItem[] offer,"
+ "ConsiderationItem[] consideration,"
+ "uint8 orderType,"
+ "uint256 startTime,"
+ "uint256 endTime,"
+ "bytes32 zoneHash,"
+ "uint256 salt,"
+ "bytes32 conduitKey,"
+ "uint256 counter"
+ ")"
+ );
// Return the combined string.
return
diff --git a/contracts/zones/PausableZone.sol b/contracts/zones/PausableZone.sol
index 6abf08d1d..c6dd084b7 100644
--- a/contracts/zones/PausableZone.sol
+++ b/contracts/zones/PausableZone.sol
@@ -7,6 +7,8 @@ import {
PausableZoneEventsAndErrors
} from "./interfaces/PausableZoneEventsAndErrors.sol";
+import { ERC165 } from "../interfaces/ERC165.sol";
+
import { SeaportInterface } from "../interfaces/SeaportInterface.sol";
import {
@@ -31,6 +33,7 @@ import { PausableZoneInterface } from "./interfaces/PausableZoneInterface.sol";
* cannot execute orders that return native tokens to the fulfiller.
*/
contract PausableZone is
+ ERC165,
PausableZoneEventsAndErrors,
ZoneInterface,
PausableZoneInterface
@@ -252,4 +255,12 @@ contract PausableZone is
return ("PausableZone", schemas);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/contracts/zones/PausableZoneController.sol b/contracts/zones/PausableZoneController.sol
index 14662e518..1e731ca95 100644
--- a/contracts/zones/PausableZoneController.sol
+++ b/contracts/zones/PausableZoneController.sol
@@ -240,7 +240,7 @@ contract PausableZoneController is
/**
* @notice Initiate Zone ownership transfer by assigning a new potential
- * owner this contract. Once set, the new potential owner
+ * owner of this contract. Once set, the new potential owner
* may call `acceptOwnership` to claim ownership.
* Only the owner in question may call this function.
*
@@ -360,7 +360,7 @@ contract PausableZoneController is
}
/**
- * @notice An external view function that return the potential owner.
+ * @notice An external view function that returns the potential owner.
*
* @return The address of the potential owner.
*/
diff --git a/contracts/zones/interfaces/PausableZoneControllerInterface.sol b/contracts/zones/interfaces/PausableZoneControllerInterface.sol
index 8c115556c..fb661b395 100644
--- a/contracts/zones/interfaces/PausableZoneControllerInterface.sol
+++ b/contracts/zones/interfaces/PausableZoneControllerInterface.sol
@@ -104,7 +104,7 @@ interface PausableZoneControllerInterface {
/**
* @notice Initiate Zone ownership transfer by assigning a new potential
- * owner this contract. Once set, the new potential owner
+ * owner of this contract. Once set, the new potential owner
* may call `acceptOwnership` to claim ownership.
* Only the owner in question may call this function.
*
@@ -153,7 +153,7 @@ interface PausableZoneControllerInterface {
function owner() external view returns (address);
/**
- * @notice An external view function that return the potential owner.
+ * @notice An external view function that returns the potential owner.
*
* @return The address of the potential owner.
*/
diff --git a/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol b/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol
index 459343631..bb80f9402 100644
--- a/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol
+++ b/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol
@@ -43,7 +43,7 @@ interface PausableZoneEventsAndErrors {
/**
* @dev Emit an event whenever a zone owner assigns a new pauser
*
- * @param newPauser The new pausear of the zone.
+ * @param newPauser The new pauser of the zone.
*/
event PauserUpdated(address newPauser);
diff --git a/docs/Deployment.md b/docs/Deployment.md
index 4226d2711..b2c6c68d9 100644
--- a/docs/Deployment.md
+++ b/docs/Deployment.md
@@ -1,6 +1,8 @@
# Deploying Seaport
-Seaport 1.1, 1.2, and the ConduitController can each be deployed to their respective canonical deployment address on all EVM chains using the CREATE2 Factory.
+Seaport 1.5 and the ConduitController can each be deployed to their respective canonical deployment address on all EVM chains using the CREATE2 Factory. Note that a pre-155 transaction with a gas price of 100 gwei (or a manual workaround) is required as part of the deployment process (subsequent transactions can be submitted without these constraints), and that EVM equivalence (particularly consistent CREATE2 address derivation) is required in order to deploy to the canonical cross-chain deployment addresses.
+
+If you are deploying a fork of Seaport, it is *highly* advised that you **supply the canonical conduit controller** as its constructor argument; this allows the Seaport fork to use conduits that are fully interoperable with the canonical Seaport instance. (Better yet, deploy the canonical Seaport instance and use a distinct conduit if needed!)
## Relevant Addresses
@@ -30,12 +32,8 @@ Seaport 1.1, 1.2, and the ConduitController can each be deployed to their respec
0x00000000F9490004C11Cef243f5400493c00Ad63 |
- Seaport 1.1 |
- 0x00000000006c3852cbEf3e08E8dF289169EdE581 |
-
-
- Seaport 1.2 |
- 0x00000000000006c7676171937C444f6BDe3D6282 |
+ Seaport 1.5 |
+ 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC |
@@ -45,7 +43,7 @@ Seaport 1.1, 1.2, and the ConduitController can each be deployed to their respec
If there is no `IMMUTABLE_CREATE2_FACTORY_ADDRESS` on the chain, deploy this first with foundry.
-1. Send 0.01 Ether to the `KEYLESS_CREATE2_DEPLOYER_ADDRESS`
+1. Send 0.01 Ether (or relevant native token for the chain in question) to the `KEYLESS_CREATE2_DEPLOYER_ADDRESS`
2. Create the `KEYLESS_CREATE2_ADDRESS` by submitting this pre-signed transaction:
```
@@ -74,30 +72,28 @@ Once the `IMMUTABLE_CREATE2_FACTORY_ADDRESS` exists, begin to deploy the contrac
cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000dc0ef3c792976604960400000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000302760c08060405234620000ea57600090610c9f906001600160401b03603f8301601f1916820181811183821017620000da575b604052828252620023889160208101908484833951902060805260405192839281840192831184841017620000ca575b8339039082f58015620000ba575b6001600160a01b03163f60a05260405161227490816200011482396080518181816102b101528181610bcc0152610d06015260a0518181816102d401528181610c620152610da90152f35b620000c462000106565b6200006f565b620000d4620000ef565b62000061565b620000e4620000ef565b62000031565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b506040513d6000823e3d90fdfe60806040526004361015610013575b600080fd5b60003560e01c8063027cc7641461012b5780630a96ad391461012257806313ad9cab1461011957806314afd79e1461011057806333bc8572146101075780634e3f9580146100fe57806351710e45146100f55780636d435421146100ec5780636e9bfd9f146100e3578063794593bc146100da5780637b37e561146100d15780638b9e028b146100c8578063906c87cc146100bf576393790f44146100b757600080fd5b61000e61126e565b5061000e6111fa565b5061000e61113c565b5061000e610fc8565b5061000e610c8a565b5061000e610b3c565b5061000e6109bf565b5061000e610765565b5061000e6106f3565b5061000e61064f565b5061000e6105db565b5061000e6102fa565b5061000e61027b565b5061000e61017a565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576101b2610134565b602435906101bf81611574565b73ffffffffffffffffffffffffffffffffffffffff80911691600083815280602052600360408220015482101561023f5790600360408361023b9661020a9552806020522001611400565b90549060031b1c166040519182918291909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0390f35b602484604051907f6ceb340b0000000000000000000000000000000000000000000000000000000082526004820152fd5b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57604080517f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020820152f35b503461000e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57610332610134565b61033a610157565b90604435918215918215840361000e5761035381611505565b73ffffffffffffffffffffffffffffffffffffffff811690813b1561000e576040517fc4e8fcb500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528515156024820152610401926000908290604490829084905af180156105ce575b6105b5575b5073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b92600484019261043183859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5491821590806105ae575b1561048157505050600361047d92930161045682826114ce565b54929073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b555b005b91949391816105a5575b5061049257005b6104df61047d938560037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600098019201916104ce83546113a4565b90808203610504575b505050611447565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b6105766105449161053b61051b61059c9588611400565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b92839187611400565b90919082549060031b9173ffffffffffffffffffffffffffffffffffffffff9283811b93849216901b16911916179055565b859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b553880806104d7565b9050153861048b565b508061043c565b806105c26105c892611335565b80610270565b386103da565b6105d6611397565b6103d5565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610615610134565b61061e81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260016040600020015416604051908152f35b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206106e861068c610134565b73ffffffffffffffffffffffffffffffffffffffff6106a9610157565b916106b381611574565b166000526000835260046040600020019073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b541515604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff610740610134565b61074981611574565b1660005260006020526020600360406000200154604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5761079d610134565b6107a681611574565b61080c6107f360026107d88473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b33036109765761047f9060007f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a2610896600261086d8373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b73ffffffffffffffffffffffffffffffffffffffff3390806108dd60016107d88673ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b169083167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec6000604051a46001610935339273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6040517f88c3a11500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576109f7610134565b6109ff610157565b90610a0981611505565b73ffffffffffffffffffffffffffffffffffffffff808316908115610b095750610a5b6107f360026107d88573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b8114610ab95761093561047f93926002927f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da6000604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b506040517fcbc080ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b82602491604051917fa388d263000000000000000000000000000000000000000000000000000000008352166004820152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576040517fff00000000000000000000000000000000000000000000000000000000000000602082019081523060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260043560358301527f0000000000000000000000000000000000000000000000000000000000000000605583015273ffffffffffffffffffffffffffffffffffffffff91610c3b81607581015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611356565b519020604080519290911673ffffffffffffffffffffffffffffffffffffffff811683523f7f000000000000000000000000000000000000000000000000000000000000000014602083015290f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610cc6610157565b73ffffffffffffffffffffffffffffffffffffffff91828216908115610f9f57338160601c03610f7657610da46107f386516020810190610d8881610c0f7f0000000000000000000000000000000000000000000000000000000000000000883087917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b51902073ffffffffffffffffffffffffffffffffffffffff1690565b92833f7f000000000000000000000000000000000000000000000000000000000000000014610f3057947f4397af6128d529b8ae0442f99db1296d5136062597a15bbc61c1b2a6431a7d15610eca838060009961023b989796865180610c9f8082019082821067ffffffffffffffff831117610f23575b6115a0833903908df515610f16575b610e9c610e578973ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b91600183019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b55835173ffffffffffffffffffffffffffffffffffffffff8716815260208101919091529081906040820190565b0390a15194859483167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec8287a473ffffffffffffffffffffffffffffffffffffffff1682526020820190565b610f1e611397565b610e2a565b610f2b611305565b610e1b565b85517f6328ccb200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b600485517fcb6e5344000000000000000000000000000000000000000000000000000000008152fd5b600485517f99faaa04000000000000000000000000000000000000000000000000000000008152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611000610134565b61100981611505565b73ffffffffffffffffffffffffffffffffffffffff9081811660009281845283602052600260408520015416156110ba575061108e600291837f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055604051f35b602490604051907f6b0136160000000000000000000000000000000000000000000000000000000082526004820152fd5b6020908160408183019282815285518094520193019160005b828110611112575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611104565b503461000e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611175610134565b9061117f82611574565b73ffffffffffffffffffffffffffffffffffffffff91826000911681528082526003604082200192604051908193808654938481520195845280842093915b8383106111e15761023b866111d5818a0382611356565b604051918291826110eb565b84548116875295810195600194850194909201916111be565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020611234610134565b61123d81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260026040600020015416604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff6112bb610134565b16600052600060205260406000205480156112db57602090604051908152f35b60046040517f4ca82090000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161134957604052565b611351611305565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761134957604052565b506040513d6000823e3d90fd5b600181106113d1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80548210156114185760005260206000200190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8054801561149f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019061147c8282611400565b73ffffffffffffffffffffffffffffffffffffffff82549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b906105446114f692805490680100000000000000008210156114f8575b600182018155611400565b565b611500611305565b6114eb565b61150e81611574565b73ffffffffffffffffffffffffffffffffffffffff809116908160005260006020526001604060002001541633036115435750565b602490604051907fd4ed9a170000000000000000000000000000000000000000000000000000000082526004820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002054156112db5756fe60a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e0033a264697066735822122031e2de61a9e35e9e87d5eef6a36b045a0bab54c4031fd01a0f8138afce3cec3164736f6c634300080e003360a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e003300000000000000000000000000000000000000000000000000
```
-2. Deploy the `Seaport 1.1` contract by submitting:
-
-```
-cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000a39d1860ddeb0e016b0900000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000670b6101c060405234620000b9576200001f6200001962000114565b62000151565b604051615f7e90816200076d82396080518161282c015260a05181612852015260c05181612809015260e051818181611758015261269701526101005181818161162401526126e60152610120518181816117f40152612734015261014051816127b7015261016051816127dd015261018051818181611003015281816122f4015261246a01526101a05181818161233201526124a80152f35b600080fd5b604081019081106001600160401b03821117620000da57604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620000da57604052565b620066eb60208138039182604051938492620001318285620000f0565b833981010312620000b957516001600160a01b0381168103620000b95790565b604060049162000160620002e3565b610120526101005260e05260c05260a05260805246610140526200018362000237565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa90811562000203575b600091620001cd575b506101a052620001cb6001600055565b565b620001f3915060403d8111620001fb575b620001ea8183620000f0565b81019062000213565b5038620001bb565b503d620001de565b6200020d6200022a565b620001b2565b9190826040910312620000b9576020825192015190565b506040513d6000823e3d90fd5b60c05160805160a0516040519160208301938452604083015260608201524660808201523060a082015260a0815260c0810181811060018060401b03821117620000da5760405251902090565b604051906200029382620000be565b6003825262312e3160e81b6020830152565b90815180926000905b828210620002cb575011620002c1570190565b6000828201520190565b915080602080928401015181850152018391620002ae565b620002ed62000747565b8051602080920120916200030062000284565b8281519101209160405181810192816200032b85600a906909ecccccae492e8cada560b31b81520190565b6e1d5a5b9d0e081a5d195b551e5c194b608a1b8152600f016d1859191c995cdcc81d1bdad95b8b60921b8152600e017f75696e74323536206964656e7469666965724f7243726974657269612c0000008152601d017f75696e74323536207374617274416d6f756e742c0000000000000000000000008152601401701d5a5b9d0c8d4d88195b99105b5bdd5b9d607a1b8152601101602960f81b81526001010392601f19938481018452620003e19084620000f0565b60405171086dedce6d2c8cae4c2e8d2dedc92e8cada560731b8282019081529481601287016e1d5a5b9d0e081a5d195b551e5c194b608a1b8152600f016d1859191c995cdcc81d1bdad95b8b60921b8152600e017f75696e74323536206964656e7469666965724f7243726974657269612c0000008152601d017f75696e74323536207374617274416d6f756e742c0000000000000000000000008152601401711d5a5b9d0c8d4d88195b99105b5bdd5b9d0b60721b8152601201701859191c995cdcc81c9958da5c1a595b9d607a1b8152601101602960f81b8152600101038181018352620004d29083620000f0565b6040519283818101620004fc906010906f09ee4c8cae486dedae0dedccadce8e6560831b81520190565b6f1859191c995cdcc81bd999995c995c8b60821b81526010016c1859191c995cdcc81e9bdb994b609a1b8152600d017113d999995c925d195b56d7481bd999995c8b60721b81526012017f436f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f8152611b8b60f21b60208201526022016f1d5a5b9d0e081bdc99195c951e5c194b60821b8152601001711d5a5b9d0c8d4d881cdd185c9d151a5b594b60721b81526012016f1d5a5b9d0c8d4d88195b99151a5b594b60821b815260100170189e5d195ccccc881e9bdb9952185cda0b607a1b81526011016c1d5a5b9d0c8d4d881cd85b1d0b609a1b8152600d017f6279746573333220636f6e647569744b65792c0000000000000000000000000081526013016e3ab4b73a191a9b1031b7bab73a32b960891b8152600f01602960f81b81526001010382810185526200064e9085620000f0565b6040516c08a92a06e626488dedac2d2dc5609b1b8282019081529080600d83016b1cdd1c9a5b99c81b985b594b60a21b8152600c016e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b8152600f016f1d5a5b9d0c8d4d8818da185a5b92590b60821b81526010017f6164647265737320766572696679696e67436f6e7472616374000000000000008152601901602960f81b8152600101038481018252620006f69082620000f0565b5190209786519020968351902095604051938492830195866200071991620002a5565b6200072491620002a5565b6200072f91620002a5565b039081018252620007419082620000f0565b51902090565b604051906200075682620000be565b600782526614d9585c1bdc9d60ca1b602083015256fe60806040526004361015610013575b600080fd5b60003560e01c806306fdde031461013f57806346423aa71461013657806355944a421461012d5780635b34b9661461012457806379df72bd1461011b57806387201b41146101125780638814773214610109578063a817440414610100578063b3a34c4c146100f7578063e7acab24146100ee578063ed98a574146100e5578063f07ec373146100dc578063f47b7740146100d3578063fb0f3ee1146100ca5763fd9f1e10146100c257600080fd5b61000e61132d565b5061000e61102c565b5061000e610f8b565b5061000e610f46565b5061000e610eb5565b5061000e610e07565b5061000e610da3565b5061000e610d32565b5061000e610be3565b5061000e610b0f565b5061000e610994565b5061000e61092f565b5061000e61089e565b5061000e6101c1565b5061000e610199565b91908251928382526000905b8482106101815750601f8460209495601f199311610174575b0116010190565b600085828601015261016d565b90602090818082850101519082860101520190610154565b503461000e57600060031936011261000e57602080526707536561706f727460475260606020f35b503461000e57602060031936011261000e57600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761027057604052565b610278610224565b604052565b60c0810190811067ffffffffffffffff82111761027057604052565b6020810190811067ffffffffffffffff82111761027057604052565b6040810190811067ffffffffffffffff82111761027057604052565b90601f601f19910116810190811067ffffffffffffffff82111761027057604052565b60405190610160820182811067ffffffffffffffff82111761027057604052565b6040519061032282610254565b565b60209067ffffffffffffffff811161033e575b60051b0190565b610346610224565b610337565b6001600160a01b0381160361000e57565b60a435906103228261034b565b35906103228261034b565b3590600682101561000e57565b92919261038d82610324565b60409461039c865192836102d1565b819584835260208093019160a080960285019481861161000e57925b8584106103c85750505050505050565b868483031261000e5784879184516103df81610254565b6103e887610374565b8152828701356103f78161034b565b83820152858701358682015260608088013590820152608080880135908201528152019301926103b8565b9080601f8301121561000e5781602061043d93359101610381565b90565b92919261044c82610324565b60409461045b865192836102d1565b819584835260208093019160c080960285019481861161000e57925b8584106104875750505050505050565b868483031261000e57848791845161049e8161027d565b6104a787610374565b8152828701356104b68161034b565b838201528587013586820152606080880135908201526080808801359082015260a080880135906104e68261034b565b820152815201930192610477565b9080601f8301121561000e5781602061043d93359101610440565b6004111561000e57565b35906103228261050f565b9190916101608184031261000e5761053a6102f4565b9261054482610369565b845261055260208301610369565b602085015267ffffffffffffffff90604083013582811161000e5781610579918501610422565b6040860152606083013591821161000e576105959183016104f4565b60608401526105a660808201610519565b608084015260a081013560a084015260c081013560c084015260e081013560e0840152610100808201359084015261012080820135908401526101408091013590830152565b35906effffffffffffffffffffffffffffff8216820361000e57565b92919267ffffffffffffffff8211610650575b604051916106336020601f19601f84011601846102d1565b82948184528183011161000e578281602093846000960137010152565b610658610224565b61061b565b9080601f8301121561000e5781602061043d93359101610608565b91909160a08184031261000e5761068d610315565b9267ffffffffffffffff823581811161000e57826106ac918501610524565b85526106ba602084016105ec565b60208601526106cb604084016105ec565b6040860152606083013581811161000e57826106e891850161065d565b6060860152608083013590811161000e57610703920161065d565b6080830152565b9080601f8301121561000e5781359061072282610324565b9261073060405194856102d1565b828452602092838086019160051b8301019280841161000e57848301915b84831061075e5750505050505090565b823567ffffffffffffffff811161000e57869161078084848094890101610678565b81520192019161074e565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156107f657565b6103226107bc565b608090805161080c816107ec565b8352816001600160a01b03918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610860575050505090565b909192938260e0600192604088516108798382516107fe565b808501516001600160a01b031660a0840152015160c082015201950193929101610852565b50606060031936011261000e5767ffffffffffffffff60043581811161000e576108cc90369060040161070a565b9060243581811161000e576108e590369060040161078b565b60443592831161000e5761092b9361091161090761091795369060040161078b565b9490933691611bff565b90613e21565b604051918291602083526020830190610840565b0390f35b503461000e57600060031936011261000e57610949615017565b3360005260016020526020604060002060018154018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b503461000e5760031960208136011261000e5760043567ffffffffffffffff811161000e576101608160040192823603011261000e576109d38261152d565b916109e06024830161152d565b906109ee6044840182611cfc565b6064850192916109fe8484611d50565b92909360848801610a0e90611dae565b95610a1891611d50565b969050610a236102f4565b6001600160a01b0390991689526001600160a01b031660208901523690610a4992610381565b60408701523690610a5992610440565b6060850152610a6b9060808501611db8565b60a482013560a084015260c482013560c084015260e482013560e08401526101048201356101008401526101248201356101208401526101408301526101440135610ab59161268a565b604051908152602090f35b9092916040820191604081528451809352606081019260208096019060005b818110610af95750505061043d9394818403910152610840565b8251151586529487019491870191600101610adf565b5060e060031936011261000e5767ffffffffffffffff60043581811161000e57610b3d90369060040161070a565b60243582811161000e57610b5590369060040161078b565b909160443584811161000e57610b6f90369060040161078b565b9060643595861161000e57610b8b610ba496369060040161078b565b929091610b9661035c565b9560c4359760843596611cc2565b9061092b60405192839283610ac0565b602060031982011261000e576004359067ffffffffffffffff821161000e57610bdf9160040161078b565b9091565b503461000e57610bf236610bb4565b610bfa615017565b60005b818110610c105760405160018152602090f35b80610c1e6001928486613f13565b610c2881806146ae565b610c318161152d565b91610c44610c3f3684610524565b614fa9565b91610c59836000526002602052604060002090565b610c6381856155a2565b50610c76610c72825460ff1690565b1590565b610c86575b505050505001610bfd565b7ffde361574a066b44b3b5fe98a87108b7565e327327954c4faeea56a4e6491a0a92610d2592610d01610d0793610cd6610ccf610cc86020968781019061158b565b3691610608565b898b615303565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b0161152d565b6040519384526001600160a01b039081169416929081906020820190565b0390a33880808080610c7b565b50604060031936011261000e5767ffffffffffffffff60043581811161000e57610d6090369060040161078b565b60249291923591821161000e5761092b92610d8d610d8561091794369060040161078b565b939092614750565b60405190610d9a82610299565b60008252613e21565b5060031960408136011261000e576004359067ffffffffffffffff821161000e57604090823603011261000e57610dfd610de16020926004016146e1565b60405190610dee82610299565b600082523391602435916141fd565b6040519015158152f35b5060031960808136011261000e576004359067ffffffffffffffff9081831161000e5760a090833603011261000e5760243590811161000e5761092b91610e55610e9692369060040161078b565b90606435610e628161034b565b6001600160a01b038116610ea85750610e90610e8433945b3690600401610678565b91604435933691611bff565b906141fd565b60405190151581529081906020820190565b610e84610e909194610e7a565b5060a060031936011261000e5767ffffffffffffffff60043581811161000e57610ee390369060040161078b565b9060243583811161000e57610efc90369060040161078b565b91909260443594851161000e57610f25610f1d610ba496369060040161078b565b929093614750565b9160405193610f3385610299565b6000855260843595339560643595612a0b565b503461000e57602060031936011261000e576020610f83600435610f698161034b565b6001600160a01b0316600052600160205260406000205490565b604051908152f35b503461000e57600060031936011261000e57610ff3610fa86127b4565b60405190610fb5826102b5565b600382527f312e3100000000000000000000000000000000000000000000000000000000006020830152604051928392606084526060840190610148565b9060208301526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660408301520390f35b5060031960208136011261000e5760043567ffffffffffffffff811161000e576102408160040192823603011261000e5761012435908160021c926001841193341585036112f85784936003821160028314916110d183600286117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe870102018815926001820185028460011b880103998a92600360a088026024013593168a6115dc565b6110e38260051b6101c40135986107ec565b156111b5575050506111036110f78261152d565b6001600160a01b031690565b6001600160a01b0390811660248401351761118b5761115f60449461115a6111759761116b9461113560a4890161152d565b9060648901946111448661152d565b9060e48b01359360c48c01359333931691611dcf565b61152d565b91610204840190611537565b93909201356119df565b61117f6001600055565b60405160018152602090f35b60046040517f6ab37ce7000000000000000000000000000000000000000000000000000000008152fd5b9194509161121e6110f7606461122396611228996111d1611514565b8a819b996111df839b6107ec565b1561122d5750610d01916111f560a4850161152d565b61120086860161152d565b9060e48601359160c4870135916001600160a01b03339216906120c8565b611ac5565b6122c4565b611175565b611236816107ec565b6003810361127d57506112789161124f60a4850161152d565b61125a86860161152d565b9060e48601359160c4870135916001600160a01b03339216906121be565b610d01565b806112896004926107ec565b036112c3576112789161129b8861152d565b6112a686860161152d565b6044860135916001600160a01b03602488013592169033906120c8565b611278916112d08861152d565b6112db86860161152d565b6044860135916001600160a01b03602488013592169033906121be565b6040517fa61be9f0000000000000000000000000000000000000000000000000000000008152346004820152602490fd5b0390fd5b503461000e5761133c36610bb4565b611344615017565b60005b81811061135a5760405160018152602090f35b611365818385614fe2565b61136e8161152d565b60209061137c82840161152d565b6001600160a01b0391828116938433141580611508575b6114de576040956113a681880182611cfc565b6060808401926113b68486611d50565b90916080948a8689016113c890611dae565b976113d3908a611d50565b9a90506113de6102f4565b6001600160a01b03909c168c526001600160a01b03909116908b0152369061140592610381565b8c890152369061141492610440565b9086015284019061142491611db8565b60a0808201359084015260c0808201359084015260e08082013590840152610100808201359084015261012080820135908401526101409182840152013561146b9161268a565b93611480856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000166101001790555193845216917f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d90602090a3600101611347565b60046040517f80ec7374000000000000000000000000000000000000000000000000000000008152fd5b50838316331415611393565b60405190611521826102b5565b60208083523683820137565b3561043d8161034b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e57602001918160061b3603831361000e57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e5760200191813603831361000e57565b9591906115e7615008565b6115fb610140880135610120890135615296565b50611604611927565b611622611615610200890189611537565b6101e08a013591506118f6565b7f00000000000000000000000000000000000000000000000000000000000000006080528160a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019384526024906102e00137610160928460a0528560c052600060e05260005b8394610204358210156116fb5790604060a0600193602090818560061b6102840161010037838560061b6102840161012037019660e0608020885201968888528960c08201526101008360061b610284019101370193929361169e565b5090929350969590966001610204350160051b610160206060525b83610264358210156117495790604060a060019301958787528860c08201526101008360061b6102840191013701611716565b505093509490506103229391507f00000000000000000000000000000000000000000000000000000000000000006080528260a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022092836102643560051b0152606060c46102406102643560051b01376118ee610cc8608435936117f1856001600160a01b03166000526001602052604060002090565b547f00000000000000000000000000000000000000000000000000000000000000006080526040608460a03760605161010052846101205260a0610144610140376101e0526101809485608020956102643560051b0190868252336101a06102643560051b015260806101c06102643560051b01526101206101e06102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a4359260a061026435026101e00190a360006060526118e56060820161115a6118bf8261152d565b966118cc6080860161152d565b906001600160a01b03809916906101608701358b61569d565b9581019061158b565b9216906147dc565b106118fd57565b60046040517f466aa616000000000000000000000000000000000000000000000000000000008152fd5b601861012435106102643560061b61026001610244351461024061022435146020600435141616161561195657565b60046040517f39f3e3fd000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90156119b95790565b61043d611980565b91908110156119d2575b60061b0190565b6119da611980565b6119cb565b919234936000915b808310611a4257505050828211611a185781611a0291611e97565b808211611a0d575050565b610322910333611e97565b60046040517f1a783b8d000000000000000000000000000000000000000000000000000000008152fd5b909194611a508683856119c1565b90813590808211611a1857611a748260206001950135611a6f8161034b565b611e97565b03950191906119e7565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818110611ab9570390565b611ac1611a7e565b0390565b90939291908115611b85579333611ade60a0830161152d565b60e08301359260c08101355b61118b578460051b6101e40335946102008201611b078184611537565b93905060005b848110611b24575050505050956103229596611f2c565b8989858e611b3c85611b368989611537565b906119c1565b803592611b6a575b91611b649391611b5d6110f7602060019998960161152d565b908c611f2c565b01611b0d565b92909493919b8c611b7a91611aae565b9b9193949092611b44565b933394611b918261152d565b6040830135926020810135611aea565b81601f8201121561000e57803591611bb883610324565b92611bc660405194856102d1565b808452602092838086019260051b82010192831161000e578301905b828210611bf0575050505090565b81358152908301908301611be2565b909291611c0b84610324565b91604094611c1b865194856102d1565b839581855260208095019160051b83019380851161000e5783925b858410611c465750505050505050565b67ffffffffffffffff90843582811161000e5786019060a08285031261000e578451611c7181610254565b8235815289830135600281101561000e578a82015285830135868201526060808401359082015260808084013594851161000e57611cb3868c96879601611ba1565b90820152815201930192611c36565b90611cf090610bdf9a99989796959493986001600160a01b03811615600014611cf6575033985b3691611bff565b90612a0b565b98611ce9565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e576020019160a082023603831361000e57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e576020019160c082023603831361000e57565b600411156107f657565b3561043d8161050f565b6004821015611dc45752565b611dcc6107bc565b52565b949290959391841515600014611e3b5761032296604051967f4ce34aa2000000000000000000000000000000000000000000000000000000008852602060048901526001602489015260448801526064870152608486015260a485015260c484015260e4830152612451565b9291946002919450611e4c816107ec565b03611e8b57600103611e61576103229361504d565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b9291906103229461515b565b90611ea181611efb565b600080808084865af115611eb3575050565b60449250611ebf612895565b6001600160a01b03604051927f470c7c1d0000000000000000000000000000000000000000000000000000000084521660048301526024820152fd5b15611f0257565b60046040517f91b3e514000000000000000000000000000000000000000000000000000000008152fd5b929193949094611f3b83611efb565b611f4581836122b1565b806120ba575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611fb4575b505050505050604052606052565b80863b151516611fa657908795969115611ff457602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b1561202e57506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d61206d575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c9080600302918082116120a1575b505060205a9101106120985785612034565b833d81803e3d90fd5b8080600392028380020360091c92030201018680612086565b9061032295929493916125c0565b959092949391936120d981836122b1565b806120f0575050600103611e61576103229361504d565b9060649593916000979593975060208251146000146121ab5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261214b565b9590919293946121cd86611efb565b6121d781836122b1565b806121e75750506103229461515b565b906064959694939291602082511460001461229e5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261223e565b906020820151036122bf5750565b610322905b60408082510361244d57602082015160c06064840151026044019180519260206001600160a01b036000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528684527f000000000000000000000000000000000000000000000000000000000000000086526055600b201696855281805284880182885af190519015612402577fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa2000000000000000000000000000000000000000000000000000000009116036123c05750505060209052565b517f1cf99b2600000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03919091166024820152604490fd5b611329848361240f612895565b517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201529081906024820190565b5050565b6040519160206001600160a01b036101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528685527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169660405282805282875af190519015612574577fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa200000000000000000000000000000000000000000000000000000000911603612530575050565b6040517f1cf99b2600000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03919091166024820152604490fd5b61132983612580612895565b6040517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201529081906024820190565b9060649492939160208251146000146126775760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c08582016001815101809152612615565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106127915750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a831061276d575050505050601f198660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b838082601f19600194510180519089815260e0812087525201920192019190612715565b8082601f19600194510180519088815260c08120875252019201920191906126cd565b467f0000000000000000000000000000000000000000000000000000000000000000036127ff577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f000000000000000000000000000000000000000000000000000000000000000082527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a0815261288f8161027d565b51902090565b3d61289c57565b601f3d0160051c60405160051c9080600302918082116128cf575b505060205a9101106128c557565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806128b7565b919082604091031261000e576040516040810181811067ffffffffffffffff821117612922575b6040526020808294803584520135910152565b61292a610224565b61290f565b92919261293b82610324565b60409261294a845192836102d1565b819581835260208093019160061b84019381851161000e57915b84831061297357505050505050565b83869161298084866128e8565b815201920191612964565b9291909261299884610324565b916129a660405193846102d1565b829480845260208094019060051b83019282841161000e5780915b8483106129d057505050505050565b823567ffffffffffffffff811161000e57820184601f8201121561000e578691612a00868385809535910161292f565b8152019201916129c1565b96989792612a268a612a359695612a2d95949998998b612c40565b369161298b565b93369161298b565b908251825191612a4d612a48848461314b565b61366d565b9760009586915b848310612b47575050506000935b838510612abf57505050505080612ab4575b50825115612a8a5782612a8691613b15565b9190565b60046040517fd5da9a1b000000000000000000000000000000000000000000000000000000008152fd5b835103835238612a74565b909192939488612ada84612ad38986612c1e565b518a613745565b8051608001516001600160a01b03166001600160a01b03612b086110f760208501516001600160a01b031690565b911603612b225750506001809101955b0193929190612a62565b8791612b4191612b3a85896001979c01038093612c1e565b528b612c1e565b50612b18565b9091968a612b6583612b5e8b879b98999a9b612c1e565b518c6136c9565b8051608001516001600160a01b03166001600160a01b03612b936110f760208501516001600160a01b031690565b911603612bb05750506001809101975b0191909594939295612a54565b8991612bcd91612bc6856001969d038093612c1e565b528d612c1e565b50612ba3565b90612bdd82610324565b612bea60405191826102d1565b828152601f19612bfa8294610324565b0190602036910137565b602090805115612c12570190565b612c1a611980565b0190565b6020918151811015612c33575b60051b010190565b612c3b611980565b612c2b565b93929091612c4c615008565b845192612c5884612bd3565b9160008352601d604560003560e01c061160011b9060005b868110612d30575050600314612d0657612c8a9086613266565b60005b838110612c9c57505050509050565b80612ca960019284612c1e565b5115612d0157612cfb612cbc8289612c1e565b5151612cc88386612c1e565b519086612cdc82516001600160a01b031690565b60208301516001600160a01b03169060606040850151940151946145e5565b01612c8d565b612cfb565b60046040517f12d3f5a3000000000000000000000000000000000000000000000000000000008152fd5b612d3a818a612c1e565b51918015612ebf57612d4d868685614cb3565b9290916001850189528215612eab57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91612d89868b612c1e565b52019380519260a084015193604060c08201519101518051908560005b838110612e405750505050606080935101519485519560005b878110612dd85750505050505050506001905b01612c70565b808760a0612de860019486612c1e565b5188612e2489898d6080860197612e01895187836131fa565b918701958651908a518214600014612e30575050508085525b80885284516131a0565b90520151905201612dbf565b612e39926131fa565b8552612e1a565b612e4a8184612c1e565b519b8c5115179b86868b60808401938451612e669085896131fa565b60608192019586519881518a1460001499612e919760019b612e9b575050508187525b52845161315f565b9052018690612da6565b612ea4926131fa565b8752612e89565b509360019392506000915060200152612dd2565b91906000602060019301528181018652612dd2565b612edc615008565b805192612ee884612bd3565b92600091828552601d6045843560e01c061160011b90835b878110612f90575050600314612d0657612f1a9083613266565b838110612f275750505050565b80612f3460019285612c1e565b5115612f8b57612f85612f478285612c1e565b5151612f538387612c1e565b5190612f6681516001600160a01b031690565b60208201516001600160a01b0316906060604084015193015193614513565b01612f1a565b612f85565b612f9a8187612c1e565b51918581156130fb5750612faf888685614ee0565b929091600185018b528883156130e95750907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91612fed868d612c1e565b52019380519260a084015191604060c0860151950151805190858c5b83811061308f5750505050606090510151938451948a5b86811061303857505050505050506001905b01612f00565b8061304560019284612c1e565b5160a0608082019189613083888b61305f87518d866131fa565b60608601948d8651908a518214600014612e305750505080855280885284516131a0565b90520151905201613020565b6130998184612c1e565b519b8c5115179b868a89608084019384516130b59085896131fa565b60608192019586519881518a14600014996130df9760019b612e9b5750505081875252845161315f565b9052018690613009565b92505093600193925060200152613032565b6020600193929401528181018852613032565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482118115151661313f570290565b613147611a7e565b0290565b81198111613157570190565b612c1a611a7e565b909283820361316e5750505090565b82939161318a613196946131909303954203918287039061310e565b9261310e565b9061314b565b9081049015150290565b90928382036131af5750505090565b926131906131cd9261318a856001969703964203918288039061310e565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b9190918281146132435782818309613219576132159161310e565b0490565b7fc63cf0890000000000000000000000000000000000000000000000000000000060005260046000fd5b50905090565b600211156107f657565b5161043d816107ec565b611dcc826107ec565b815181519260005b8281106133a45750505060005b82811061328757505050565b6132918183612c1e565b516132c56132b160208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b1561339b5751606081018051519060005b828110613354575050506040809101908151519160005b83811061330257505050506001905b0161327b565b61331f613319613313838551612c1e565b51613253565b60031090565b61332b576001016132ed565b600483517fa6cfc673000000000000000000000000000000000000000000000000000000008152fd5b613365613319613313838551612c1e565b613371576001016132d6565b60046040517fff75a340000000000000000000000000000000000000000000000000000000008152fd5b506001906132fc565b6133ae8183612c1e565b5180519086821015613565576020916133e56132b1846133ce848b612c1e565b5101516effffffffffffffffffffffffffffff1690565b1561355a576133f49087612c1e565b515191604092838301519183015161340b81613249565b61341481613249565b6134e55783015180518210156134bc579061342e91612c1e565b5191600383519361343e856107ec565b84906134558482019160048351981485039061325d565b606085015190525b11156134935750906001929181613478575b50505b0161326e565b61348c91608060608301519201519161358f565b388061346f565b600490517f94eb6af6000000000000000000000000000000000000000000000000000000008152fd5b600484517fbfb3f8ce000000000000000000000000000000000000000000000000000000008152fd5b929060608094015180518210156135315760039161350291612c1e565b5193845194613510866107ec565b85916135278583019260048451991486039061325d565b850151905261345d565b600483517f6088d7de000000000000000000000000000000000000000000000000000000008152fd5b505050600190613472565b60046040517f869586c4000000000000000000000000000000000000000000000000000000008152fd5b91909160009081526020808220928181019282825192600593841b0101915b8285106135eb575050505050036135c157565b60046040517f09bde339000000000000000000000000000000000000000000000000000000008152fd5b8451808711821b968752958418959095526040812094938301936135ae565b604051906060820182811067ffffffffffffffff821117613660575b8060405260408361363683610254565b6000928381528360808301528360a08301528360c08301528360e083015281528260208201520152565b613668610224565b613626565b9061367782610324565b61368460405191826102d1565b828152601f196136948294610324565b019060005b8281106136a557505050565b6020906136b061360a565b82828501015201613699565b906002821015611dc45752565b9092916136d461360a565b93805115613714576136f6926001600160a01b038693166080845101526137e9565b81516060810151156137055750565b60806000918260208601520152565b60246040517f375c24c100000000000000000000000000000000000000000000000000000000815260006004820152fd5b92919061375061360a565b9381511561378d576137639185916139aa565b60208301903382526040840152825190606082015115613781575050565b60009182608092520152565b60246040517f375c24c100000000000000000000000000000000000000000000000000000000815260016004820152fd5b507f7fda72790000000000000000000000000000000000000000000000000000000060005260046000fd5b92919260208201906020825151825181101561399d575b60051b82010151928351926020604085015181835101518151811015613990575b60051b01015160009460208697015161397a575b9061012060609260408b5193805185526020810151602086015201516040840152805160208c0152015160408a01522091805160051b01905b8181106138c1575050505060608293945101526138885750565b60011461389757610322611a7e565b7f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b60209095949501906020825151855181101561396d575b60051b85010151602081015115613964575160606020604083015181865101518151811015613957575b60051b01015196818801519081158a8381011060011b17179801966000828201522084149060408a0151610120820151149060208b015190511416161561394a575b9061386e565b6139526137be565b613944565b61395f6137be565b613902565b50949394613944565b6139756137be565b6138d8565b6060820180516000909152801597509550613835565b6139986137be565b613821565b6139a56137be565b613800565b9291602080830194855151918151831015613b08575b80600593841b8301015194606093828588510151818b5101518151811015613afb575b831b010151926000968188990151613ae6575b51948451865281850151828701526040850151604087015260a0809501519a608087019b8c52878720948051851b01905b818110613a4257505050505050508394955001526138885750565b83909a999a01908c848351518551811015613ad9575b871b850101518581015115613acf578a869151015181855101518151811015613ac2575b881b0101518a81019b8d8d518091019e8f9115911060011b17179c9b60009052888b822089149251910151141615613ab5575b90613a27565b613abd6137be565b613aaf565b613aca6137be565b613a7c565b5050999899613aaf565b613ae16137be565b613a58565b848701805160009091528015995097506139f6565b613b036137be565b6139e3565b613b106137be565b6139c0565b908151613b2181612bd3565b9260005b828110613be5575050503490613b39611514565b9080519060005b828110613b7457505050613b53906122c4565b80613b64575b5061043d6001600055565b613b6e9033611e97565b38613b59565b613b7e8183612c1e565b518051908151613b8d816107ec565b613b96816107ec565b15613bca575b8560019392826040613bbb6020613bc49601516001600160a01b031690565b91015191613cae565b01613b40565b9560608293920181815111611a185751900395909190613b9c565b613bef8183612c1e565b51613c0f6132b160208301516effffffffffffffffffffffffffffff1690565b15613ca557613c27613c218388612c1e565b60019052565b606080915101519081519160005b838110613c4a57505050506001905b01613b25565b82613c558284612c1e565b51015180613c665750600101613c35565b6040517fa5f542080000000000000000000000000000000000000000000000000000000081526004810187905260248101929092526044820152606490fd5b50600190613c44565b9290918351613cbc816107ec565b613cc5816107ec565b613d1a57505050613ce36110f760208301516001600160a01b031690565b6001600160a01b03604083015191161761118b57806060613d1160806103229401516001600160a01b031690565b91015190611e97565b90919260018151613d2a816107ec565b613d33816107ec565b03613d8357604081015161118b5761032293613d5960208301516001600160a01b031690565b906001600160a01b036060613d7860808601516001600160a01b031690565b940151931691611f2c565b9260028451613d91816107ec565b613d9a816107ec565b03613de05783613db760206103229601516001600160a01b031690565b60808201516001600160a01b0316926001600160a01b03606060408501519401519416916120c8565b83613df860206103229601516001600160a01b031690565b60808201516001600160a01b0316926001600160a01b03606060408501519401519416916121be565b90613e33909493929482519083612ed4565b613e3c8261366d565b9160009485915b808310613e705750505090613e619184829495613e65575b50613b15565b5090565b825103825238613e5b565b909195613e7e878385613f13565b613ea4613e8b8280611537565b90613e9b60209485810190611537565b92909189613f6c565b906001600160a01b03613ed96110f7613ec960808651016001600160a01b0390511690565b938501516001600160a01b031690565b911603613ef057506001809101965b019190613e43565b96613f0d8298600193830390613f06828a612c1e565b5287612c1e565b50613ee8565b9190811015613f54575b60051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18136030182121561000e570190565b613f5c611980565b613f1d565b61043d9036906128e8565b92909391613f7861360a565b948115801561415e575b61413457613f8e61360a565b613fa381613f9d36888861292f565b886139aa565b5191613fba87613fb436848661292f565b886137e9565b613fc48751613253565b835190613fd0826107ec565b613fd9826107ec565b613fe2816107ec565b148015906140fc575b80156140e9575b6140bf5761043d9561406f95608095896060948588019687518784510151106000146140825750505061403161402c8593614057936119b0565b613f61565b60208361404a8d828a5191510151900396845190612c1e565b5151015191015190612c1e565b5101528651015190525b01516001600160a01b031690565b6080835101906001600160a01b03169052565b86979694506140b1935061404a856140a161402c6020956040956119b0565b9451015188518551910397612c1e565b510152519086510152614061565b60046040517f09cfb455000000000000000000000000000000000000000000000000000000008152fd5b5060408751015160408401511415613ff2565b508651602001516001600160a01b03166001600160a01b0361412b6110f760208701516001600160a01b031690565b91161415613feb565b60046040517f98e9db6e000000000000000000000000000000000000000000000000000000008152fd5b508315613f82565b6040519061417382610254565b604051608083610160830167ffffffffffffffff8111848210176141f0575b6040526000808452806020850152606093846040820152848082015281848201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528252806020830152604082015282808201520152565b6141f8610224565b614192565b909291614208615017565b600260005561421784836148c0565b9490919260405195614228876102b5565b6001875260005b6020808210156142515790602091614245614166565b90828b0101520161422f565b505061428583959761428061429e9a61428e97998351156142ba575b60208401528251156142ad575b82613266565b612c04565b515195866142c7565b81516001600160a01b0316612cdc565b6142a86001600055565b600190565b6142b5611980565b61427a565b6142c2611980565b61426d565b939192909360a093848201519360c0830151966142e2611514565b96604092838601908151519160005b8381106143d7575050505034986060809601978851519860005b8a8110614338575050505050505050505050614326906122c4565b8061432e5750565b6103229033611e97565b614343818351612c1e565b51898101805161435d87878d8c60808801958651906144a1565b8092528783015190528151614371816107ec565b61437a816107ec565b15614397575b50906143918d8c6001943390613cae565b0161430b565b90919e9d8082116143ae579d9e9d039c908a614380565b600489517f1a783b8d000000000000000000000000000000000000000000000000000000008152fd5b6143e2818351612c1e565b5180516143ee816107ec565b6143f7816107ec565b15614441579061443b8d8f93868f8d6144236001988e936060870193845195608089019687519061446a565b9052528c610120613bbb82516001600160a01b031690565b016142f1565b600488517f12d3f5a3000000000000000000000000000000000000000000000000000000008152fd5b90939084810361448057505061043d93506131fa565b938361449561043d979661449b9496866131fa565b936131fa565b9061315f565b9093908481036144b757505061043d93506131fa565b938361449561043d97966144cc9496866131fa565b906131a0565b90815180825260208080930193019160005b8281106144f2575050505090565b909192938260a08261450760019489516107fe565b019501939291016144e4565b91939290936040805193608091828601918652602090600082880152838188015285518093528160a088019601936000915b84831061459a5750505050505091614595827f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31948380950360608501526001600160a01b038091169716956144d2565b0390a3565b90919293949684836001928a5180516145b2816107ec565b8252808401516001600160a01b031684830152858101518683015260609081015190820152019801959493019190614545565b92909493916040918251946080918287019187526001600160a01b0394856020921682890152838189015286518093528160a089019701936000915b84831061466a57505050505050828285949361459593867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f319896036060870152169716956144d2565b90919293949784836001928b518051614682816107ec565b8252808401518c1684830152858101518683015260609081015190820152019901959493019190614621565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea18136030182121561000e570190565b6146e9614166565b506147336147056146fa83806146ae565b92602081019061158b565b61471c6040519461471586610254565b3690610524565b845260016020850152600160408501523691610608565b606082015260405161474481610299565b60008152608082015290565b61475982610324565b9161476760405193846102d1565b808352601f1961477682610324565b0160005b8181106147c557505060005b8181106147935750505090565b806147a96147a46001938587613f13565b6146e1565b6147b38287612c1e565b526147be8186612c1e565b5001614786565b6020906147d0614166565b8282880101520161477a565b929190836000526002602052604060002091825460ff8160081c1661487b576effffffffffffffffffffffffffffff8160101c1661484a579460ff7101000000000000000000000000000001000195961615614839575b50505055565b61484292615303565b388080614833565b602486604051907fee9e0e630000000000000000000000000000000000000000000000000000000082526004820152fd5b602486604051907f1a5155740000000000000000000000000000000000000000000000000000000082526004820152fd5b90805b6148b7575090565b809106806148af565b90918151926148db610c7260a086015160c087015190615296565b614ca7576148fe6132b160208501516effffffffffffffffffffffffffffff1690565b9361491e6132b160408601516effffffffffffffffffffffffffffff1690565b948581118015614c9f575b614c755785811080614c5d575b614c335761498261494683614fa9565b9360e0840151608085015161495a81611da4565b85516001600160a01b0316918761497b60208901516001600160a01b031690565b948b615cc1565b614996836000526002602052604060002090565b916149a4610c7284866155a2565b614c23578254958460ff881615614bfc575b5050506effffffffffffffffffffffffffffff90818660101c169560881c96871515600014614b7f5760018103614b4757505085945b856149f7888361314b565b11614b3d575b86614a079161314b565b8082871183831117614ad6575b5090614a8f818493614a4e614ad19660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b84547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff16911660101b70ffffffffffffffffffffffffffffff000016178355565b815470ffffffffffffffffffffffffffffffffff1690861660881b7fffffffffffffffffffffffffffffff000000000000000000000000000000000016179055565b929190565b9690614ae987614aef92989594986148ac565b826148ac565b80150180809204970492049480861181841117614b0e57909138614a14565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80860396506149fd565b959096868103614b58575b506149ec565b614b7281614b6c89614b78959b9a9b61310e565b9861310e565b9761310e565b9438614b52565b9550955090614ad191614bb78260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b81547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff1687821660101b70ffffffffffffffffffffffffffffff000016178255614a8f565b6060614c12614c1b94516001600160a01b031690565b92015191615303565b3880846149b6565b5050509150915090600090600090565b60046040517fa11b63ff000000000000000000000000000000000000000000000000000000008152fd5b5060016080830151614c6e81611da4565b1615614936565b60046040517f5a052b32000000000000000000000000000000000000000000000000000000008152fd5b508015614929565b50600092508291508190565b919290928251614ccf610c7260a083015160c0840151906152df565b614ed057614cf26132b160208601516effffffffffffffffffffffffffffff1690565b614d116132b160408701516effffffffffffffffffffffffffffff1690565b958682118015614ec8575b614c755786821080614eb0575b614c3357614d7d90614d3a84614fa9565b9460e0850151608086015190614d4f82611da4565b87614d6188516001600160a01b031690565b93614d7660208a01516001600160a01b031690565b958c615da2565b614d91836000526002602052604060002090565b91614d9f610c728486615645565b614c23578254958460ff881615614e92575b5050506effffffffffffffffffffffffffffff90818660101c169560881c96871515600014614b7f5760018103614e6657505085945b85614df2888361314b565b11614e5c575b86614e029161314b565b8082871183821117614e48575090614a8f818493614a4e614ad19660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b969050614aef614ae98789989594986148ac565b8086039650614df8565b959096868103614e77575b50614de7565b614b7281614b6c89614e8b959b9a9b61310e565b9438614e71565b6060614c12614ea894516001600160a01b031690565b388084614db1565b5060016080840151614ec181611da4565b1615614d29565b508115614d1c565b5050915050600090600090600090565b919290928251614efc610c7260a083015160c084015190615296565b614ed057614f1f6132b160208601516effffffffffffffffffffffffffffff1690565b614f3e6132b160408701516effffffffffffffffffffffffffffff1690565b958682118015614fa1575b614c755786821080614f89575b614c3357614f6790614d3a84614fa9565b614f7b836000526002602052604060002090565b91614d9f610c7284866155a2565b5060016080840151614f9a81611da4565b1615614f56565b508115614f49565b61043d90614fc2606082015151610140830151906118f6565b80516001600160a01b03166000908152600160205260409020549061268a565b909161043d92811015614ffb575b60051b8101906146ae565b615003611980565b614ff0565b615010615017565b6002600055565b60016000540361502357565b60046040517f7fa8a987000000000000000000000000000000000000000000000000000000008152fd5b9092813b1561512d57604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156150a65750505050604052606052565b8593943d6150e9575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211615114575b505060205a91011061209857856150af565b8080600392028380020360091c92030201018680615102565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b1561526857604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af1156151d857505050505060805260a05260c052604052606052565b89949550883d61521b575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c90806003029180821161524f575b505060205a91011061524657866151e3565b843d81803e3d90fd5b8080600392028380020360091c92030201018780615234565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b42109081156152d4575b506152aa57600190565b60046040517f6f7eac26000000000000000000000000000000000000000000000000000000008152fd5b9050421015386152a0565b42109081156152f8575b506152f357600190565b600090565b9050421015386152e9565b9091336001600160a01b0383161461559d5761531d6127b4565b926000937f190100000000000000000000000000000000000000000000000000000000000085526002526022526042832090836022528380528392815191601f198101805184604103918860018411938415615532575b508514851515169788156153c3575b5050505050505050156153935750565b60049061539e612895565b7f4f7fb80d000000000000000000000000000000000000000000000000000000008152fd5b909192939495969750604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8501937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0855196019660208b60648a519b7f1626ba7e000000000000000000000000000000000000000000000000000000009d8e8b528c520188845afa998a615469575b505050505252523880808080808080615383565b8b51036154765780615455565b908a913b61550a576154e257640101000000821a156154b757807f815e1d640000000000000000000000000000000000000000000000000000000060049252fd5b6024917f1f003d0a000000000000000000000000000000000000000000000000000000008252600452fd5b807f8baa579f0000000000000000000000000000000000000000000000000000000060049252fd5b6004827f4f7fb80d000000000000000000000000000000000000000000000000000000008152fd5b9850506040840180519060608601518b1a99615569575b89865288835260208b60808560015afa5083835287865252885138615374565b9850601b8160ff1c01987f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152615549565b505050565b905460ff8160081c16615614576effffffffffffffffffffffffffffff8160101c1690816155d3575b505050600190565b60881c11156155e35780806155cb565b602490604051907f10fda3e10000000000000000000000000000000000000000000000000000000082526004820152fd5b602482604051907f1a5155740000000000000000000000000000000000000000000000000000000082526004820152fd5b906000905460ff8160081c16615694576effffffffffffffffffffffffffffff8160101c16908161567a575b50505050600190565b60881c111561568a578080615671565b6155e35750600090565b50905050600090565b90929160019060048110156156fd575b11806156ea575b806156d7575b6156c5575b50505050565b6156ce9361570a565b388080806156bf565b506001600160a01b0382163314156156ba565b506001600160a01b0384163314156156b4565b6157056107bc565b6156ad565b6000919290829161032295604051906001600160a01b0360208301937f0e1d31dc00000000000000000000000000000000000000000000000000000000855288602485015233604485015216606483015260848201526084815261576d8161027d565b51915afa615e78565b90815180825260208080930193019160005b828110615796575050505090565b909192938260a0600192875180516157ad816107ec565b8252808401516001600160a01b03168483015260408082015190830152606080820151908301526080908101519082015201950193929101615788565b90815180825260208080930193019160005b82811061580a575050505090565b909192938260c060019287518051615821816107ec565b8252808401516001600160a01b039081168584015260408083015190840152606080830151908401526080808301519084015260a0918201511690820152019501939291016157fc565b906004821015611dc45752565b6060519081815260208091019160809160005b828110615899575050505090565b83518552938101939281019260010161588b565b90815180825260208080930193019160005b8281106158cd575050505090565b8351855293810193928101926001016158bf565b90815180825260208092019182818360051b85019501936000915b84831061590c5750505050505090565b909192939495848061595e83856001950387528a518051825261593584820151858401906136bc565b60408082015190830152606080820151908301526080809101519160a0809282015201906158ad565b98019301930191949392906158fc565b92615b02906001600160a01b0361043d9694615b0f94875216602086015260a06040860152805160a080870152610140906159b482880182516001600160a01b03169052565b6080615af1615a286159f38a6159dc6020870151610160809301906001600160a01b03169052565b6040860151906101808d01526102a08c0190615776565b60608501517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec08c8303016101a08d01526157ea565b615a3a838501516101c08c019061586b565b60a08401516101e08b015260c08401516102008b015260e08401516102208b015261010094858501516102408c015261012094858101516102608d015201516102808b0152615aa1602087015160c08c01906effffffffffffffffffffffffffffff169052565b60408601516effffffffffffffffffffffffffffff1660e08b015260608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6095868c840301908c0152610148565b930151918784030190870152610148565b8381036060850152615878565b9160808184039101526158e1565b939061043d95936001600160a01b03615b0f94615cb393885216602087015260a06040870152805160a08088015261014090615b6482890182516001600160a01b03169052565b6080615ca2615bd8615ba38b6020860151615b8d61016091828401906001600160a01b03169052565b61018060408801519201526102a08d0190615776565b60608501518c82037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0016101a08e01526157ea565b615bea838501516101c08d019061586b565b60a08401516101e08c015260c08401516102008c015260e08401516102208c015261010094858501516102408d0152610120948c6102608783015191015201516102808c0152615c52602087015160c08d01906effffffffffffffffffffffffffffff169052565b60408601516effffffffffffffffffffffffffffff1660e08c015260608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6095868d840301908d0152610148565b930151918884030190880152610148565b9084820360608601526158ad565b909591929493600190615cd381611da4565b1180615d8f575b80615d7c575b615ced575b505050505050565b6080810151511580615d73575b15615d155750615d0a945061570a565b388080808080615ce5565b6000935083929450615d6061576d615d6e9760405192839160208301957f33131570000000000000000000000000000000000000000000000000000000008752338b6024860161596e565b03601f1981018352826102d1565b615d0a565b50855115615cfa565b506001600160a01b038416331415615ce0565b506001600160a01b038216331415615cda565b919692939594600190615db481611da4565b1180615e65575b80615e52575b615dcf575b50505050505050565b6080820151511580615e49575b15615df9575050615ded945061570a565b38808080808080615dc6565b600094508493955061576d615e4497615d6060405193849260208401967f33131570000000000000000000000000000000000000000000000000000000008852338c60248701615b1d565b615ded565b50805115615ddc565b506001600160a01b038516331415615dc1565b506001600160a01b038316331415615dbb565b15615f0f577f0e1d31dc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000600060203d14615f04575b1603615ed35750565b602490604051907ffb5014fc0000000000000000000000000000000000000000000000000000000082526004820152fd5b602081803e51615eca565b602490615f1a612895565b604051907ffb5014fc0000000000000000000000000000000000000000000000000000000082526004820152fdfea26469706673582212200d53e9d4f26a00cc6af37b012c26f8d770777dfea74c99c52ea7d855f909a12a64736f6c634300080e003300000000000000000000000000000000f9490004c11cef243f5400493c00ad63000000000000000000000000000000000000000000
-```
-
-3. Deploy the `Seaport 1.2` contract by submitting:
+2. Deploy the `Seaport 1.5` contract by submitting:
```
-cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e03087000000000000000000000000000000000000000007cfc129aa3aa9006db365ff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000068786101e060405234620000c3576200001f620000196200012e565b6200016b565b604051615c0190816200076e823960805181612622015260a05181612646015260c051816125ff015260e0518181816113b80152612430015261010051818181611257015261247f01526101205181818161145e01526124eb015261014051816125ac015261016051816125d3015261018051818181610f1f01528181612144015261226501526101a0518161559c01526101c05181818161218201526122a30152f35b600080fd5b50634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b03821117620000fb57604052565b62000105620000c8565b604052565b601f909101601f19168101906001600160401b03821190821017620000fb57604052565b62006858602081380391826040519384926200014b82856200010a565b833981010312620000c357516001600160a01b0381168103620000c35790565b60406004916200017a62000638565b610120526101005260e05260c05260a0526080528151806104e980820190828210600180881b038311176200029d575b6200636f833903906000f080156200028d575b6101a0524661014052620001fd60c0519060805160a0516040519360005281602052604052466060523060805260a0600020926040526000606052608052565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa9081156200027d575b60009162000247575b506101c052620002456001600055565b565b6200026d915060403d811162000275575b6200026481836200010a565b810190620002ba565b503862000235565b503d62000258565b62000287620002ad565b6200022c565b62000297620002ad565b620001bd565b620002a7620000c8565b620001aa565b506040513d6000823e3d90fd5b9190826040910312620000c3576020825192015190565b60405190620002e082620000df565b60038252565b6040519060a082016001600160401b038111838210176200030b575b604052606a8252565b62000315620000c8565b62000302565b6040519060c082016001600160401b03811183821017620003e5575b6040526084825263656e742960e01b60a0837f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e746966696560408201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c616464726573732072656369706960808201520152565b620003ef620000c8565b62000337565b6040519061010082016001600160401b0381118382101762000525575b60405260d482527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0837f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c4360408201527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608201527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808201527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08201527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08201520152565b6200052f620000c8565b62000412565b60405190608082016001600160401b03811183821017620005c1575b6040526052825271766572696679696e67436f6e74726163742960701b6060837f454950373132446f6d61696e28737472696e67206e616d652c737472696e672060208201527f76657273696f6e2c75696e7432353620636861696e49642c616464726573732060408201520152565b620005cb620000c8565b62000551565b9081519160005b838110620005ea575050016000815290565b8060208092840101518185015201620005d8565b620006296200062294936200062262000245946040519788956020870190620005d1565b90620005d1565b03601f1981018452836200010a565b6040516200064681620000df565b600781526614d9585c1bdc9d60ca1b6020918201527f32b5c112df393a49218d7552f96b2eeb829dfb4272f4f24eef510a586b85feef9162000687620002d1565b828101906218971960e91b825251902091620006a2620002e6565b818101927f4f666665724974656d2875696e7438206974656d547970652c6164647265737384527f20746f6b656e2c75696e74323536206964656e7469666965724f72437269746560408301527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606083015269656e64416d6f756e742960b01b6080830152620007326200031b565b926200076562000741620003f5565b936200074c62000535565b83815191012096815190209580518482012095620005fe565b80519101209056fe60806040526004361015610023575b361561001957600080fd5b610021614fef565b005b60003560e01c80156100eb57806306fdde031461016957806346423aa7146101605780635b34b9661461015757806379df72bd1461014e57806387201b4114610145578063881477321461013c578063a817440414610133578063a900866b1461012a578063b3a34c4c14610121578063e7acab2414610118578063ed98a5741461010f578063f07ec37314610106578063f2d12b12146100fd578063f47b7740146100f4578063fb0f3ee1146100eb5763fd9f1e100361000e576100e6610f50565b61000e565b506100e66101c8565b506100e6610ec8565b506100e6610df2565b506100e6610d8a565b506100e6610cc2565b506100e6610c05565b506100e6610b81565b506100e6610b17565b506100e6610a60565b506100e66108d6565b506100e66107c6565b506100e661059d565b506100e66104f5565b506100e6610474565b506100e661042e565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc906020828201126101c3576004359167ffffffffffffffff83116101c35782610240920301126101c35760040190565b600080fd5b506101d236610172565b6101243590600382169160021c91600183119234158403610420575b60038111907f0203020301010000000000000000000000000000000000000000000000000000811a9061024c8260a0850260240135887d010102030000000000000000000000000000000000000000000000000000851a888a61121a565b928060051b6101c4013596610260816106a8565b6102b3575050604435602435176102a55761028b9461027e916115b5565b61028661166d565b61569e565b6102956001600055565b60405160018152602090f35b0390f35b636ab37ce76000526004601cfd5b610286925061028b969161032a916102c96111a8565b9384836102d682956106a8565b6002810361032f5750610325918a6102f060a082016111bf565b6102fc606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611efe565b611738565b612105565b610338816106a8565b600381036103875750610325918a61035260a082016111bf565b61035e606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611fff565b806103936004926106a8565b036103dc57610325918a6103a6816111bf565b6103b2606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611efe565b610325918a6103ea816111bf565b6103f6606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611fff565b61042934611d42565b6101ee565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357602080526707536561706f727460475260606020f35b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35761052d614fd5565b3360005260016020526020604060002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43014060801c018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b50346101c3577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020813601126101c3576004359067ffffffffffffffff82116101c3576101609082360301126101c35761061263ffffffff6020921661014461060982600401611cd6565b91013590612423565b604051908152f35b9181601f840112156101c35782359167ffffffffffffffff83116101c3576020808501948460051b0101116101c357565b73ffffffffffffffffffffffffffffffffffffffff8116036101c357565b60a435906106768261064b565b565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156106b257565b610676610678565b60809080516106c8816106a8565b83528173ffffffffffffffffffffffffffffffffffffffff918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610729575050505090565b909192938260e0600192604088516107428382516106ba565b8085015173ffffffffffffffffffffffffffffffffffffffff1660a0840152015160c08201520195019392910161071b565b9092916040820191604081528451809352606081019260208096019060005b8181106107b0575050506107ad9394818403910152610709565b90565b8251151586529487019491870191600101610793565b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff6004358181116101c35761081290369060040161061a565b50506024358181116101c35761082c90369060040161061a565b50506044358181116101c35761084690369060040161061a565b50506064359081116101c35761086090369060040161061a565b505061087961086d610669565b60c43590608435611813565b906102a160405192839283610774565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101c3576004359067ffffffffffffffff82116101c3576108d29160040161061a565b9091565b50346101c3576108e536610889565b505060046108fb63ffffffff8235168201611aba565b90610904614fd5565b81519060005b82811061091d5760405160018152602090f35b8061092a600192866129f9565b51805184608082015161093c816129a5565b610945816129a5565b14610a4857805173ffffffffffffffffffffffffffffffffffffffff1661096b826147c6565b90610980826000526002602052604060002090565b61098a81846155c2565b5061099d610999825460ff1690565b1590565b6109ae575b50505050505b0161090a565b6109f4610a1f928460207ff280791efe782edcf06ce15c8f4dff17601db3b88eb3805a0db7d77faf757f04986060890151516101408a015103610a3b575b0151916151f2565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b610a2e60405192839283614e9e565b0390a138808080806109a2565b610a43614cb0565b6109ec565b50506109a8565b9060206107ad928181520190610709565b5060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35760043567ffffffffffffffff8082116101c357610aab368360040161061a565b50506024359081116101c3576102a191610b0391610acc368260040161061a565b5050610afb610ae463ffffffff809416600401615b97565b92610aed6110db565b926000845216600401611c52565b903392613bcc565b604051918291602083526020830190610709565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35773ffffffffffffffffffffffffffffffffffffffff600435610b688161064b565b1660005260036020526020604060002054604051908152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6040813601126101c3576004359067ffffffffffffffff82116101c35760409082360301126101c357610bfb610be363ffffffff602093166004016119cd565b610beb6110db565b9060008252339160243591613f74565b6040519015158152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6080813601126101c3576004359067ffffffffffffffff908183116101c35760a09083360301126101c3576024359081116101c3576102a191610cb091610c71368260040161061a565b5050610ca060643592610c838461064b565b610c9663ffffffff80921660040161186c565b9216600401611a2d565b9133811502019160443591613f74565b60405190151581529081906020820190565b5060a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600467ffffffffffffffff81358181116101c357610d0d3682850161061a565b5050602435908282116101c357610d263683860161061a565b50506044359283116101c357610d7b61087994610d453686830161061a565b5050610d5963ffffffff8094168201615b97565b92610d7381610d666110db565b9660008852168301611b44565b951601611b44565b608435933393606435936126d4565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c3576020610612600435610dcb8161064b565b73ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff600480358281116101c357610e3d3682840161061a565b5050602435908382116101c357610e563683850161061a565b50506044359384116101c3576102a193610eb0610ebc94610e793684830161061a565b5050610e9f610ea860643595610e8e8761064b565b63ffffffff92838092168501611bf5565b97168301611a2d565b931601611c52565b91338115020192613bcc565b60405191829182610a4f565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357610f006125a7565b606060005260205273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166040526303312e3260635260a06000f35b50346101c357610f5f36610889565b90610f68614fd5565b600091825b818110610f925783610f855760405160018152602090f35b610f8d614d6b565b610295565b80610fa06001928486614d10565b94610faa866111bf565b907f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d611075611006610fde60208b016111bf565b93610feb60808c01614d5e565b60048633148833141715911417179961014061060982611cd6565b9261104a61101e856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016610100179055565b60405193845273ffffffffffffffffffffffffffffffffffffffff9081169416929081906020820190565b0390a301610f6d565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519060a0820182811067ffffffffffffffff8211176110ce57604052565b6110d661107e565b604052565b604051906020820182811067ffffffffffffffff8211176110ce57604052565b604051906040820182811067ffffffffffffffff8211176110ce57604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176110ce57604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff811161119b575b01160190565b6111a361107e565b611195565b6111b06110fb565b90602082526020828136910137565b356107ad8161064b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101c3570180359067ffffffffffffffff82116101c3576020019181360383136101c357565b959392919094611228614fa7565b61123061155f565b6101643561014435428211154282111761154b57505061020435610264351061153d5793907f00000000000000000000000000000000000000000000000000000000000000006080528060a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019283526024906102e00137610160948360a0528460c052600060e05260009260005b83610204358210156113315790604060019261010060a060208560061b9a818c610284018537858c61028401610120376102a48c0135179d019860e06080208a5201988a8a528b60c08401526102840191013701969392966112d4565b5096509192979690976001610204350160051b610160206060525b836102643588101561138957906102a460a060019301958787528860c082015260408a60061b91610100836102840191013701351796019561134c565b50925095945095925073ffffffffffffffffffffffffffffffffffffffff91501161152f576107ad91611528917f00000000000000000000000000000000000000000000000000000000000000006080528060a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022090816102643560051b0152606060c46102406102643560051b013761036060843561145a8173ffffffffffffffffffffffffffffffffffffffff166000526001602052604060002090565b54967f00000000000000000000000000000000000000000000000000000000000000006080526040608460a037606051610100526101205260a0610144610140376101e09687526101809687608020976102643560051b0191888352336101a06102643560051b015260806101c06102643560051b0152610120826102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a06102643502938460a435940190a360006060526102643560051b01016040528101906111c9565b9083614371565b6339f3e3fd6000526004601cfd5b63466aa6166000526004601cfd5b6321ccfeb76000526020526040526044601cfd5b7401000000000000000000000000000000000000000060243560c4351760a43560843517171060186101243510166102643560061b61026001610244351461024061022435146020600435141616161561152f57565b608435916101043560e43560c4358315611627579461067695604051957f4ce34aa200000000000000000000000000000000000000000000000000000000875260206004880152600160248801526044870152606486015260848501523360a485015260c484015260e483015261223e565b925092806116366002926106a8565b0361166057928360016106769503611651575b503391615004565b61165a90611d31565b38611649565b91906106769333916150e3565b3460643560006102643560061b815b8181106116bd575050508181116116b0575b61169a81608435611d62565b8082116116a5575050565b610676910333611d62565b6116b8611d22565b61168e565b806102840135948086116116e657906116e08660409303966102a4830135611d62565b0161167c565b638ffff98084526004601cfd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190820391821161173057565b6106766116f3565b919082156117d95760843592610104353360c43560e4355b6117cc575b8360051b6101e40335936102643560061b9060005b82811061177f57505050956106769596611dae565b87876102848301358c856117ab575b918493916117a5936102a46040970135908a611dae565b0161176a565b9891816117bf60409695936117a595611723565b9a9193509193945061178e565b6117d4611d53565b611755565b3392606435608435602435604435611750565b60209067ffffffffffffffff8111611806575b60051b0190565b61180e61107e565b6117ff565b906108d2929163ffffffff9161182f8360043516600401611bf5565b926118408160243516600401611a2d565b6118606118538360443516600401611b44565b9260643516600401611b44565b923381150201946126d4565b90604051610200810160405260806118c68294604060208201602086013760a084018085526118a563ffffffff918284351684016118f5565b6118b68160608401351683016118cb565b60608601528382013516016118cb565b910152565b9060206040519263ffffffff813563ffffffe0601f82011692848401908737168452830101604052565b6118c660609161016081853763ffffffff611917816040840135168301611927565b604086015283820135160161197a565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160a0809402910185378086015b83811061196c5750505050604052565b84815293820193810161195c565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160c0809402910185378086015b8381106119bf5750505050604052565b8481529382019381016119af565b906040516102008101604052611a13819360a083018084526119f963ffffffff918284351684016118f5565b6001602085015260016040850152602082013516016118cb565b606082015260806040519160208301604052600083520152565b803591600592641fffffffe081851b16604080519060209384848401018252829663ffffffff809216845260005b858110611a6e5750505050505050909150565b8083888093850101351683018551908360a091828401895287608093848484018737820135160101908d60018884351601901b8851928184018a52833782015282828801015201611a5b565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611af55750505050505050565b808388809385010135168301611b34838851928984016101a085018b52611b2581848b81860135168501016118f5565b8452878a8201351601016118cb565b8382015282828701015201611ae5565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611b7f57505050505050565b80611b9587848180958801013516860101611ba1565b82828701015201611b70565b90813591604080519363ffffffff81168552602080641fffffffe08360051b168701019381643fffffffc0869460061b16910185378086015b828110611be75750505052565b848152938301938101611bda565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611c3057505050505050565b80611c468784818095880101351686010161186c565b82828701015201611c21565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611c8d5750505050505050565b808388809385010135168301611cc6838851928984018a52611cb782898184013516830101611ba1565b8452878a820135160101611ba1565b8382015282828701015201611c7d565b9060405161016081016040528092611d16610140918281853763ffffffff611d05816040840135168301611927565b60408601526060820135160161197a565b80606084015251910152565b50638ffff9806000526004601cfd5b6369f958276000526020526024601cfd5b63a61be9f06000526020526024601cfd5b50636ab37ce76000526004601cfd5b611d6b82611d99565b600080808085855af115611d7d575050565b611d85612681565b63bc806b966000526020526040526044601cfd5b15611da057565b6391b3e5146000526004601cfd5b929193949094611dbd83611d99565b611dc781836120f2565b80611ef0575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611e36575b505050505050604052606052565b80863b151516611e2857908795969115611e5b5786635f15d67287526020526024601cfd5b959192939515611e80575063988919238594526020526040526060526080526084601cfd5b3d611ea3575b5063f486bc87845260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c908060030291808211611ed7575b505060205a910110611ece5785611e86565b833d81803e3d90fd5b8080600392028380020360091c92030201018680611ebc565b906106769592949391612359565b919395909294611f0e81836120f2565b80611f375750508460016106769603611f28575b50615004565b611f3190611d31565b38611f22565b815160649693959394929190602003611fec5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c08682016001815101809152611f8c565b95909192939461200e86611d99565b61201881836120f2565b80612028575050610676946150e3565b90606495969493929160208251146000146120df5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261207f565b906020820151036121005750565b610676905b90604082510361223a5760208201519160c06064820151026044019260405193602073ffffffffffffffffffffffffffffffffffffffff6000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528584527f00000000000000000000000000000000000000000000000000000000000000006040526055600b2016976040528180526040860182895af190805191156122215750937f4ce34aa2000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000060209596160361221557505052565b61221e91612345565b52565b63d13d53d48691612230612681565b526020526024601cfd5b9050565b9060405190602073ffffffffffffffffffffffffffffffffffffffff6101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528785527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169560405282805282865af1908051911561233657507fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa20000000000000000000000000000000000000000000000000000000091160361232d575050565b61067691612345565b63d13d53d49150612230612681565b631cf99b266000526020526040526044601cfd5b9060649492939160208251146000146124105760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c085820160018151018091526123ae565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106125665750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a83106125245750505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b8380827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519089815260e08120875252019201920191906124ae565b80827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519088815260c0812087525201920192019190612466565b6000467f0000000000000000000000000000000000000000000000000000000000000000036125f557507f000000000000000000000000000000000000000000000000000000000000000090565b60405190608051907f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020527f0000000000000000000000000000000000000000000000000000000000000000604052466060523060805260a081209260405260605260805290565b3d61268857565b601f3d0160051c60405160051c9080600302918082116126bb575b505060205a9101106126b157565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806126a3565b9196948094966126e49284612a1b565b9186519581516126fc6126f7828a612efa565b61339b565b976000998a905b8282106128015750506000925b828410612753575050505050936127379394868297612748575b5081511561273b57613748565b9190565b6127436133fe565b613748565b82510382523861272a565b90919293998961276e83612767888f6129f9565b5189613478565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff6127c0602084015173ffffffffffffffffffffffffffffffffffffffff1690565b9116036127dc5750506001809101945b01929190999399612710565b86916127fb916127f485886001979b010380936129f9565b528c6129f9565b506127d0565b90949a8a61281e8a6128178986999798996129f9565b518a61340d565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff612870602084015173ffffffffffffffffffffffffffffffffffffffff1690565b91160361288e5750506001809101955b01909a949a93929193612703565b87916128ab916128a4856001969b0380936129f9565b528d6129f9565b50612880565b6128b96110ae565b90604051610160810181811067ffffffffffffffff821117612938575b604052600080825280602083015260609182604082015282808201528160808201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528452806020850152604084015280808401526080830152565b61294061107e565b6128d6565b61294d6110fb565b600181529060203681840137565b9061296d612968836117ec565b61111b565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061299b82946117ec565b0190602036910137565b600511156106b257565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020908051156129ed570190565b6129f56129af565b0190565b6020918151811015612a0e575b60051b010190565b612a166129af565b612a06565b90919392612a27614fb6565b6000357c40000000000000000000000000000000000000000000000000000000001692612a526128b1565b50825193612a5f8561295b565b96600180960160051b9560205b878110612b2f57505050907c4000000000000000000000000000000000000000000000000000000001612aa59214612b22575b8361301b565b60205b838110612ab55750505050565b806020918701518015612b1c57612b1690828601515185612aea825173ffffffffffffffffffffffffffffffffffffffff1690565b8287015173ffffffffffffffffffffffffffffffffffffffff165b906060604085015194015194614296565b01612aa8565b50612b16565b612b2a612ff8565b612a9f565b80870151928015612cbf57612b43846146cc565b95918d82949215612cab5790857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93920152019481519260a08401519360c081015160408201518051906004608080950151612b9e816129a5565b106000528960005b838110612c42575050505060608095510151958651958960005b888110612bdb57505050505050505050506020905b01612a6c565b87612be6828c6129f9565b5160a088820191612c2089898d612bff87518983612fc5565b908b86019889519089518214600014612c32575050508088525b8751612f53565b80945201908151905252018a90612bc0565b612c3b92612fc5565b8852612c19565b87612c4d82856129f9565b519e8f600051905110179e612c8f878d8a8401938c6060612c7087518984612fc5565b92019687519087518214600014612c9b575050508086525b8551612f07565b80925252018a90612ba6565b612ca492612fc5565b8652612c88565b505094506020809392506000910152612bd5565b92906000602080930152612bd5565b90919392612cda614fb6565b6000357c40000000000000000000000000000000000000000000000000000000001692612d056128b1565b50825193612d128561295b565b96600180960160051b9560205b878110612da857505050907c4000000000000000000000000000000000000000000000000000000001612d579214612b22578361301b565b60205b838110612d675750505050565b806020918701518015612da257612d9c90828601515185612aea825173ffffffffffffffffffffffffffffffffffffffff1690565b01612d5a565b50612d9c565b80870151928015612ed857612dbc84614478565b95918d82949215612ec45790857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93920152019481519260a08401519360c081015160408201518051906004608080950151612e17816129a5565b106000528960005b838110612e8a575050505060608095510151958651958960005b888110612e5457505050505050505050506020905b01612d1f565b87612e5f828c6129f9565b5160a088820191612e7889898d612bff87518983612fc5565b80945201908151905252018a90612e39565b87612e9582856129f9565b519e8f600051905110179e612eb8878d8a8401938c6060612c7087518984612fc5565b80925252018a90612e1f565b505094506020809392506000910152612e4e565b92906000602080930152612e4e565b8181029291811591840414171561173057565b9190820180921161173057565b929092838103612f175750505090565b612f2d83612f3393039342039182850390612ee7565b93612ee7565b8201809211612f46575b81049015150290565b612f4e6116f3565b612f3d565b919092838303612f635750505090565b600192612f7c83612f8293039342039182850390612ee7565b94612ee7565b8301809311612fb8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b612fc06116f3565b612f8c565b919091828114612ff25782818309612fe457612fe091612ee7565b0490565b63c63cf0896000526004601cfd5b50905090565b506312d3f5a36000526004601cfd5b600211156106b257565b516107ad816106a8565b815181519260005b8281106131245750505060005b82811061303c57505050565b61304681836129f9565b5161307a61306660208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b1561311b5751606081018051519060005b8281106130ed575050506040018051519060005b8281106130b3575050506001905b01613030565b806130d36130cd6130c760019486516129f9565b51613011565b60031090565b6130de575b0161309f565b6130e8818661321e565b6130d8565b806131016130cd6130c760019486516129f9565b61310c575b0161308b565b613116818761320a565b613106565b506001906130ad565b61312e81836129f9565b516131438151878110156131de575b866129f9565b51602090613165613066838301516effffffffffffffffffffffffffffff1690565b156131d357519060409081830151918401519263bfb3f8ce9185015161318a81613007565b61319381613007565b6131c0575b5081518310156131b75750916131b1916001949361323b565b01613023565b6000526004601cfd5b9050606091500151636088d7de38613198565b5050506001906131b1565b6131f460208401516131ef81613007565b6131f9565b61313d565b63133c37c66000526020526024601cfd5b63a8930e9a6000526020526040526044601cfd5b63d69293326000526020526040526044601cfd5b61221e826106a8565b90613245916129f9565b51805191613252836106a8565b60038311156132b157613292826004604060609501958651801515600014613298576132889087870151906080880151916132ce565b1460030390613232565b01519052565b5060808501515115613288576132ac6132bf565b613288565b6394eb6af66000526004601cfd5b506309bde3396000526004601cfd5b916000928352602090818420918082019181815191600592831b0101905b81841061330c5750505050036132ff5750565b6309bde33990526004601cfd5b8351808611821b958652948318949094526040862093928201926132ec565b6133336110ae565b906000825260006020830152600060408301526000606083015260006080830152565b604051906060820182811067ffffffffffffffff82111761338e575b604052600060408361338261332b565b81528260208201520152565b61339661107e565b613372565b906133a8612968836117ec565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06133d682946117ec565b019060005b8281106133e757505050565b6020906133f2613356565b828285010152016133db565b5063d5da9a1b6000526004601cfd5b909291613418613356565b93805115613465576134479273ffffffffffffffffffffffffffffffffffffffff8693166080845101526134f6565b81516060810151156134565750565b60806000918260208601520152565b63375c24c160005260006020526024601cfd5b929190613483613356565b938151156134c05761349691859161363d565b602083019033825260408401528251906060820151156134b4575050565b60009182608092520152565b63375c24c160005260016020526024601cfd5b50637fda72796000526004601cfd5b50634e487b7160005260116020526024601cfd5b9092919260009081928290828351905b8160051b8501811061353557505050505060608293945101526135265750565b600114611da0576106766134e2565b602090969596019060208251518451811015613630575b60051b840101518051906020845101516020604084015192015115825182101517613625579060209160051b0101519660609081890151998a81019a15908b1060011b171798976000828201528b5187156001146135d857502085189060408b0151610120820151189060208c015190511817176135cb575b90613506565b6135d36134d3565b6135c5565b929061012092949750806040915185526020810151602086015201516040840152805160208d0152015160408b015220926020850182811861361b575b506135c5565b8251905238613615565b5050509594956135c5565b6136386134d3565b61354c565b9092919260009081928291808051600590811b82015b80841061366f5750505050505060608293945101526135265750565b60209796978094019380855151875181101561373b575b841b87010151908086510151916060928284835101519201511582518210151761372f576000918391871b010151928301998a519b8c81019c15908d1060011b17179a99528b5188156001146136ef57505060a090208614613653576136ea6134d3565b613653565b8251815281830151818301526040808401519082015260808084015191015260a090912096508301848118613725575b50613653565b845190523861371f565b50505050969596613653565b6137436134d3565b613686565b9290918351916137578361295b565b946137606111a8565b9480519060005b8281106138ee5750505060005b8481106137ab57505050505061378990612105565b478061379b575b506107ad6001600055565b6137a59033611d62565b38613790565b6137b581836129f9565b516137d561306660208301516effffffffffffffffffffffffffffff1690565b156138d8576137ed6137e7838a6129f9565b60019052565b8051604081015180519060005b828110613873575050506060809101519081519160005b83811061383b5750505050906138356001928661382e84826129f9565b519161577d565b01613774565b80613848600192846129f9565b5160a085820191825180613862575b500151905201613811565b61386d90858c61397a565b38613857565b808b613881600193856129f9565b518a6080820151926060830192835161389f575b50505052016137fa565b6138d0926138ac91613968565b895173ffffffffffffffffffffffffffffffffffffffff166101208b015191613991565b8a8e38613895565b508060006138e86001938a6129f9565b52613835565b80613949896138ff600194866129f9565b51805190815161390e816106a8565b613917816106a8565b1561394f575b6040613940602083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015191613991565b01613767565b476060830151111561391d57613963611d22565b61391d565b919061397261332b565b506080830152565b63a5f542086000526020526040526060526064601cfd5b929190835161399f816106a8565b6139a8816106a8565b613a4b57505050806139f06139d7602061067694015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6040830151911617613a3e575b6060613a35608083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015190611d62565b613a46611d53565b613a12565b60018451613a58816106a8565b613a61816106a8565b03613ae15792610676936040820151613ad4575b602082015173ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff6060613ac9608086015173ffffffffffffffffffffffffffffffffffffffff1690565b940151931691611dae565b613adc611d53565b613a75565b60028451613aee816106a8565b613af7816106a8565b03613b645783613b21602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611efe565b83613b89602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611fff565b9193929081613bde9184519085612cce565b805160051b604001927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051907f4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e760209687835282a152855195613c438761339b565b9460009788915b818310613c78575050505092613c699386829697613c6d575b50613748565b5090565b825103825238613c63565b90919298613c9884613c8a818d6129f9565b518481519101519088613d26565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff613ce98584015173ffffffffffffffffffffffffffffffffffffffff1690565b911603613d0357506001809101935b019190989298613c4a565b93613d208295600193830390613d19828d6129f9565b528a6129f9565b50613cf8565b909192613d31613356565b938351158015613f13575b613f06575b613d49613356565b90613d5582828661363d565b81519460609384870193845115613eee575092859288836107ad9996613d828360809a97613e859c6134f6565b613d8c8351613011565b613d95816106a8565b885190613da1826106a8565b613daa826106a8565b60ff85519273ffffffffffffffffffffffffffffffffffffffff8c604080613dec6139d760208a015173ffffffffffffffffffffffffffffffffffffffff1690565b613e106139d7602086015173ffffffffffffffffffffffffffffffffffffffff1690565b189701519101511894169218161717613edf575b50835182518601511015613ea557505090602083613e59613e47613e66956129df565b5193518c5183015185519103976129f9565b51510151910151906129f9565b5101525b015173ffffffffffffffffffffffffffffffffffffffff1690565b60808351019073ffffffffffffffffffffffffffffffffffffffff169052565b8495939492509060206040613e5985613ec0613ed1966129df565b5194510151885185519103976129f9565b510152519086510152613e6a565b613ee890613f1c565b38613e24565b97505050505050506080600091826020850152015290565b613f0e613f2d565b613d41565b50805115613d3c565b63bced929d6000526020526024601cfd5b506398e9db6e6000526004601cfd5b613f446110fb565b90600182528160005b60209081811015613f6f57602091613f636128b1565b90828501015201613f4d565b505050565b9261400e613fda9261404695613fa460046080835101516005811015614055575b613f9e816129a5565b14614fc5565b613fec84613fb183614478565b9098829a9296613fbf613f3c565b96613fc9886129df565b52613fd3876129df565b508661301b565b613fe3856129df565b51519889614062565b614008613ff7612945565b9183614002846129df565b526129df565b5161577d565b815173ffffffffffffffffffffffffffffffffffffffff16602083015173ffffffffffffffffffffffffffffffffffffffff16612b05565b6140506001600055565b600190565b61405d610678565b613f95565b60a08082015160c083015197969095939161407b6111a8565b9689604086019384515190600095865b8c898d86841061417b5750505050505050506080926004848701516140af816129a5565b101661416e575b6060809501968751519760005b8981106140f257505050505050505050506140df919250612105565b47806140e85750565b6106769033611d62565b8061414e8c8f8b8b8b8f93614123908c8c61411060019c8e516129f9565b5196870195865195880195865190614224565b8092528b83015190528151614137816106a8565b614140816106a8565b15614154575b503390613991565b016140c3565b4710614161575b38614146565b614169611d22565b61415b565b614176612ff8565b6140b6565b99856141e29392869798999c6141bd6141978860019a516129f9565b519485516141a4816106a8565b15179e8d606087019384519560808901968751906141ed565b9052528c610120613940825173ffffffffffffffffffffffffffffffffffffffff1690565b01908d93929161408b565b9093908481036142035750506107ad9350612fc5565b93836142186107ad979661421e949686612fc5565b93612fc5565b90612f07565b90939084810361423a5750506107ad9350612fc5565b93836142186107ad979661424f949686612fc5565b90612f53565b90815180825260208080930193019160005b828110614275575050505090565b909192938260a08261428a60019489516106ba565b01950193929101614267565b929094939160409182519460809182870191875273ffffffffffffffffffffffffffffffffffffffff94856020921682890152838189015286518093528160a089019701936000915b84831061432d57505050505050828285949361432893867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31989603606087015216971695614255565b0390a3565b90919293949784836001928b518051614345816106a8565b8252808401518c16848301528581015186830152606090810151908201520199019594930191906142df565b9092916000938285526002602052604085209283549260ff8460081c16614453576effffffffffffffffffffffffffffff8460101c166144425760ff8416156143d8575b505071010000000000000000000000000000010001909255509091506106769050565b6143e46129688261115f565b92818452368282011161443e57926201000194926106769798602084614436957fffffffffffffffffffffffffffffff00000000000000000000000000000000009883870137840101526084356151f2565b9185946143b5565b8780fd5b5063ee9e0e6386526020526024601cfd5b50631a51557486526020526024601cfd5b90805b61446f575090565b80910680614467565b80519061449161099960a084015160c0850151906151dd565b6146bf576effffffffffffffffffffffffffffff9260209284848401511693856040850151169360808301600481516144c9816129a5565b6144d2816129a5565b1461468c5786158688111761467f575b51916144ed836129a5565b60018093161586881016614672575b614505846147c6565b9761451a896000526002602052604060002090565b94614528610999878c6155c2565b614663578554938a60ff86161561462f575b5050508260881c8481159061455c575b505050508460881b9060101b17179055565b989798939091929361461f5760101c821688851461460b578189146145ed5788829102970297029587019686881187890302809103970391818711828411176145a7575b808061454a565b909591966145be6145b8848a614464565b82614464565b801501808092049804920495808711908311176145db57806145a0565b601190634e487b71600052526024601cfd5b9250505084959401948486118587030280910395033880808061454a565b93975095505050830393833880808061454a565b505050508394933880808061454a565b606061465261465b945173ffffffffffffffffffffffffffffffffffffffff1690565b920151916151f2565b38808a61453a565b50600097508796505050505050565b61467a614832565b6144fc565b614687614823565b6144e2565b5091936080939650600191506146ab9502186146b2575b015190614841565b9192909190565b6146ba614823565b6146a3565b5050600090600090600090565b8051906146e961099960a084015160c08501514210904210151690565b6146bf576effffffffffffffffffffffffffffff926020928484840151169385604085015116936080830160048151614721816129a5565b61472a816129a5565b1461479a5786158688111761478d575b5191614745836129a5565b60018093161586881016614780575b61475d846147c6565b97614772896000526002602052604060002090565b94614528610999878c615625565b614788614832565b614754565b614795614823565b61473a565b5091936080939650600191506146ab9502186147b9575b015190614a00565b6147c1614823565b6147b1565b6060810151516101408201511161153d578061481d73ffffffffffffffffffffffffffffffffffffffff6107ad93511673ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b90612423565b50635a052b326000526004601cfd5b5063a11b63ff6000526004601cfd5b6060906040828201805151610140840151036149f3575b60008061488361487c865173ffffffffffffffffffffffffffffffffffffffff1690565b9786614bc4565b9082895af1936148b38673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055821b1894156149e5575b6148d1615a9f565b94909195866149d7575b01805151825181116149c9575b6000905b89818310614993575050505281519083519180518311614985575b91906000925b888385106149325750505050505261492457918190565b61492d81614f96565b918190565b909192939661494188846129f9565b5161497961494f8a8a6129f9565b518681015187840151106149638285614cbf565b179260a080910151910151908091149015171590565b1717960192919061490d565b61498e87614f96565b614907565b9091976149a18985516129f9565b516149bf6149af8b886129f9565b5188830151898201511092614cbf565b17179701906148ec565b6149d288614f96565b6148e8565b6149e088614f96565b6148db565b6149ee85614f96565b6148c9565b6149fb614cb0565b614858565b60608082019182515161014082015103614b5c575b614a3d614a36825173ffffffffffffffffffffffffffffffffffffffff1690565b9482614bc4565b929060008094819282895af193614a748673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055831b189415614b5257614a91615a9f565b93919485614b115760400180515182518111614b445787905b8a818310614b1e575050505281519083519180518311614b1157919086925b89838510614ae857505050505052614ae15750918190565b9150918190565b9091929396614af788846129f9565b51614b0561494f8a8a6129f9565b17179601929190614ac9565b5050505050509150918190565b909197614b2c8985516129f9565b51614b3a6149af8b886129f9565b1717970190614aaa565b505050505050509150918190565b5050509150918190565b614b64614cb0565b614a15565b91909160408051936020928360e083028701018352818652839160010160051b92838701915b848410614b9e57505050505050565b60c060a0879285878c01528460808083893e606083019088013e01930193019291614b8f565b9190608490614c2b604051916398919765835260a0601c84019633602086015260806040860152614c176060614c01604084015185890190614c55565b9283608001828901520151838388010190614c55565b018094608082016080820152010190614c30565b010190565b8051603f0163ffffffe0169291610676918491905b829060045afa153d15176101c357565b9081519081815260209283808083019301918460051b0101915b84838210614c82575050505060071b0190565b8160809251805185528281015183860152604080820151908601526060809101519085015201910190614c6f565b50632165628a6000526004601cfd5b90815191604081015180156003851116614cfc575b6020809160608401516080850151149060408601511416948451149301519101511416161590565b506040820151600490931460030392614cd4565b9190811015614d51575b60051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea1813603018212156101c3570190565b614d596129af565b614d1a565b3560058110156101c35790565b5063fed398fc6000526004601cfd5b90815180825260208080930193019160005b828110614d9a575050505090565b909192938260a060019287518051614db1816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff168483015260408082015190830152606080820151908301526080908101519082015201950193929101614d8c565b90815180825260208080930193019160005b828110614e1b575050505090565b909192938260c060019287518051614e32816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff9081168584015260408083015190840152606080830151908401526080808301519084015260a091820151169082015201950193929101614e0d565b906005821015614e965752565b61221e610678565b90815260406020820152614ecb60408201835173ffffffffffffffffffffffffffffffffffffffff169052565b602082015173ffffffffffffffffffffffffffffffffffffffff1660608201526101806040830151614f42614f0e610160928360808701526101a0860190614d7a565b60608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08683030160a0870152614dfb565b93614f55608082015160c0860190614e89565b60a081015160e085015260c081015191610100928386015260e082015192610120938487015282015192610140938487015282015190850152015191015290565b63939792856000526020526024601cfd5b614faf614fd5565b6002600055565b614fbe614fd5565b6003600055565b614fcd614fd5565b600201600055565b600160005403614fe157565b637fa8a9876000526004601cfd5b600360005403614ffb57565b61067634611d42565b929091833b156150d157604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652816004528260245283604452858060648180855af11561505e5750505050604052606052565b85853d615085575b5063f486bc879052602052604052606052608052600160a05260a4601cfd5b601f3d0160051c9060051c9080600302918082116150b8575b505060205a9101106150b05785615066565b3d81803e3d90fd5b8080600392028380020360091c9203020101868061509e565b83635f15d6726000526020526024601cfd5b9392919091843b156151cb57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528160045282602452836044528460645260a06084528960a452898060c48180855af11561516257505050505060805260a05260c052604052606052565b89893d615187575b5063f486bc87905260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c9080600302918082116151b2575b505060205a9101106150b0578661516a565b8080600392028380020360091c920302010187806151a0565b84635f15d6726000526020526024601cfd5b9190428111428411151692831561154b575050565b929190338414615373576152046125a7565b9361524182867f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b908351926002601f601d860116106102e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9d860110166000146153655760018085169081604103927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf600593880101831c93808952880160209384820151928560238560e81c94019460e31c1690815285845191185283925b8684106153455750505050509661533f9161067697986152fe60406000209261556d565b600052526040600020907f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b90615379565b85859101938684821c841b166040600020815287865191185201926152da565b506106769495508190615379565b50509050565b909291926000948580528051957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051918860410390809160018111968715615503575b5050508514851515169788156153f5575b5050505050505050156153df57565b6153e7612681565b634f7fb80d6000526004601cfd5b909192939495809798508452604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8401938451957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08201976020600060648b519c7f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8c528d520189845afa9a8b6154a1575b505050505052525238808080808080806153d0565b600051036154af578061548c565b3b6153e7576154f557606001906041640101000000835160001a1a159114166154e05763815e1d646000526004601cfd5b631f003d0a6000525160001a6020526024601cfd5b638baa579f6000526004601cfd5b9091925060408601908151926060880151851a9061553b575b8752845260208360808660015afa508484528a865252513880806153bf565b50601b8360ff1c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416835261551c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020910160051b60010160007f00000000000000000000000000000000000000000000000000000000000000003c60005190565b905460ff8160081c16615613576effffffffffffffffffffffffffffff8160101c1690816155f3575b505050600190565b60881c1115615604575b80806155eb565b61560d9061568d565b386155fd565b50631a5155746000526020526024601cfd5b906000905460ff8160081c16615684576effffffffffffffffffffffffffffff8160101c16908161565a575b50505050600190565b60881c111561566a578080615651565b615675575b50600090565b61567e9061568d565b3861566f565b50905050600090565b6310fda3e16000526020526024601cfd5b9190608082019081356156b08161064b565b33141590600460018211911016166156c757505050565b610676926156f56139d76060604051956317b1f94287526020808801528460408801523382880152016111bf565b6080840152606061014461012085013761014060a08401526101e060c0840152615778601c6103246102643561574160a08202918261016001906101808a019060051b61020001614c45565b6102a0810160e08801528461032082890160006102e08201526102c084016101008b015260016103008201520152019401926111bf565b6159e3565b919082519060808201918251926005841015615838575b6157c5602083019473ffffffffffffffffffffffffffffffffffffffff865116331415906004600182119110161690565b156157ed5750906157df91608061067696015190856158c9565b91519263fb5014fc93615a4b565b600491949350516157fd816129a5565b615806816129a5565b0361583257610676936158269184519460808660601b9301519085615845565b91639397928593615a4b565b50505050565b615840610678565b615794565b9493919260c060a4946158b5614c2b946040519663f4dd92ce8852601c88019a1860a088015260a0602088015261589f60606158886040840151878b0190614c55565b928360a00160408b0152015185838a01019061599b565b019160a083016060880152838388010190614c30565b01809460a08201608082015201019061597f565b9392614c2b906101649392604051936317b1f9428552601c85019760208087015260408601523360608601528151608086015260a082015161012086015260c082015190610140918287015260e08301516101608701528160a087015261596f60408401519361595a606061594461018097888c0190614c55565b9283870160c08c0152015186838b01019061599b565b019183830160e0890152848389010190614c30565b0194859182016101008201520101905b6129f5602092839283815180845260051b948593019101614c45565b8051908183526020928380808401938560051b01019101915b8181106159c55750505060a0020190565b60a090818481835160045afa153d15176101c35785019201916159b4565b6020909391937fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615a3c5715615a2c575050565b63fb5014fc90526020526024601cfd5b5063fb5014fc90612230612681565b602090949391947fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615a96571561223057505050565b50612230612681565b60009081906080803d109060009081908280918515615b42575b8515615aca575b5050505050929190565b91939750919550602094939480920196604051918360c08302840101604052818352839160010160051b98898401905b8a8410615b1f5750505050615b1493949596509501614b69565b913880808080615ac0565b60a083879284878901528181863e60608501518286015201920193019290615afa565b9450909150604081803e5190602051913d81113d8411179485615ab95794508093506020915060003e60005191602082813e602051903d8260a0028560071b0186011161ffff83861711179460008052615ab9565b908135641fffffffe08160051b169060405191602091828285010160405263ffffffff809116845260005b828110615bd25750929450505050565b80615be885848180958c010135168a01016119cd565b82828801015201615bc256fea164736f6c6343000811000a60406080815234610081576100156103a0604052565b6018806080526103003660a03761002a6101fe565b61003261027d565b9280519160005b84811061004c5760fe608052610301609ff35b8061005860019261011f565b6003028352610067878461016e565b60208151910120610077826101d5565b5284845201610039565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b038211176100b857604052565b6100c0610086565b604052565b60c081019081106001600160401b038211176100b857604052565b61010081019081106001600160401b038211176100b857604052565b601f909101601f19168101906001600160401b038211908210176100b857604052565b906001820180921161012d57565b634e487b7160e01b600052601160045260246000fd5b9081519160005b83811061015b575050016000815290565b806020809284010151818501520161014a565b6101d3906101c56101b2949360066040519687947f42756c6b4f72646572284f72646572436f6d706f6e656e74730000000000000060208701526039860190610143565b6520747265652960d01b81520190610143565b03601f1981018452836100fc565b565b6080518110156101e85760051b60a00190565b634e487b7160e01b600052603260045260246000fd5b604051608081016001600160401b0381118282101761024b575b6040526048815260208101606036823760688201905b81811061023a57505090565b625b325d60e81b815260030161022e565b610253610086565b610218565b6101c561027794936102776101d3946040519788956020870190610143565b90610143565b6104e56040805161028d8161009d565b606a81527f4f666665724974656d2875696e7438206974656d547970652c6164647265737360208201527f20746f6b656e2c75696e74323536206964656e7469666965724f724372697465828201527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606082015269656e64416d6f756e742960b01b60808201528151610320816100c5565b608481527f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e7469666965838201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c6164647265737320726563697069608082015263656e742960e01b60a08201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c438351936103f5856100e0565b60d485527f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208601528401527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608401527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808401527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08401527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08401527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0840152610258565b9056fe00000000000000000000000000000000f9490004c11cef243f5400493c00ad630000000000000000
+cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000d4b6fcc21169b803f25d22100000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000660c6101c060405234620000b9576200001f6200001962000114565b62000151565b604051615f2f9081620006bd823960805181612622015260a05181612646015260c051816125ff015260e0518181816113b80152612430015261010051818181611257015261247f01526101205181818161145e01526124eb015261014051816125ac015261016051816125d3015261018051818181610f1f01528181612144015261226501526101a05181818161218201526122a30152f35b600080fd5b604081019081106001600160401b03821117620000da57604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620000da57604052565b620065ec60208138039182604051938492620001318285620000f0565b833981010312620000b957516001600160a01b0381168103620000b95790565b60406004916200016062000587565b610120526101005260e05260c05260a0526080524661014052620001b060c0519060805160a0516040519360005281602052604052466060523060805260a0600020926040526000606052608052565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa90811562000230575b600091620001fa575b506101a052620001f86001600055565b565b62000220915060403d811162000228575b620002178183620000f0565b81019062000240565b5038620001e8565b503d6200020b565b6200023a62000257565b620001df565b9190826040910312620000b9576020825192015190565b506040513d6000823e3d90fd5b604051906200027382620000be565b60038252565b6040519060a082016001600160401b03811183821017620000da57604052606a8252565b6040519060c082016001600160401b03811183821017620000da576040526084825263656e742960e01b60a0837f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e746966696560408201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c616464726573732072656369706960808201520152565b6040519061010082016001600160401b03811183821017620000da5760405260d482527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0837f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c4360408201527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608201527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808201527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08201527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08201520152565b60405190608082016001600160401b03811183821017620000da576040526052825271766572696679696e67436f6e74726163742960701b6060837f454950373132446f6d61696e28737472696e67206e616d652c737472696e672060208201527f76657273696f6e2c75696e7432353620636861696e49642c616464726573732060408201520152565b9081519160005b83811062000539575050016000815290565b806020809284010151818501520162000527565b6200057862000571949362000571620001f894604051978895602087019062000520565b9062000520565b03601f198101845283620000f0565b6040516200059581620000be565b600781526614d9585c1bdc9d60ca1b6020918201527f32b5c112df393a49218d7552f96b2eeb829dfb4272f4f24eef510a586b85feef91620005d662000264565b8281019062312e3560e81b825251902091620005f162000279565b818101927f4f666665724974656d2875696e7438206974656d547970652c6164647265737384527f20746f6b656e2c75696e74323536206964656e7469666965724f72437269746560408301527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606083015269656e64416d6f756e742960b01b6080830152620006816200029d565b92620006b46200069062000366565b936200069b62000495565b838151910120968151902095805184820120956200054d565b80519101209056fe60806040526004361015610023575b361561001957600080fd5b610021614f96565b005b60003560e01c80156100eb57806306fdde031461016957806346423aa7146101605780635b34b9661461015757806379df72bd1461014e57806387201b4114610145578063881477321461013c578063a817440414610133578063a900866b1461012a578063b3a34c4c14610121578063e7acab2414610118578063ed98a5741461010f578063f07ec37314610106578063f2d12b12146100fd578063f47b7740146100f4578063fb0f3ee1146100eb5763fd9f1e100361000e576100e6610f50565b61000e565b506100e66101c8565b506100e6610ec8565b506100e6610df2565b506100e6610d8a565b506100e6610cc2565b506100e6610c05565b506100e6610b81565b506100e6610b17565b506100e6610a60565b506100e66108d6565b506100e66107c6565b506100e661059d565b506100e66104f5565b506100e6610474565b506100e661042e565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc906020828201126101c3576004359167ffffffffffffffff83116101c35782610240920301126101c35760040190565b600080fd5b506101d236610172565b6101243590600382169160021c91600183119234158403610420575b60038111907f0203020301010000000000000000000000000000000000000000000000000000811a9061024c8260a0850260240135887d010102030000000000000000000000000000000000000000000000000000851a888a61121a565b928060051b6101c4013596610260816106a8565b6102b3575050604435602435176102a55761028b9461027e916115b5565b61028661166d565b6159cc565b6102956001600055565b60405160018152602090f35b0390f35b636ab37ce76000526004601cfd5b610286925061028b969161032a916102c96111a8565b9384836102d682956106a8565b6002810361032f5750610325918a6102f060a082016111bf565b6102fc606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611efe565b611738565b612105565b610338816106a8565b600381036103875750610325918a61035260a082016111bf565b61035e606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611fff565b806103936004926106a8565b036103dc57610325918a6103a6816111bf565b6103b2606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611efe565b610325918a6103ea816111bf565b6103f6606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611fff565b61042934611d42565b6101ee565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357602080526707536561706f727460475260606020f35b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35761052d614f7c565b3360005260016020526020604060002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43014060801c018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b50346101c3577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020813601126101c3576004359067ffffffffffffffff82116101c3576101609082360301126101c35761061263ffffffff6020921661014461060982600401611cd6565b91013590612423565b604051908152f35b9181601f840112156101c35782359167ffffffffffffffff83116101c3576020808501948460051b0101116101c357565b73ffffffffffffffffffffffffffffffffffffffff8116036101c357565b60a435906106768261064b565b565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156106b257565b610676610678565b60809080516106c8816106a8565b83528173ffffffffffffffffffffffffffffffffffffffff918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610729575050505090565b909192938260e0600192604088516107428382516106ba565b8085015173ffffffffffffffffffffffffffffffffffffffff1660a0840152015160c08201520195019392910161071b565b9092916040820191604081528451809352606081019260208096019060005b8181106107b0575050506107ad9394818403910152610709565b90565b8251151586529487019491870191600101610793565b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff6004358181116101c35761081290369060040161061a565b50506024358181116101c35761082c90369060040161061a565b50506044358181116101c35761084690369060040161061a565b50506064359081116101c35761086090369060040161061a565b505061087961086d610669565b60c43590608435611813565b906102a160405192839283610774565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101c3576004359067ffffffffffffffff82116101c3576108d29160040161061a565b9091565b50346101c3576108e536610889565b505060046108fb63ffffffff8235168201611aba565b90610904614f7c565b81519060005b82811061091d5760405160018152602090f35b8061092a60019286612988565b51805184608082015161093c81612934565b61094581612934565b14610a4857805173ffffffffffffffffffffffffffffffffffffffff1661096b82614762565b90610980826000526002602052604060002090565b61098a81846158f0565b5061099d610999825460ff1690565b1590565b6109ae575b50505050505b0161090a565b6109f4610a1f928460207ff280791efe782edcf06ce15c8f4dff17601db3b88eb3805a0db7d77faf757f04986060890151516101408a015103610a3b575b015191615199565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b610a2e60405192839283614e56565b0390a138808080806109a2565b610a43614c68565b6109ec565b50506109a8565b9060206107ad928181520190610709565b5060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35760043567ffffffffffffffff8082116101c357610aab368360040161061a565b50506024359081116101c3576102a191610b0391610acc368260040161061a565b5050610afb610ae463ffffffff809416600401615ec5565b92610aed6110db565b926000845216600401611c52565b903392613b96565b604051918291602083526020830190610709565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35773ffffffffffffffffffffffffffffffffffffffff600435610b688161064b565b1660005260036020526020604060002054604051908152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6040813601126101c3576004359067ffffffffffffffff82116101c35760409082360301126101c357610bfb610be363ffffffff602093166004016119cd565b610beb6110db565b9060008252339160243591613f10565b6040519015158152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6080813601126101c3576004359067ffffffffffffffff908183116101c35760a09083360301126101c3576024359081116101c3576102a191610cb091610c71368260040161061a565b5050610ca060643592610c838461064b565b610c9663ffffffff80921660040161186c565b9216600401611a2d565b9133811502019160443591613f10565b60405190151581529081906020820190565b5060a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600467ffffffffffffffff81358181116101c357610d0d3682850161061a565b5050602435908282116101c357610d263683860161061a565b50506044359283116101c357610d7b61087994610d453686830161061a565b5050610d5963ffffffff8094168201615ec5565b92610d7381610d666110db565b9660008852168301611b44565b951601611b44565b608435933393606435936126d4565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c3576020610612600435610dcb8161064b565b73ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff600480358281116101c357610e3d3682840161061a565b5050602435908382116101c357610e563683850161061a565b50506044359384116101c3576102a193610eb0610ebc94610e793684830161061a565b5050610e9f610ea860643595610e8e8761064b565b63ffffffff92838092168501611bf5565b97168301611a2d565b931601611c52565b91338115020192613b96565b60405191829182610a4f565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357610f006125a7565b606060005260205273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166040526303312e3560635260a06000f35b50346101c357610f5f36610889565b90610f68614f7c565b600091825b818110610f925783610f855760405160018152602090f35b610f8d614d23565b610295565b80610fa06001928486614cc8565b94610faa866111bf565b907f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d611075611006610fde60208b016111bf565b93610feb60808c01614d16565b60048633148833141715911417179961014061060982611cd6565b9261104a61101e856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016610100179055565b60405193845273ffffffffffffffffffffffffffffffffffffffff9081169416929081906020820190565b0390a301610f6d565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519060a0820182811067ffffffffffffffff8211176110ce57604052565b6110d661107e565b604052565b604051906020820182811067ffffffffffffffff8211176110ce57604052565b604051906040820182811067ffffffffffffffff8211176110ce57604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176110ce57604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff811161119b575b01160190565b6111a361107e565b611195565b6111b06110fb565b90602082526020828136910137565b356107ad8161064b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101c3570180359067ffffffffffffffff82116101c3576020019181360383136101c357565b959392919094611228614f4e565b61123061155f565b6101643561014435428211154282111761154b57505061020435610264351061153d5793907f00000000000000000000000000000000000000000000000000000000000000006080528060a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019283526024906102e00137610160948360a0528460c052600060e05260009260005b83610204358210156113315790604060019261010060a060208560061b9a818c610284018537858c61028401610120376102a48c0135179d019860e06080208a5201988a8a528b60c08401526102840191013701969392966112d4565b5096509192979690976001610204350160051b610160206060525b836102643588101561138957906102a460a060019301958787528860c082015260408a60061b91610100836102840191013701351796019561134c565b50925095945095925073ffffffffffffffffffffffffffffffffffffffff91501161152f576107ad91611528917f00000000000000000000000000000000000000000000000000000000000000006080528060a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022090816102643560051b0152606060c46102406102643560051b013761036060843561145a8173ffffffffffffffffffffffffffffffffffffffff166000526001602052604060002090565b54967f00000000000000000000000000000000000000000000000000000000000000006080526040608460a037606051610100526101205260a0610144610140376101e09687526101809687608020976102643560051b0191888352336101a06102643560051b015260806101c06102643560051b0152610120826102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a06102643502938460a435940190a360006060526102643560051b01016040528101906111c9565b908361430d565b6339f3e3fd6000526004601cfd5b63466aa6166000526004601cfd5b6321ccfeb76000526020526040526044601cfd5b7401000000000000000000000000000000000000000060243560c4351760a43560843517171060186101243510166102643560061b61026001610244351461024061022435146020600435141616161561152f57565b608435916101043560e43560c4358315611627579461067695604051957f4ce34aa200000000000000000000000000000000000000000000000000000000875260206004880152600160248801526044870152606486015260848501523360a485015260c484015260e483015261223e565b925092806116366002926106a8565b0361166057928360016106769503611651575b503391614fab565b61165a90611d31565b38611649565b919061067693339161508a565b3460643560006102643560061b815b8181106116bd575050508181116116b0575b61169a81608435611d62565b8082116116a5575050565b610676910333611d62565b6116b8611d22565b61168e565b806102840135948086116116e657906116e08660409303966102a4830135611d62565b0161167c565b638ffff98084526004601cfd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190820391821161173057565b6106766116f3565b919082156117d95760843592610104353360c43560e4355b6117cc575b8360051b6101e40335936102643560061b9060005b82811061177f57505050956106769596611dae565b87876102848301358c856117ab575b918493916117a5936102a46040970135908a611dae565b0161176a565b9891816117bf60409695936117a595611723565b9a9193509193945061178e565b6117d4611d53565b611755565b3392606435608435602435604435611750565b60209067ffffffffffffffff8111611806575b60051b0190565b61180e61107e565b6117ff565b906108d2929163ffffffff9161182f8360043516600401611bf5565b926118408160243516600401611a2d565b6118606118538360443516600401611b44565b9260643516600401611b44565b923381150201946126d4565b90604051610200810160405260806118c68294604060208201602086013760a084018085526118a563ffffffff918284351684016118f5565b6118b68160608401351683016118cb565b60608601528382013516016118cb565b910152565b9060206040519263ffffffff813563ffffffe0601f82011692848401908737168452830101604052565b6118c660609161016081853763ffffffff611917816040840135168301611927565b604086015283820135160161197a565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160a0809402910185378086015b83811061196c5750505050604052565b84815293820193810161195c565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160c0809402910185378086015b8381106119bf5750505050604052565b8481529382019381016119af565b906040516102008101604052611a13819360a083018084526119f963ffffffff918284351684016118f5565b6001602085015260016040850152602082013516016118cb565b606082015260806040519160208301604052600083520152565b803591600592641fffffffe081851b16604080519060209384848401018252829663ffffffff809216845260005b858110611a6e5750505050505050909150565b8083888093850101351683018551908360a091828401895287608093848484018737820135160101908d60018884351601901b8851928184018a52833782015282828801015201611a5b565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611af55750505050505050565b808388809385010135168301611b34838851928984016101a085018b52611b2581848b81860135168501016118f5565b8452878a8201351601016118cb565b8382015282828701015201611ae5565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611b7f57505050505050565b80611b9587848180958801013516860101611ba1565b82828701015201611b70565b90813591604080519363ffffffff81168552602080641fffffffe08360051b168701019381643fffffffc0869460061b16910185378086015b828110611be75750505052565b848152938301938101611bda565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611c3057505050505050565b80611c468784818095880101351686010161186c565b82828701015201611c21565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611c8d5750505050505050565b808388809385010135168301611cc6838851928984018a52611cb782898184013516830101611ba1565b8452878a820135160101611ba1565b8382015282828701015201611c7d565b9060405161016081016040528092611d16610140918281853763ffffffff611d05816040840135168301611927565b60408601526060820135160161197a565b80606084015251910152565b50638ffff9806000526004601cfd5b6369f958276000526020526024601cfd5b63a61be9f06000526020526024601cfd5b50636ab37ce76000526004601cfd5b611d6b82611d99565b600080808085855af115611d7d575050565b611d85612681565b63bc806b966000526020526040526044601cfd5b15611da057565b6391b3e5146000526004601cfd5b929193949094611dbd83611d99565b611dc781836120f2565b80611ef0575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611e36575b505050505050604052606052565b80863b151516611e2857908795969115611e5b5786635f15d67287526020526024601cfd5b959192939515611e80575063988919238594526020526040526060526080526084601cfd5b3d611ea3575b5063f486bc87845260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c908060030291808211611ed7575b505060205a910110611ece5785611e86565b833d81803e3d90fd5b8080600392028380020360091c92030201018680611ebc565b906106769592949391612359565b919395909294611f0e81836120f2565b80611f375750508460016106769603611f28575b50614fab565b611f3190611d31565b38611f22565b815160649693959394929190602003611fec5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c08682016001815101809152611f8c565b95909192939461200e86611d99565b61201881836120f2565b806120285750506106769461508a565b90606495969493929160208251146000146120df5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261207f565b906020820151036121005750565b610676905b90604082510361223a5760208201519160c06064820151026044019260405193602073ffffffffffffffffffffffffffffffffffffffff6000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528584527f00000000000000000000000000000000000000000000000000000000000000006040526055600b2016976040528180526040860182895af190805191156122215750937f4ce34aa2000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000060209596160361221557505052565b61221e91612345565b52565b63d13d53d48691612230612681565b526020526024601cfd5b9050565b9060405190602073ffffffffffffffffffffffffffffffffffffffff6101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528785527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169560405282805282865af1908051911561233657507fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa20000000000000000000000000000000000000000000000000000000091160361232d575050565b61067691612345565b63d13d53d49150612230612681565b631cf99b266000526020526040526044601cfd5b9060649492939160208251146000146124105760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c085820160018151018091526123ae565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106125665750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a83106125245750505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b8380827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519089815260e08120875252019201920191906124ae565b80827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519088815260c0812087525201920192019190612466565b6000467f0000000000000000000000000000000000000000000000000000000000000000036125f557507f000000000000000000000000000000000000000000000000000000000000000090565b60405190608051907f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020527f0000000000000000000000000000000000000000000000000000000000000000604052466060523060805260a081209260405260605260805290565b3d61268857565b601f3d0160051c60405160051c9080600302918082116126bb575b505060205a9101106126b157565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806126a3565b93959480939297956126e692866129aa565b93909187519681516127006126fb828b612e96565b613328565b9860009a8b905b8282106127cb5750506000925b8284106127575750505050509461273b949587829861274c575b5081511561273f576136dc565b9190565b61274761338b565b6136dc565b82510382523861272e565b909192939a8a6127738361276c8f8990612988565b5189613408565b61278c8180516080602082511515930151910151141690565b156127a65750506001809101945b019291909a939a612714565b86916127c5916127be85886001979b01038093612988565b528d612988565b5061279a565b90949b6127e7896127e0888598969798612988565b518961339a565b8c6128018280516080602082511515930151910151141690565b1561281d5750506001809101955b01909b949b93929193612707565b879161283a91846001959a03916128348383612988565b52612988565b5061280f565b6128486110ae565b90604051610160810181811067ffffffffffffffff8211176128c7575b604052600080825280602083015260609182604082015282808201528160808201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528452806020850152604084015280808401526080830152565b6128cf61107e565b612865565b6128dc6110fb565b600181529060203681840137565b906128fc6128f7836117ec565b61111b565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061292a82946117ec565b0190602036910137565b600511156106b257565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60209080511561297c570190565b61298461293e565b0190565b602091815181101561299d575b60051b010190565b6129a561293e565b612995565b9391936000936129b8614f5d565b6000357c400000000000000000000000000000000000000000000000000000000016926129e3612840565b508251936129f0856128ea565b9760205b6001870160051b8110612ac6575050907c4000000000000000000000000000000000000000000000000000000001612a329214612ab9575b83612fb7565b60205b6001840160051b8110612a485750505050565b6020816001928901518015612ab357612aab90828701515186612a7f825173ffffffffffffffffffffffffffffffffffffffff1690565b8287015173ffffffffffffffffffffffffffffffffffffffff165b906060604085015194015194614232565b019050612a35565b50612aab565b612ac1612f94565b612a2c565b808601518215612c5557612ad981614668565b918d82969215612c42578501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019382519260a08401519360c08101519060408101519e8f519160800151612b2e81612934565b60048110600052600110179e60005b828110612bd757505050606080925101519485519560005b878110612b6e5750505050505050506020905b016129f4565b80878760a0612b7f60019587612988565b51612bb789898c6080850196612b9788518a83612f61565b9186019889519089518214600014612bc7575050508088525b8751612eef565b8094520190815190525201612b55565b612bd092612f61565b8852612bb0565b8087612be560019385612988565b519c8d600051905110179c612c28878c60808401938c6060612c0987518984612f61565b92019687519087518214600014612c32575050508086525b8551612ea3565b8092525201612b3d565b612c3b92612f61565b8652612c21565b5050935050906000602080930152612b68565b906000602080930152612b68565b939193600093612c71614f5d565b6000357c40000000000000000000000000000000000000000000000000000000001692612c9c612840565b50825193612ca9856128ea565b9760205b6001870160051b8110612d45575050907c4000000000000000000000000000000000000000000000000000000001612cea9214612ab95783612fb7565b60205b6001840160051b8110612d005750505050565b6020816001928901518015612d3f57612d3790828701515186612a7f825173ffffffffffffffffffffffffffffffffffffffff1690565b019050612ced565b50612d37565b808601518215612e7557612d5881614414565b918d82969215612e62578501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019382519260a08401519360c08101519060408101519e8f519160800151612dad81612934565b60048110600052600110179e60005b828110612e2657505050606080925101519485519560005b878110612ded5750505050505050506020905b01612cad565b80878760a0612dfe60019587612988565b51612e1689898c6080850196612b9788518a83612f61565b8094520190815190525201612dd4565b8087612e3460019385612988565b519c8d600051905110179c612e58878c60808401938c6060612c0987518984612f61565b8092525201612dbc565b5050935050906000602080930152612de7565b906000602080930152612de7565b8181029291811591840414171561173057565b9190820180921161173057565b929092838103612eb35750505090565b612ec983612ecf93039342039182850390612e83565b93612e83565b8201809211612ee2575b81049015150290565b612eea6116f3565b612ed9565b919092838303612eff5750505090565b600192612f1883612f1e93039342039182850390612e83565b94612e83565b8301809311612f54575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b612f5c6116f3565b612f28565b919091828114612f8e5782818309612f8057612f7c91612e83565b0490565b63c63cf0896000526004601cfd5b50905090565b506312d3f5a36000526004601cfd5b600211156106b257565b516107ad816106a8565b815181519260005b8281106130c05750505060005b828110612fd857505050565b612fe28183612988565b5161301661300260208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b156130b75751606081018051519060005b828110613089575050506040018051519060005b82811061304f575050506001905b01612fcc565b8061306f6130696130636001948651612988565b51612fad565b60031090565b61307a575b0161303b565b61308481866131ba565b613074565b8061309d6130696130636001948651612988565b6130a8575b01613027565b6130b281876131a6565b6130a2565b50600190613049565b6130ca8183612988565b516130df81518781101561317a575b86612988565b51602090613101613002838301516effffffffffffffffffffffffffffff1690565b1561316f57519060409081830151918401519263bfb3f8ce9185015161312681612fa3565b61312f81612fa3565b61315c575b50815183101561315357509161314d91600194936131d7565b01612fbf565b6000526004601cfd5b9050606091500151636088d7de38613134565b50505060019061314d565b613190602084015161318b81612fa3565b613195565b6130d9565b63133c37c66000526020526024601cfd5b63a8930e9a6000526020526040526044601cfd5b63d69293326000526020526040526044601cfd5b61221e826106a8565b906131e191612988565b518051916131ee836106a8565b600383111561324d5761322e8260046040606095019586518015156000146132345761322490878701519060808801519161326a565b14600303906131ce565b01519052565b50608085015151156132245761324861325b565b613224565b6394eb6af66000526004601cfd5b506309bde3396000526004601cfd5b916000928352602090818420918082019181815191600592831b0101905b8184106132a857505050500361329b5750565b6309bde33990526004601cfd5b8351808611821b95865294831894909452604086209392820192613288565b604051906060820182811067ffffffffffffffff82111761331b575b6040528160406132f16110ae565b91600092838152836020820152838382015283606082015283608082015281528260208201520152565b61332361107e565b6132e3565b906133356128f7836117ec565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061336382946117ec565b019060005b82811061337457505050565b60209061337f6132c7565b82828501015201613368565b5063d5da9a1b6000526004601cfd5b929190926133a66132c7565b938051156133f557846133d89181519373ffffffffffffffffffffffffffffffffffffffff608086019616865261348a565b6060810151156133e6575050565b60006001928160208701525252565b63375c24c160005260006020526024601cfd5b929190926134146132c7565b938051156134545784613429918151936135d1565b60208401913383526040850152606081015115613444575050565b6000600192526000608082015252565b63375c24c160005260016020526024601cfd5b50637fda72796000526004601cfd5b50634e487b7160005260116020526024601cfd5b9092919260009081928290828351905b8160051b850181106134c957505050505060608293945101526134ba5750565b600114611da057610676613476565b6020909695960190602082515184518110156135c4575b60051b8401015180519060208451015160206040840151920151158251821015176135b9579060209160051b0101519660609081890151998a81019a15908b1060011b171798976000828201528b51871560011461356c57502085189060408b0151610120820151189060208c0151905118171761355f575b9061349a565b613567613467565b613559565b929061012092949750806040915185526020810151602086015201516040840152805160208d0152015160408b01522092602085018281186135af575b50613559565b82519052386135a9565b505050959495613559565b6135cc613467565b6134e0565b9092919260009081928291808051600590811b82015b8084106136035750505050505060608293945101526134ba5750565b6020979697809401938085515187518110156136cf575b841b8701015190808651015191606092828483510151920151158251821015176136c3576000918391871b010151928301998a519b8c81019c15908d1060011b17179a99528b51881560011461368357505060a0902086146135e75761367e613467565b6135e7565b8251815281830151818301526040808401519082015260808084015191015260a0909120965083018481186136b9575b506135e7565b84519052386136b3565b505050509695966135e7565b6136d7613467565b61361a565b9092938151936136eb856128ea565b956136f46111a8565b9180519060005b8281106138ca5750505060005b86811061379a57505061371a90612105565b478061378a575b50613734575b5050506107ad6001600055565b60005b8381106137445750613727565b8061375a61375460019388612988565b51151590565b613765575b01613737565b6137856137728285612988565b518561377e8482612988565b5191615aab565b61375f565b6137949033611d62565b38613721565b6137a48186612988565b516137c461300260208301516effffffffffffffffffffffffffffff1690565b156138b4576137dc6137d6838b612988565b60019052565b51604081015180519060005b82811061384c575050506060809101519081519160005b83811061381457505050506001905b01613708565b8061382160019284612988565b5160a08582019182518061383b575b5001519052016137ff565b61384690858b613944565b38613830565b8061385960019284612988565b51608060608201918251613872575b01519052016137e8565b608081018051908b90526138ab8c61389e8b5173ffffffffffffffffffffffffffffffffffffffff1690565b6101208c0151908561395b565b82820152613868565b508060006138c46001938b612988565b5261380e565b80613925866138db60019486612988565b5180519081516138ea816106a8565b6138f3816106a8565b1561392b575b604061391c602083015173ffffffffffffffffffffffffffffffffffffffff1690565b9101519161395b565b016136fb565b47606083015111156138f95761393f611d22565b6138f9565b63a5f542086000526020526040526060526064601cfd5b9291908351613969816106a8565b613972816106a8565b613a1557505050806139ba6139a1602061067694015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6040830151911617613a08575b60606139ff608083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015190611d62565b613a10611d53565b6139dc565b60018451613a22816106a8565b613a2b816106a8565b03613aab5792610676936040820151613a9e575b602082015173ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff6060613a93608086015173ffffffffffffffffffffffffffffffffffffffff1690565b940151931691611dae565b613aa6611d53565b613a3f565b60028451613ab8816106a8565b613ac1816106a8565b03613b2e5783613aeb602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611efe565b83613b53602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611fff565b9193929081613ba89184519085612c63565b9190805160051b604001937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051907f4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e760209788835282a152865196613c0f88613328565b9560009889915b818310613c44575050505093613c359487829798613c39575b506136dc565b5090565b825103825238613c2f565b9091929988613c6585613c57818f612988565b518581519101519089613cba565b613c7e8180516080602082511515930151910151141690565b15613c975750506001809101935b019190999299613c16565b8591613cb491613cad8560019699038093612988565b528b612988565b50613c8c565b909192613cc56132c7565b938351158015613eaf575b613ea2575b613cdd6132c7565b90613ce98282866135d1565b81519460609384870193845115613e82575092859288836107ad9996613d168360809a97613e199c61348a565b613d208351612fad565b613d29816106a8565b885190613d35826106a8565b613d3e826106a8565b60ff85519273ffffffffffffffffffffffffffffffffffffffff8c604080613d806139a160208a015173ffffffffffffffffffffffffffffffffffffffff1690565b613da46139a1602086015173ffffffffffffffffffffffffffffffffffffffff1690565b189701519101511894169218161717613e73575b50835182518601511015613e3957505090602083613ded613ddb613dfa9561296e565b5193518c518301518551910397612988565b5151015191015190612988565b5101525b015173ffffffffffffffffffffffffffffffffffffffff1690565b60808351019073ffffffffffffffffffffffffffffffffffffffff169052565b8495939492509060206040613ded85613e54613e659661296e565b519451015188518551910397612988565b510152519086510152613dfe565b613e7c90613eb8565b38613db8565b9750505050505050608060009182602085015201526107ad815160019052565b613eaa613ec9565b613cd5565b50805115613cd0565b63bced929d6000526020526024601cfd5b506398e9db6e6000526004601cfd5b613ee06110fb565b90600182528160005b60209081811015613f0b57602091613eff612840565b90828501015201613ee9565b505050565b92613faa613f7692613fe295613f4060046080835101516005811015613ff1575b613f3a81612934565b14614f6c565b613f8884613f4d83614414565b9098829a9296613f5b613ed8565b96613f658861296e565b52613f6f8761296e565b5086612fb7565b613f7f8561296e565b51519889613ffe565b613fa4613f936128d4565b9183613f9e8461296e565b5261296e565b51615aab565b815173ffffffffffffffffffffffffffffffffffffffff16602083015173ffffffffffffffffffffffffffffffffffffffff16612a9a565b613fec6001600055565b600190565b613ff9610678565b613f31565b60a08082015160c08301519796909593916140176111a8565b9689604086019384515190600095865b8c898d86841061411757505050505050505060809260048487015161404b81612934565b101661410a575b6060809501968751519760005b89811061408e575050505050505050505061407b919250612105565b47806140845750565b6106769033611d62565b806140ea8c8f8b8b8b8f936140bf908c8c6140ac60019c8e51612988565b51968701958651958801958651906141c0565b8092528b830151905281516140d3816106a8565b6140dc816106a8565b156140f0575b50339061395b565b0161405f565b47106140fd575b386140e2565b614105611d22565b6140f7565b614112612f94565b614052565b998561417e9392869798999c6141596141338860019a51612988565b51948551614140816106a8565b15179e8d60608701938451956080890196875190614189565b9052528c61012061391c825173ffffffffffffffffffffffffffffffffffffffff1690565b01908d939291614027565b90939084810361419f5750506107ad9350612f61565b93836141b46107ad97966141ba949686612f61565b93612f61565b90612ea3565b9093908481036141d65750506107ad9350612f61565b93836141b46107ad97966141eb949686612f61565b90612eef565b90815180825260208080930193019160005b828110614211575050505090565b909192938260a08261422660019489516106ba565b01950193929101614203565b929094939160409182519460809182870191875273ffffffffffffffffffffffffffffffffffffffff94856020921682890152838189015286518093528160a089019701936000915b8483106142c95750505050505082828594936142c493867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f319896036060870152169716956141f1565b0390a3565b90919293949784836001928b5180516142e1816106a8565b8252808401518c168483015285810151868301526060908101519082015201990195949301919061427b565b9092916000938285526002602052604085209283549260ff8460081c166143ef576effffffffffffffffffffffffffffff8460101c166143de5760ff841615614374575b505071010000000000000000000000000000010001909255509091506106769050565b6143806128f78261115f565b9281845236828201116143da579262010001949261067697986020846143d2957fffffffffffffffffffffffffffffff0000000000000000000000000000000000988387013784010152608435615199565b918594614351565b8780fd5b5063ee9e0e6386526020526024601cfd5b50631a51557486526020526024601cfd5b90805b61440b575090565b80910680614403565b80519061442d61099960a084015160c085015190615184565b61465b576effffffffffffffffffffffffffffff92602092848484015116938560408501511693608083016004815161446581612934565b61446e81612934565b146146285786158688111761461b575b519161448983612934565b6001809316158688101661460e575b6144a184614762565b976144b6896000526002602052604060002090565b946144c4610999878c6158f0565b6145ff578554938a60ff8616156145cb575b5050508260881c848115906144f8575b505050508460881b9060101b17179055565b98979893909192936145bb5760101c82168885146145a757818914614589578882910297029702958701968688118789030280910397039181871182841117614543575b80806144e6565b9095919661455a614554848a614400565b82614400565b80150180809204980492049580871190831117614577578061453c565b601190634e487b71600052526024601cfd5b925050508495940194848611858703028091039503388080806144e6565b9397509550505083039383388080806144e6565b50505050839493388080806144e6565b60606145ee6145f7945173ffffffffffffffffffffffffffffffffffffffff1690565b92015191615199565b38808a6144d6565b50600097508796505050505050565b6146166147ce565b614498565b6146236147bf565b61447e565b50919360809396506001915061464795021861464e575b0151906147dd565b9192909190565b6146566147bf565b61463f565b5050600090600090600090565b80519061468561099960a084015160c08501514210904210151690565b61465b576effffffffffffffffffffffffffffff9260209284848401511693856040850151169360808301600481516146bd81612934565b6146c681612934565b1461473657861586881117614729575b51916146e183612934565b6001809316158688101661471c575b6146f984614762565b9761470e896000526002602052604060002090565b946144c4610999878c615953565b6147246147ce565b6146f0565b6147316147bf565b6146d6565b509193608093965060019150614647950218614755575b01519061499c565b61475d6147bf565b61474d565b6060810151516101408201511161153d57806147b973ffffffffffffffffffffffffffffffffffffffff6107ad93511673ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b90612423565b50635a052b326000526004601cfd5b5063a11b63ff6000526004601cfd5b60609060408282018051516101408401510361498f575b60008061481f614818865173ffffffffffffffffffffffffffffffffffffffff1690565b9786614b6b565b9082895af19361484f8673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055821b189415614981575b61486d615dcd565b9490919586614973575b0180515182518111614965575b6000905b8981831061492f575050505281519083519180518311614921575b91906000925b888385106148ce575050505050526148c057918190565b6148c981614c57565b918190565b90919293966148dd8884612988565b516149156148eb8a8a612988565b518681015187840151106148ff8285614c77565b179260a080910151910151908091149015171590565b171796019291906148a9565b61492a87614c57565b6148a3565b90919761493d898551612988565b5161495b61494b8b88612988565b5188830151898201511092614c77565b1717970190614888565b61496e88614c57565b614884565b61497c88614c57565b614877565b61498a85614c57565b614865565b614997614c68565b6147f4565b60609081810180515161014083015103614b03575b6149d96149d2835173ffffffffffffffffffffffffffffffffffffffff1690565b9483614b6b565b9060008092819282895af193614a0f8673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055821b189415614af9579060409291614a31615dcd565b9590919687614aeb575b0180515182518111614add575b84905b8a818310614ab75750505052825184519281518411614aa9575b9291905b88838510614a80575050505050526148c057918190565b9091929396614a8f8884612988565b51614a9d6148eb8a8a612988565b17179601929190614a69565b614ab288614c57565b614a65565b909198614ac58a8551612988565b51614ad361494b8c88612988565b1717980190614a4b565b614ae689614c57565b614a48565b614af489614c57565b614a3b565b5093505050918190565b614b0b614c68565b6149b1565b91909160408051936020928360e083028701018352818652839160010160051b92838701915b848410614b4557505050505050565b60c060a0879285878c01528460808083893e606083019088013e01930193019291614b36565b9190608490614bd2604051916398919765835260a0601c84019633602086015260806040860152614bbe6060614ba8604084015185890190614bfc565b9283608001828901520151838388010190614bfc565b018094608082016080820152010190614bd7565b010190565b8051603f0163ffffffe0169291610676918491905b829060045afa153d15176101c357565b9081519081815260209283808083019301918460051b0101915b84838210614c29575050505060071b0190565b8160809251805185528281015183860152604080820151908601526060809101519085015201910190614c16565b63939792856000526020526024601cfd5b50632165628a6000526004601cfd5b90815191604081015180156003851116614cb4575b6020809160608401516080850151149060408601511416948451149301519101511416161590565b506040820151600490931460030392614c8c565b9190811015614d09575b60051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea1813603018212156101c3570190565b614d1161293e565b614cd2565b3560058110156101c35790565b5063fed398fc6000526004601cfd5b90815180825260208080930193019160005b828110614d52575050505090565b909192938260a060019287518051614d69816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff168483015260408082015190830152606080820151908301526080908101519082015201950193929101614d44565b90815180825260208080930193019160005b828110614dd3575050505090565b909192938260c060019287518051614dea816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff9081168584015260408083015190840152606080830151908401526080808301519084015260a091820151169082015201950193929101614dc5565b906005821015614e4e5752565b61221e610678565b90815260406020820152614e8360408201835173ffffffffffffffffffffffffffffffffffffffff169052565b602082015173ffffffffffffffffffffffffffffffffffffffff1660608201526101806040830151614efa614ec6610160928360808701526101a0860190614d32565b60608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08683030160a0870152614db3565b93614f0d608082015160c0860190614e41565b60a081015160e085015260c081015191610100928386015260e082015192610120938487015282015192610140938487015282015190850152015191015290565b614f56614f7c565b6002600055565b614f65614f7c565b6003600055565b614f74614f7c565b600201600055565b600160005403614f8857565b637fa8a9876000526004601cfd5b600360005403614fa257565b61067634611d42565b929091833b1561507857604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652816004528260245283604452858060648180855af1156150055750505050604052606052565b85853d61502c575b5063f486bc879052602052604052606052608052600160a05260a4601cfd5b601f3d0160051c9060051c90806003029180821161505f575b505060205a910110615057578561500d565b3d81803e3d90fd5b8080600392028380020360091c92030201018680615045565b83635f15d6726000526020526024601cfd5b9392919091843b1561517257604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528160045282602452836044528460645260a06084528960a452898060c48180855af11561510957505050505060805260a05260c052604052606052565b89893d61512e575b5063f486bc87905260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c908060030291808211615159575b505060205a9101106150575786615111565b8080600392028380020360091c92030201018780615147565b84635f15d6726000526020526024601cfd5b9190428111428411151692831561154b575050565b92919033841461531a576151ab6125a7565b936151e882867f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b908351926002601f601d860116106102e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9d8601101660001461530c5760018085169081604103927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf600593880101831c93808952880160209384820151928560238560e81c94019460e31c1690815285845191185283925b8684106152ec575050505050966152e69161067697986152a5604060002092615514565b600052526040600020907f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b90615320565b85859101938684821c841b16604060002081528786519118520192615281565b506106769495508190615320565b50509050565b909291926000948580528051957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820180519188604103908091600181119687156154aa575b50505085148515151697881561539c575b50505050505050501561538657565b61538e612681565b634f7fb80d6000526004601cfd5b909192939495809798508452604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8401938451957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08201976020600060648b519c7f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8c528d520189845afa9a8b615448575b50505050505252523880808080808080615377565b600051036154565780615433565b3b61538e5761549c57606001906041640101000000835160001a1a159114166154875763815e1d646000526004601cfd5b631f003d0a6000525160001a6020526024601cfd5b638baa579f6000526004601cfd5b9091925060408601908151926060880151851a906154e2575b8752845260208360808660015afa508484528a86525251388080615366565b50601b8360ff1c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841683526154c3565b600981106157ac576011811061566857601581106155ca576017811061557f5760177f403be09941a31d05cfc2f896505811353d45d38743288b016630cce39435476a9114027f1d51df90cba8de7637ca3e8fe1e3511d1dc2f23487d05dbdecb781860c21ac1c1890565b60157fbb40bf8cea3a5a716e2b6eb08bbdac8ec159f82f380783db3c56904f15a43d049114027f3bd8cff538aba49a9c374c806d277181e9651624b3e31111bc0624574f8bca1d1890565b6013811061561d5760137f54b3212a178782f104e0d514b41a9a5c4ca9c980bf6597c3cecbf280917e202a9114027f5a4f867d3d458dabecad65f6201ceeaba0096df2d0c491cc32e6ea4e643500171890565b60117f2d7a3ed6dab270fdb8e054b2ad525f0ce2a8b89cc76c17f0965434740f673a559114027fc3939feff011e53ab8c35ca3370aad54c5df1fc2938cd62543174fa6e7d858771890565b600d811061570e57600f81106156c357600f7fcc4886e37eedd9aacd6c1c2c9247197a621a71282e87a7cbc673f3736d9aa1419114027f1da3eed3ecef6ebaa6e5023c057ec2c75150693fd0dac5c90f4a142f9879fde81890565b600d7f8df51df98847160517f5b1186b4bc3f418d98b8a7f17f1292f392d79d600d79e9114027f6b5b04cbae4fcb1a9d78e7b2dfc51a36933d023cf6e347e03d517b472a8525901890565b600b811061576157600b7f32f4e7485d6485f9f6c255929b9905c62ba919758bbe231f231eaeecf33d810c9114027fbb98d87cc12922b83759626c5f07d72266da9702d19ffad6a514c73a89002f5f1890565b60097f6f0ec38c21f6f583ab7f3c5413c773ffd5344c34fde1d390958e438bf667448f9114027fd1d97d1ef5eaa37a4ee5fbf234e6f6d64eb511eb562221cd7edfbdde0848da051890565b6005811061585257600781106158075760077fb58d772fb09b426b9dece637f61ca9065f2b994f1464b51e9207f55f7c8f59489114027f7ff98d9d4e55d876c5cfac10b43c04039522f3ddfb0ea9bfe70c68cfb5c7cc141890565b60057f25d02425402d882d211a7ab774c0ed6eca048c4d03d9af40132475744753b2a39114027f1c19f71958cdd8f081b4c31f7caf5c010b29d12950be2fa1c95070dc47e30b551890565b600381106158a55760037ff3e8417a785f980bdaf134fa0274a6bf891eeb8195cd94b09d2aa651046e28bc9114027fa02eb7ff164c884e5e2c336dc85f81c6a93329d8e9adf214b32729b894de2af11890565b60017f832c58a5b611aadcfa6a082ac9d04bace53d8278387f10040347b7e98eb5b3029114027fbf8e29b89f29ed9b529c154a63038ffca562f8d7cd1e2545dda53a1b582dde301890565b905460ff8160081c16615941576effffffffffffffffffffffffffffff8160101c169081615921575b505050600190565b60881c1115615932575b8080615919565b61593b906159bb565b3861592b565b50631a5155746000526020526024601cfd5b906000905460ff8160081c166159b2576effffffffffffffffffffffffffffff8160101c169081615988575b50505050600190565b60881c111561599857808061597f565b6159a3575b50600090565b6159ac906159bb565b3861599d565b50905050600090565b6310fda3e16000526020526024601cfd5b9190608082019081356159de8161064b565b33141590600460018211911016166159f557505050565b61067692615a236139a16060604051956317b1f94287526020808801528460408801523382880152016111bf565b6080840152606061014461012085013761014060a08401526101e060c0840152615aa6601c61032461026435615a6f60a08202918261016001906101808a019060051b61020001614bec565b6102a0810160e08801528461032082890160006102e08201526102c084016101008b015260016103008201520152019401926111bf565b615d11565b919082519060808201918251926005841015615b66575b615af3602083019473ffffffffffffffffffffffffffffffffffffffff865116331415906004600182119110161690565b15615b1b575090615b0d9160806106769601519085615bf7565b91519263fb5014fc93615d79565b60049194935051615b2b81612934565b615b3481612934565b03615b605761067693615b549184519460808660601b9301519085615b73565b91639397928593615d79565b50505050565b615b6e610678565b615ac2565b9493919260c060a494615be3614bd2946040519663f4dd92ce8852601c88019a1860a088015260a06020880152615bcd6060615bb66040840151878b0190614bfc565b928360a00160408b0152015185838a010190615cc9565b019160a083016060880152838388010190614bd7565b01809460a082016080820152010190615cad565b9392614bd2906101649392604051936317b1f9428552601c85019760208087015260408601523360608601528151608086015260a082015161012086015260c082015190610140918287015260e08301516101608701528160a0870152615c9d604084015193615c886060615c7261018097888c0190614bfc565b9283870160c08c0152015186838b010190615cc9565b019183830160e0890152848389010190614bd7565b0194859182016101008201520101905b612984602092839283815180845260051b948593019101614bec565b8051908183526020928380808401938560051b01019101915b818110615cf35750505060a0020190565b60a090818481835160045afa153d15176101c3578501920191615ce2565b6020909391937fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615d6a5715615d5a575050565b63fb5014fc90526020526024601cfd5b5063fb5014fc90612230612681565b602090949391947fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615dc4571561223057505050565b50612230612681565b60009081906080803d109060009081908280918515615e70575b8515615df8575b5050505050929190565b91939750919550602094939480920196604051918360c08302840101604052818352839160010160051b98898401905b8a8410615e4d5750505050615e4293949596509501614b10565b913880808080615dee565b60a083879284878901528181863e60608501518286015201920193019290615e28565b9450909150604081803e5190602051913d81113d8411179485615de75794508093506020915060003e60005191602082813e602051903d8260a0028560071b0186011161ffff83861711179460008052615de7565b908135641fffffffe08160051b169060405191602091828285010160405263ffffffff809116845260005b828110615f005750929450505050565b80615f1685848180958c010135168a01016119cd565b82828801015201615ef056fea164736f6c6343000811000a00000000000000000000000000000000f9490004c11cef243f5400493c00ad630000000000000000000000000000000000000000
```
-4. Validate deployments were successful by checking that `Seaport` is returned:
+3. Validate deployments were successful by checking that `Seaport` is returned:
```
-cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000006c3852cbEf3e08E8dF289169EdE581 'name()')
-cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000000006c7676171937C444f6BDe3D6282 'name()')
+cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC 'name()')
```
## Verifying Seaport and ConduitController
After `Seaport` and `ConduitController` are deployed, they are verified as follows:
1. Ensure that `EXPLORER_API_KEY` and `NETWORK_RPC` are set in `.env` appropriately.
-2. Navigate to the `1.1` release tag.
+2. Navigate to the `1.1` release tag on the Seaport repo and build artifacts:
+```
+git clone https://github.com/ProjectOpenSea/seaport && cd seaport
+git checkout 821a049
+yarn build
+```
3. Verify `ConduitController` by calling:
@@ -105,16 +101,24 @@ After `Seaport` and `ConduitController` are deployed, they are verified as follo
npx hardhat verify --network verificationNetwork "0x00000000F9490004C11Cef243f5400493c00Ad63"
```
-4. Verify `Seaport 1.1` by calling:
+4. Navigate to the `1.5` release tag on the Seaport repo and clean up existing artifacts:
+```
+git checkout ab3b5cb
+yarn clean
+```
+5. Open `hardhat.config.ts` and modify the `runs` setting on line 78 to work around a limitation on the max optimization run value on many block explorers (the build artifacts are equivalent):
```
-npx hardhat verify --network verificationNetwork "0x00000000006c3852cbEf3e08E8dF289169EdE581" "0x00000000F9490004C11Cef243f5400493c00Ad63"
+runs: 4_294_967_295 => runs: 9_999_999
```
-5. Navigate to the `1.2` release tag.
+6. Build artifacts:
+```
+yarn build
+```
-6. Verify `Seaport 1.2` by calling:
+7. Verify `Seaport 1.5` by calling:
```
-npx hardhat verify --network verificationNetwork "0x00000000000006c7676171937C444f6BDe3D6282" "0x00000000F9490004C11Cef243f5400493c00Ad63"
+npx hardhat verify --network verificationNetwork "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC" "0x00000000F9490004C11Cef243f5400493c00Ad63"
```
\ No newline at end of file
diff --git a/docs/OrderValidator.md b/docs/OrderValidator.md
new file mode 100644
index 000000000..5d29be2f4
--- /dev/null
+++ b/docs/OrderValidator.md
@@ -0,0 +1,80 @@
+# Seaport Order Validator
+
+The SeaportValidator contract offers various validation methods to ensure that supplied Seaport orders are being constructed correctly. Most contract calls return an `ErrorsAndWarnings` struct with two `uint16` arrays to help developers debug issues with their orders.
+
+See below for the full list of Errors and Warnings.
+
+The contract has been verified and deployed to [0x00000000be3af6882a06323fd3f400a9e6a0dc42](https://etherscan.io/address/0x00000000be3af6882a06323fd3f400a9e6a0dc42#code).
+
+Special thanks to [arr00](https://github.com/arr00), who deployed an earlier version of a SeaportValidator contract which can be found [here](https://etherscan.io/address/0xF75194740067D6E4000000003b350688DD770000#code).
+
+## Errors and Warnings
+| Code | Issue |
+| - | ----------- |
+| 100 | Invalid order format. Ensure offer/consideration follow requirements |
+| 200 | ERC20 identifier must be zero |
+| 201 | ERC20 invalid token |
+| 202 | ERC20 insufficient allowance to conduit |
+| 203 | ERC20 insufficient balance |
+| 300 | ERC721 amount must be one |
+| 301 | ERC721 token is invalid |
+| 302 | ERC721 token with identifier does not exist |
+| 303 | ERC721 not owner of token |
+| 304 | ERC721 conduit not approved |
+| 305 | ERC721 offer item using criteria and more than amount of one requires partial fills |
+| 400 | ERC1155 invalid token |
+| 401 | ERC1155 conduit not approved |
+| 402 | ERC1155 insufficient balance |
+| 500 | Consideration amount must not be zero |
+| 501 | Consideration recipient must not be null address |
+| 502 | Consideration contains extra items |
+| 503 | Private sale cannot be to self |
+| 504 | Zero consideration items |
+| 505 | Duplicate consideration items |
+| 506 | Offerer is not receiving at least one item |
+| 507 | Private Sale Order. Be careful on fulfillment |
+| 508 | Amount velocity is too high. Amount changes over 5% per 30 min if warning and over 50% per 30 min if error |
+| 509 | Amount step large. The steps between each step may be more than expected. Offer items are rounded down and consideration items are rounded up. |
+| 600 | Zero offer items |
+| 601 | Offer amount must not be zero |
+| 602 | More than one offer item |
+| 603 | Native offer item |
+| 604 | Duplicate offer item |
+| 605 | Amount velocity is too high. Amount changes over 5% per 30 min if warning and over 50% per 30 min if error |
+| 606 | Amount step large. The steps between each step may be more than expected. Offer items are rounded down and consideration items are rounded up. |
+| 700 | Primary fee missing |
+| 701 | Primary fee item type incorrect |
+| 702 | Primary fee token incorrect |
+| 703 | Primary fee start amount too low |
+| 704 | Primary fee end amount too low |
+| 705 | Primary fee recipient incorrect |
+| 800 | Order cancelled |
+| 801 | Order fully filled |
+| 802 | Cannot validate status of contract order
+| 900 | End time is before start time |
+| 901 | Order expired |
+| 902 | Order expiration in too long (default 26 weeks) |
+| 903 | Order not active |
+| 904 | Short order duration (default 30 min) |
+| 1000 | Conduit key invalid |
+| 1001 | Conduit does not have canonical Seaport as an open channel |
+| 1100 | Signature invalid |
+| 1101 | Contract orders do not have signatures |
+| 1102 | Signature counter below current counter |
+| 1103 | Signature counter above current counter |
+| 1104 | Signature may be invalid since `totalOriginalConsiderationItems` is not set correctly |
+| 1200 | Creator fee missing |
+| 1201 | Creator fee item type incorrect |
+| 1202 | Creator fee token incorrect |
+| 1203 | Creator fee start amount too low |
+| 1204 | Creator fee end amount too low |
+| 1205 | Creator fee recipient incorrect |
+| 1300 | Native token address must be null address |
+| 1301 | Native token identifier must be zero |
+| 1302 | Native token insufficient balance |
+| 1400 | Zone is invalid |
+| 1401 | Zone rejected order. This order must be fulfilled by the zone. |
+| 1401 | Zone not set. Order unfulfillable |
+| 1500 | Merkle input only has one leaf |
+| 1501 | Merkle input not sorted correctly |
+| 1600 | Contract offerer is invalid |
\ No newline at end of file
diff --git a/docs/SeaportDocumentation.md b/docs/SeaportDocumentation.md
index 93b7f144f..7fa55acd4 100644
--- a/docs/SeaportDocumentation.md
+++ b/docs/SeaportDocumentation.md
@@ -7,6 +7,8 @@ Documentation around creating orders, fulfillment, and interacting with Seaport.
- [Order](#order)
- [Order Fulfillment](#order-fulfillment)
- [Sequence of Events](#sequence-of-events)
+- [Contract Orders](#contract-orders)
+- [Bulk Order Creation](#bulk-order-creation)
- [Known Limitations And Workarounds](#known-limitations-and-workarounds)
## Order
@@ -57,7 +59,7 @@ Orders are fulfilled via one of four methods:
- If the order has an ERC721 item, that item has an amount of `1`.
- If the order has multiple consideration items and all consideration items other than the first consideration item have the same item type as the offered item, the offered item amount is not less than the sum of all consideration item amounts excluding the first consideration item amount.
- Calling one of two "fulfill available" functions, `fulfillAvailableOrders` and `fulfillAvailableAdvancedOrders`, where a group of orders are supplied alongside a group of fulfillments specifying which offer items can be aggregated into distinct transfers and which consideration items can be accordingly aggregated, and where any orders that have been cancelled, have an invalid time, or have already been fully filled will be skipped without causing the rest of the available orders to revert. Additionally, any remaining orders will be skipped once `maximumFulfilled` available orders have been located. Similar to the standard fulfillment method, all offer items will be transferred from the respective offerer to the fulfiller, then all consideration items will be transferred from the fulfiller to the named recipient.
-- Calling one of two "match" functions, `matchOrders` and `matchAdvancedOrders`, where a group of explicit orders are supplied alongside a group of fulfillments specifying which offer items to apply to which consideration items (and with the "advanced" case operating in a similar fashion to the standard method, but supporting partial fills via supplied `numerator` and `denominator` fractional values as well as an optional `extraData` argument that will be supplied as part of a call to the `validateOrder` function when fulfilling restricted order types or to `generateOrder` and `ratifyOrder` as "context" on contract order types). Note that orders fulfilled in this manner do not have an explicit fulfiller; instead, Seaport will simply ensure coincidence of wants across each order. Note also that contract orders do not enforce usage of a specific conduit, but a contract offerer can require the usage of a specific conduit by setting allowances or approval on tokens for specific conduits. If a fulfiller does not supply the correct conduit key, the call will revert. There's currently no endpoint for finding which conduit a given contract offerer prefers.
+- Calling one of two "match" functions, `matchOrders` and `matchAdvancedOrders`, where a group of explicit orders are supplied alongside a group of fulfillments specifying which offer items to apply to which consideration items (and with the "advanced" case operating in a similar fashion to the standard method, but supporting partial fills via supplied `numerator` and `denominator` fractional values as well as an optional `extraData` argument that will be supplied as part of a call to the `validateOrder` function when fulfilling restricted order types or to `generateOrder` and `ratifyOrder` as "context" on contract order types). Note that orders fulfilled in this manner do not have an explicit fulfiller; instead, Seaport will simply ensure coincidence of wants across each order. Note also that contract orders do not enforce usage of a specific conduit, but a Seaport app can require the usage of a specific conduit by setting allowances or approval on tokens for specific conduits. If a fulfiller does not supply the correct conduit key, the call will revert. There's currently no endpoint for finding which conduit a given Seaport app prefers.
While the standard method can technically be used for fulfilling any order, it suffers from key efficiency limitations in certain scenarios:
@@ -65,13 +67,9 @@ While the standard method can technically be used for fulfilling any order, it s
- It requires the fulfiller to approve each consideration item, even if the consideration item can be fulfilled using an offer item (as is commonly the case when fulfilling an order that offers ERC20 items for an ERC721 or ERC1155 item and also includes consideration items with the same ERC20 item type for paying fees).
- It can result in unnecessary transfers, whereas in the "match" case those transfers can be reduced to a more minimal set.
-> Note: When a collection-wide criteria-based item (criteria = 0) is provided as an input to a contract order, the contract offerer has full latitude to choose any identifier they want mid-flight, which differs from the usual behavior. For regular criteria-based orders with identifierOrCriteria = 0, the fulfiller can pick which identifier to receive by providing a CriteriaResolver. For contract offers with identifierOrCriteria = 0, Seaport does not expect a corresponding CriteriaResolver, and will revert if one is provided. See `_getGeneratedOrder` and `_compareItems` for more detail.
+> Note: Calls to Seaport that would fulfill or match a collection of advanced orders can be monitored and where there are unused offer items, it's possible for a third party to claim them. Anyone can monitor the mempool to find calls to `matchOrders` or `matchAdvancedOrders` without "ad-hoc" orders (where the offerer is the caller, hence does not require a signature) and calculate if there are any unused offer item amounts. If there are unused offer item amounts, the third party can frontrun the transaction and supply themselves as the recipient, thereby allowing that third party to claim the unused offer items for themselves. A Seaport app or a zone could prevent this, or the fulfiller can utilize a private mempool, but by default it's possible.
-> Note: Calls to Seaport that would fulfill or match a collection of advanced orders can be monitored and where there are unused offer items, it's possible for a third party to claim them. Anyone can monitor the mempool to find calls to `fulfillAvailableOrders`, `fulfillAvailableAdvancedOrders`, `matchOrders`, `matchAdvancedOrders` and calculate if there are any unused offer item amounts. If there are unused offer item amounts, the third party can create orders with no offer items, but with consideration items mirroring the unused offer items and populate the fulfillment aggregation data to match the unused offer items with the new mirrored consideration items. This would allow the third party to claim the unused offer items. A contract offerer or a zone could prevent this, but by default, it's possible.
-
-> Note: Contract orders can supply additional offer amounts when the order is executed. However, if they supply extra offer items with criteria, on the fly, the fulfiller won't be able to supply the necessary criteria resolvers, which would make fulfilling the order infeasible. Contract offerers should specifically avoid returning criteria-based items and generally avoid mismatches between previewOrder and what's executed on-chain.
-
-> Note: In some cases, contract offerers will be able to lower the value of an offered NFT by transferring out valuable tokens that are attached to the NFT. For example, a contract offerer could modify a property of an NFT when Seaport calls `generateOrder`. Consider using a mirrored order that allows for a post-transfer validation, such as a contract order or a restricted order, in cases like this.
+> Note: Contract orders can supply additional offer amounts when the order is executed. However, if they supply extra offer items with criteria, on the fly, the fulfiller won't be able to supply the necessary criteria resolvers, which would make fulfilling the order infeasible. Seaport apps should specifically avoid returning criteria-based items and generally avoid mismatches between previewOrder and what's executed on-chain.
### Balance and Approval Requirements
@@ -166,6 +164,407 @@ When matching a group of orders via `matchOrders` or `matchAdvancedOrders`, step
- Use either conduit or Seaport directly to source approvals, depending on the original order type
- Ignore each execution where `to == from`
+## Contract Orders
+
+Seaport v1.2 introduced support for a new type of order: the contract order. In brief, a smart contract that implements the `ContractOffererInterface` (referred to as an “Seaport app contract” or "Seaport app" in the docs and a “contract offerer” in the code) can now provide a dynamically generated order (a contract order) in response to a buyer or seller’s contract order request. Support for contract orders puts on-chain liquidity on equal footing with off-chain liquidity in the Seaport ecosystem. Further, the two types of liquidity are now broadly composable.
+
+This unlocks a broad range of Seaport-native functionality, including instant conversion from an order’s specified currency (e.g. WETH) to a fulfiller’s preferred currency (e.g. ETH or DAI), flashloan-enriched functionality, liquidation engines, and more. In general, Seaport apps allow the Seaport community to extend default Seaport functionality. Developers with ideas or use cases that could be implemented as Seaport apps should open PRs in [the Seaport Improvement Protocol (SIP) repo](https://github.com/ProjectOpenSea/SIPs).
+
+### Creating a Seaport App
+
+Anyone can build a Seaport app contract that interfaces with Seaport. A Seaport app just has to comply with the following interface:
+
+```solidity
+interface ContractOffererInterface {
+
+function generateOrder(
+ address fulfiller,
+ SpentItem[] calldata minimumReceived,
+ SpentItem[] calldata maximumSpent,
+ bytes calldata context
+ )
+ external
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
+
+function ratifyOrder(
+ SpentItem[] calldata offer,
+ ReceivedItem[] calldata consideration,
+ bytes calldata context,
+ bytes32[] calldata orderHashes,
+ uint256 contractNonce
+ ) external returns (bytes4 ratifyOrderMagicValue);
+
+function previewOrder(
+ address caller,
+ address fulfiller,
+ SpentItem[] calldata minimumReceived,
+ SpentItem[] calldata maximumSpent,
+ bytes calldata context
+ )
+ external
+ view
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
+
+function getSeaportMetadata()
+ external
+ view
+ returns (
+ string memory name,
+ Schema[] memory schemas
+ );
+}
+```
+
+See the [TestContractOfferer.sol](https://github.com/ProjectOpenSea/seaport/blob/main/contracts/test/TestContractOfferer.sol) file in `./contracts/test/` for an example of an MVP Seaport app contract.
+
+### Arguments and Basic Functionality
+
+When Seaport receives a contract order request from a fulfiller, it calls the Seaport app contract’s `generateOrder` function, which returns an array of `SpentItem`s and an array of `ReceivedItem`s. The Seaport app can adjust the response according to its own rules and if its response falls within the acceptable range specified in the original requester's offer (`minimumReceived`) and consideration (`maximumSpent`) parameters, Seaport will execute the orders. If not, the call will revert.
+
+Note that when a request for a contract order is made, the requester is not supplying a conventional, signed order. Instead, the requester is supplying parameters that specify an acceptable range for the Seaport app to work within.
+
+The `minimumReceived` array represents the smallest set that a requester is willing to accept from the Seaport app contract in the deal, though the Seaport app can provide more. The `maximumSpent` array represents the largest set that a requester is willing to provide to the Seaport app in the deal, though the Seaport app can accept less. In a very straightforward case, the requester's `minimumReceived` array would become the `offer` array on the Seaport app's contract order and the requester's `maximumSpent` array would become the `consideration` array on the Seaport app's contract order. These two guardrails can provide protection against slippage, among other safety functions.
+
+Where a Seaport app provides extra offer items, increases offer item amounts (i.e. where it voluntarily exceeds the `minimumReceived` specified by the requester), removes consideration items, or reduces consideration item amounts (i.e. where it voluntarily demands less than the `maximumSpent` specified by the requester), those changes are collectively referred to as a "rebate." When a Seaport app attempts to provide fewer offer items, decreased offer item amounts, additional consideration items, or increased consideration item amounts, those changes are collectively referred to as a "penalty," and Seaport will catch and reject the order.
+
+An optimal Seaport app should return an order with penalties when its `previewOrder` function is called with unacceptable `minimumReceived` and `maximumSpent` arrays, so that the caller can learn what the Seaport app expects. But it should revert when its `generateOrder` is called with unacceptable `minimumReceived` and `maximumSpent` arrays, so the function fails fast, gets skipped, and avoids wasting gas by leaving the validation to Seaport.
+
+### The Context Concept
+
+The third argument provided to a Seaport app contract is `context`, which functions analogously to a zone’s `extraData` argument. For example, a Seaport app that provides AMM-like functionality might use context to determine which token IDs a buyer prefers or whether to take an “exact in” or “exact out” approach to deriving the order. The `context` is arbitrary bytes, but should be encoded according to a standard provided in [the Seaport Improvement Protocol (SIP) repo](https://github.com/ProjectOpenSea/SIPs).
+
+While it’s still early days for the SIP ecosystem, every order generator contract should eventually be able to find an SIP that provides a `context` encoding and decoding standard that matches its use case. Order generators that adopt one or more SIP-standardized encoding or decoding approaches should signal that fact according to the specifications found in [SIP 5](https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-5.md), which functions analogously to EIP 165.
+
+Context may be left empty, or it may contain all of the information necessary to fulfill the contract order (in place of fleshed-out `minimumReceived` and `maximumSpent` arguments). The latter case should only be utilized when the Seaport app contract in question is known to be reliable, as using the `minimumReceived` and `maximumSpent` arrays will cause Seaport to perform additional validation that the returned order meets the fulfiller’s expectations. Note that `minimumReceived` is optional, but `maximumSpent` is not. Even if the context is doing the majority of the work, `maximumSpent` must still be present as a safeguard.
+
+### Lifecycle
+
+Contract orders are not signed and validated ahead of time like the other Seaport order types, but instead are generated on demand by the Seaport app contract. Order hashes for orders created by order generators are derived on the fly in `_getGeneratedOrder`, based on the Seaport app’s address and the `contractNonce`, which is incremented per order generator on each generated contract order. By virtue of responding to a call from Seaport, a Seaport app is effectively stating that its provided offer is acceptable and valid from its perspective.
+
+The contract order lifecycle contains both a stateful `generateOrder` call to derive the contract order prior to execution and a stateful `ratifyOrder` call performed after execution. This means that contract orders can respond to the condition of e.g. the price of a fungible token before execution and verify post-execution that a flashloan was repaid or a critical feature of an NFT was not changed mid-flight.
+
+### Divergence from Non-Contract Orders
+
+Note that when a collection-wide criteria-based item (criteria = 0) is provided as an input to a contract order, the Seaport app contract has full latitude to choose any identifier they want mid-flight. This deviates from Seaport’s behavior elsewhere, where the fulfiller can pick which identifier to receive by providing a CriteriaResolver. For contract order requests with identifierOrCriteria = 0, Seaport does not expect a corresponding CriteriaResolver, and will revert if one is provided. See `_getGeneratedOrder` and `_compareItems` for more detail.
+
+During fulfillment, contract orders may designate native token (e.g. Ether) offer items; order generator contracts can then send native tokens directly to Seaport as part of the `generateOrder` call (or otherwise), allowing the fulfiller to use those native tokens. Any unused native tokens will be sent to the fulfiller (i.e. the caller). Native tokens can only be sent to Seaport when the reentrancy lock is set, and only then under specific circumstances. This enables conversion between ETH and WETH on-the-fly, among other possibilities. Note that any native tokens sent to Seaport will be immediately spendable by the current (or next) caller. Note also that this is a deviation from Seaport’s behavior elsewhere, where buyers may not supply native tokens as offer items.
+
+Seaport also makes an exception to its normal reentrancy policies for order generator contracts. Order generator contracts may call the receive hook and provide native tokens. Anything that’s available to the Seaport app can be spent, including `msg.value` and balance.
+
+Buyers interacting with order generator contracts should note that in some cases, order generator contracts will be able to lower the value of an offered NFT by transferring out valuable tokens that are attached to the NFT. For example, a Seaport app could modify a property of an NFT it owns when Seaport calls its `generateOrder` function. Consider using a mirrored order that allows for a post-transfer validation, such as a contract order or a restricted order, in cases like this.
+
+# Example Lifecycle Journey
+
+To recap everything discussed above, here’s a description of the lifecycle of an example contract order:
+
+- An EOA buyer calls `fulfillOrder` and passes in an `Order` struct with `OrderParameters` that has `OrderType` of `CONTRACT`. Basically, the order says, "Go to the Seaport app contract at 0x123 and tell it I want to buy at least one Blitmap. Tell the Seaport app that I'm willing to spend up to 10 ETH but no more."
+- `fulfillOrder` calls `_validateAndFulfillAdvancedOrder`, as with other order types.
+- `_validateAndFulfillAdvancedOrder` calls `_validateOrderAndUpdateStatus`, as with other order types.
+- Inside `_validateOrderAndUpdateStatus`, at the point where the code path hits the line `if (orderParameters.orderType == OrderType.CONTRACT) { ...`, the code path for contract orders diverges from the code path for other order types.
+- After some initial checks, `_validateOrderAndUpdateStatus` calls `_getGeneratedOrder`.
+- `_getGeneratedOrder` does a low level call to the targeted order generator's `generateOrder` function.
+- The Seaport app contract can do pretty much anything it wants at this point, but a typical example would include processing the arguments it received, picking some NFTs it’s willing to sell, and returning a `SpentItem` array and a `ReceivedItem` array. In this example narrative, the Seaport app's response says "OK, I'm willing to sell the Blitmaps item for 10 ETH."
+- `_getGeneratedOrder` massages the result of the external `generateOrder` call into Seaport format, does some checks, and then returns the order hash to `_validateOrderAndUpdateStatus`.
+- `_validateOrderAndUpdateStatus` transfers the NFTs and the payment via `_applyFractionsAndTransferEach` and performs further checks, including calling `_assertRestrictedAdvancedOrderValidity`.
+`_assertRestrictedAdvancedOrderValidity` calls the Seaport app contract’s `ratifyOrder` function, which gives the Seaport app a chance to object to the way things played out. If, from the perspective of the Seaport app, something went wrong in the process of the transfer, the Seaport app contract has the opportunity to pass along a revert to Seaport, which will revert the entire `fulfillOrder` function call.
+- If `_assertRestrictedAdvancedOrderValidity` and the other checks all pass, `_validateOrderAndUpdateStatus` emits an `OrderFulfilled` event, and returns `true` to `fulfillOrder`, which in turn returns `true` itself, as with other order types.
+
+Here’s a simplified code example of what the Seaport app contract from the example above might look like:
+
+```solidity
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
+
+/**
+ * @title ExampleContractOfferer
+ * @notice ExampleContractOfferer is a pseudocode sketch of a Seaport app
+ * contract that sells one Blitmaps NFT at a time for 10 or more ETH.
+ */
+contract ExampleContractOfferer is ContractOffererInterface {
+ error OrderUnavailable();
+
+ address private immutable _SEAPORT;
+ address private immutable _BLITMAPS;
+
+ constructor(address seaport, address blitmaps) {
+ _SEAPORT = seaport;
+ _BLITMAPS = blitmaps;
+ }
+
+ receive() external payable {}
+
+ function generateOrder(
+ address,
+ SpentItem[] calldata originalOffer,
+ SpentItem[] calldata originalConsideration,
+ bytes calldata /* context */
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ SpentItem memory _originalOffer = originalOffer[0];
+ SpentItem memory _originalConsideration = originalConsideration[0];
+
+ if (
+ // Ensure that the original prompt was looking for a Blitmaps item.
+ (_originalOffer.token == _BLITMAPS && _originalOffer.amount == 1) &&
+ // Ensure that the original prompt was willing to spend 10 ETH.
+ (_originalConsideration.amount >= 10 ether)
+ ) {
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1);
+ consideration = new ReceivedItem[](1);
+
+ offer[0] = _originalOffer;
+ consideration[0] = ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: 10 ether,
+ recipient: payable(address(this))
+ });
+ } else {
+ revert OrderUnavailable();
+ }
+ }
+
+ function previewOrder(
+ address /* caller */,
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata /* context */
+ )
+ external
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Show what the order would look like given some set of params.
+ // Should match the order that would be generated by `generateOrder`.
+ SpentItem[] memory _offer;
+ ReceivedItem[] memory _consideration;
+ return (_offer, _consideration);
+ }
+
+ function ratifyOrder(
+ SpentItem[] calldata /* offer */,
+ ReceivedItem[] calldata /* consideration */,
+ bytes calldata /* context */,
+ bytes32[] calldata /*orderHashes*/,
+ uint256 /* contractNonce */
+ )
+ external
+ pure
+ virtual
+ override
+ returns (bytes4 /* ratifyOrderMagicValue */)
+ {
+ // Do some post-execution validation here if desired.
+ return ContractOffererInterface.ratifyOrder.selector;
+ }
+
+ /**
+ * @dev Returns the metadata for this contract offerer.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+
+ return ("ExampleContractOfferer", schemas);
+ }
+}
+```
+
+Remember to create a [Seaport Improvement Protocol (SIP)](https://github.com/ProjectOpenSea/SIPs) proposal for any novel Seaport app.
+
+## Bulk Order Creation
+
+Seaport v1.2 introduced a bulk order creation feature. In brief, a buyer or seller can now sign a single bulk order payload that creates multiple orders with one ECDSA signature. So, instead of signing a dozen single order payloads to create a dozen orders, a user can now create the same dozen orders with a single click in their wallet UI.
+
+Bulk signature payloads of depth 1 (2 orders) to depth 24 (16,777,216 orders) are fully supported as of v1.2. Just as with single order signing, bulk order payloads will be typed, human-readable EIP 712 data. Any individual order created in the course of bulk order creation is fulfillable independently. In other words, one order or multiple orders created in the course of bulk order creation can be included in a fulfillment transaction.
+
+Note that there is a gas cost increase associated with fulfilling orders created in the course of bulk order creation. The cost increases logarithmically with the number of orders in the bulk order payload: roughly 4,000 gas for a tree height of 1 and then roughly an additional 700 gas per extra unit of height. Accordingly, it’s advisable to balance the convenience of creating multiple orders at once against the additional gas cost imposed on fulfillers.
+
+### Bulk Order Cancellation
+
+Note that the `incrementCounter` function was modified in v1.2 to increment the counter by a quasi-random value derived from the last block hash. This change prevents the type of situation where a user is tricked into signing a malicious bulk signature payload containing orders that are fulfillable at both the current counter value and future counter values, which would be possible if counters were still incremented serially. Instead, since the counter jumps a very large, quasi-random amount, the effects of a malicious signature can still be neutralized by incrementing the counter a single time. In other words, the change to `incrementCounter` gives buyers and sellers the ability to "hard reset" regardless of what orders might have been unknowingly signed for in a large, malicious bulk order payload.
+
+Note that orders created in the course of bulk order creation still need to be canceled individually. For example, if a maker creates 4 orders in a single bulk order payload, it will take 4 `cancel` transactions to cancel those 4 orders. Alternatively, the maker could call `incrementCounter` once, but that will also cause all of the maker’s other active orders to become unfillable. Users should exercise caution in creating large numbers of orders using bulk order creation and should prefer to regularly create short-lived orders instead of occasionally creating long lasting orders.
+
+### Bulk Order Signing and Structure
+
+A bulk signature is an EIP 712 type Merkle tree where the root is a `BulkOrder` and the leaves are `OrderComponents`. Each level will be either a pair of orders or an order and an array. Each level gets hashed up the tree until it’s all rolled up into a single hash, which gets signed. The signature on the rolled up hash is the ECDSA signature referred to throughout.
+
+A marketplace can either use the signature in combination with the entire set of orders (to fulfill the entire set of orders) or enable the maker to iterate over each order, set the appropriate key, and compute the proof for each order. Then, each proof gets appended onto the end of the ECDSA signature, which allows a fulfiller to target one or more specific orders from the bulk signature payload. See below for more detail.
+
+Because of the Merkle tree structure of the bulk order payload and the limitations of EIP 712, each payload must contain exactly 2^N orders, where 1 ≤ N ≤ 24. If the desired number of orders to sign for is not a permissible value, empty (and hence unfulfillable) orders must be provided to bring the total order count to an acceptable value (4, 8, 16, 32, etc.). In other words, you can create any number of orders between 2 and 2^24, but the bulk signature payload needs to be padded with dummy orders. The dummy orders need to be present and have the right “shape” to make the bulk signature payload play nicely with EIP 712, but they should have no other effect and they should not be actionable. See [the `signSparseBulkOrder` function](https://github.com/ProjectOpenSea/seaport/blob/main/test/foundry/utils/EIP712MerkleTree.sol#L102-L180) in the Seaport Foundry tests, for an example of a bulk signature payload padded with empty orders.
+
+Here’s a diagram of a bulk order payload for the case where a seller wants to list 9 different NFTs at once:
+
+![bulk-sig-payload-diagram](https://user-images.githubusercontent.com/14304708/217900009-5511abef-d5c9-4c91-b6d6-6441a3b9b52a.png)
+
+A valid bulk signature will have a length greater than or equal to 99 (1 x 32 + 67) and less than or equal to 836 (24 x 32 + 68) and will satisfy the following formula: ((length - 67) % 32) ≤ 1, since each proof should be 32 bytes long. The 67 and 68 bytes referenced in the preceding sentences are made up of a 64 or 65 byte ECDSA signature plus a 3 byte index. In other words, the recipe for a valid bulk signature is:
+
+```
+A 64 or 65 byte ECDSA signature
++ a three byte index
++ a series of 32 byte proof elements up to 24 proofs long
+```
+
+If a bulk order payload contains 4 orders, there will be one unique “bulk signature” for each, where 1) the beginning of the bulk signature is the same ECDSA signature for each, then 2) a unique index for each (0-3) depending on which order in the bulk order payload the signature is for, then 3) a series distinct proofs for each order.
+
+For example:
+
+| ECDSA sig | index | proof 1 | proof 2 | proof 3 | proof 4 |
+|-------------|--------|---------|---------|---------|---------|
+| 0x95eb…3e9a | 000000 | 4a…e1 | 9d…3f | 7b…0c | 2d…5b |
+| 0x95eb…3e9a | 000001 | 4a…e1 | 9d…3f | 7b…0c | 2d…5b |
+| 0x95eb…3e9a | 000002 | 4a…e1 | 9d…3f | 7b…0c | 2d…5b |
+| 0x95eb…3e9a | 000003 | 4a…e1 | 9d…3f | 7b…0c | 2d…5b |
+
+This structure allows a fulfiller to disregard the fact that a signature is for a bulk order. A fulfiller can just select the full bulk signature that has the index of the order they want to fulfill and pass it in as if it were a bare signature for a single order. Seaport handles parsing of the bulk signature into its component parts and allows the fulfiller to fulfill exclusively the order they are targeting.
+
+### Bulk Order Construction Example
+
+In JavaScript, the `bulkOrderType` is defined like this:
+
+```javascript
+const bulkOrderType = {
+ BulkOrder: [{ name: "tree", type: "OrderComponents[2][2][2][2][2][2][2]" }],
+ OrderComponents: [
+ { name: "offerer", type: "address" },
+ { name: "zone", type: "address" },
+ { name: "offer", type: "OfferItem[]" },
+ { name: "consideration", type: "ConsiderationItem[]" },
+ { name: "orderType", type: "uint8" },
+ { name: "startTime", type: "uint256" },
+ { name: "endTime", type: "uint256" },
+ { name: "zoneHash", type: "bytes32" },
+ { name: "salt", type: "uint256" },
+ { name: "conduitKey", type: "bytes32" },
+ { name: "counter", type: "uint256" },
+ ],
+ OfferItem: [
+ { name: "itemType", type: "uint8" },
+ { name: "token", type: "address" },
+ { name: "identifierOrCriteria", type: "uint256" },
+ { name: "startAmount", type: "uint256" },
+ { name: "endAmount", type: "uint256" },
+ ],
+ ConsiderationItem: [
+ { name: "itemType", type: "uint8" },
+ { name: "token", type: "address" },
+ { name: "identifierOrCriteria", type: "uint256" },
+ { name: "startAmount", type: "uint256" },
+ { name: "endAmount", type: "uint256" },
+ { name: "recipient", type: "address" },
+ ],
+};
+```
+
+So, an example bulk order object in Javascript might look like this:
+
+```javascript
+const bulkOrder = {
+ name: "tree",
+ type: "OrderComponents[2][2][2][2][2][2][2]",
+ BulkOrder: [{
+ offerer: "0x123...",
+ zone: "0x456...",
+ offer: [{
+ itemType: 1,
+ token: "0x789...",
+ identifierOrCriteria: 123456,
+ startAmount: 100,
+ endAmount: 200
+ }],
+ consideration: [{
+ itemType: 2,
+ token: "0xabc...",
+ identifierOrCriteria: 789012,
+ startAmount: 1,
+ endAmount: 1,
+ recipient: "0xdef..."
+ }],
+ orderType: 0,
+ startTime: 1546300800,
+ endTime: 1546387199,
+ zoneHash: "0x9abcdef...",
+ salt: 123456,
+ conduitKey: "0xabcdef...",
+ counter: 789012345678901234
+ },
+ {
+ offerer: "0x987...",
+ zone: "0x654...",
+ offer: [{
+ itemType: 1,
+ token: "0x321...",
+ identifierOrCriteria: 654321,
+ startAmount: 150,
+ endAmount: 250
+ }],
+ consideration: [{
+ itemType: 2,
+ token: "0xcba...",
+ identifierOrCriteria: 987654,
+ startAmount: 1,
+ endAmount: 1,
+ recipient: "0xfed..."
+ }],
+ orderType: 1,
+ startTime: 1547300800,
+ endTime: 1547387199,
+ zoneHash: "0x1abcdef...",
+ salt: 987654,
+ conduitKey: "0x1abcdef...",
+ counter: 789012345678901234
+ }]
+};
+```
+
+So, creating a bulk signature might happen like this:
+
+```javascript
+const signature = _signTypedData(
+ domainData,
+ bulkOrderType,
+ value
+);
+```
+
+Where `domainData` is the same as it would be for a single order, the `bulkOrderType` is defined as it is above, and the value is a tree of `OrderComponents`, as illustrated above. For an implementation example, see [the `signBulkOrder` function](https://github.com/ProjectOpenSea/seaport-js/blob/2c8e9bee9240c3c7669fc63d4d2a703ea4718d46/src/seaport.ts#L522-L555) in seaport-js.
+
+Note again that the heavy lifting for marketplaces supporting bulk orders happens on the maker signature creation side. On the taker side, a fulfiller will be able to pass in a bulk signature just as if it were a signature for a normal order. For completeness and general interest, the following two paragraphs provide a sketch of how Seaport internally parses bulk signatures.
+
+### Bulk Signature Processing in Seaport
+
+When processing a signature, Seaport will first check if the signature is a bulk signature (a 64 or 65 byte ECDSA signature, followed by a three-byte index, followed by additional proof elements). Then, Seaport will remove the extra data to create a new digest and process the remaining 64 or 65 byte ECDSA signature normally, following the usual code paths starting with signature validation.
+
+In other words, if `_isValidBulkOrderSize` returns true, Seaport will call `_computeBulkOrderProof` using the full `signature` and the `orderHash` that were passed into `_verifySignature` to generate the trimmed ECDSA signature and relevant `bulkOrderHash`. Then, `_deriveEIP712Digest` creates the relevant digest. From that point onwards, Seaport handles the digest and the ECDSA signature normally, starting with `_assertValidSignature`.
+
+
## Known Limitations and Workarounds
- As all offer and consideration items are allocated against one another in memory, there are scenarios in which the actual received item amount will differ from the amount specified by the order — notably, this includes items with a fee-on-transfer mechanic. Orders that contain items of this nature (or, more broadly, items that have some post-fulfillment state that should be met) should leverage "restricted" order types and route the order fulfillment through a zone contract that performs the necessary checks after order fulfillment is completed.
diff --git a/foundry.toml b/foundry.toml
index a86f15a71..06f511f1c 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -10,14 +10,26 @@ remappings = [
'forge-std/=lib/forge-std/src/',
'murky/=lib/murky/src/',
'openzeppelin-contracts/=lib/openzeppelin-contracts/',
- 'seaport-sol/=contracts/helpers/sol/'
+ 'seaport-sol/=contracts/helpers/sol/',
+ 'seaport-core/=contracts/',
+ 'solarray/=lib/solarray/src/',
+ 'solady/=lib/solady/'
]
optimizer_runs = 4_294_967_295
fs_permissions = [
{ access = "read", path = "./optimized-out" },
{ access = "read", path = "./reference-out" },
+ { access = "write", path = "./call-metrics.txt" },
+ { access = "write", path = "./mutation-metrics.txt" },
+ { access = "write", path = "./assume-metrics.txt" },
+ { access = "write", path = "./fuzz_debug.json" }
]
+[profile.validator]
+solc = '0.8.17'
+src = 'contracts/helpers/order-validator'
+optimizer_runs = 1
+
[fuzz]
runs = 1_000
max_test_rejects = 1_000_000
@@ -34,6 +46,7 @@ test = 'reference'
via_ir = true
out = 'optimized-out'
script = 'contracts'
+bytecode_hash = 'none'
# no need to compile tests with via-ir since they load optimized bytecode directly by default
test ='contracts'
@@ -48,6 +61,11 @@ out = 'optimized-out'
[profile.debug]
src = 'contracts'
+optimizer = false
+
+[profile.moat_debug]
+optimizer = false
+test = 'test/foundry/new'
[profile.offerers]
src='offerers'
diff --git a/hardhat-validator.config.ts b/hardhat-validator.config.ts
new file mode 100644
index 000000000..b63190285
--- /dev/null
+++ b/hardhat-validator.config.ts
@@ -0,0 +1,58 @@
+import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names";
+import { subtask } from "hardhat/config";
+
+import type { HardhatUserConfig } from "hardhat/config";
+
+import "dotenv/config";
+import "@nomiclabs/hardhat-ethers";
+import "@nomicfoundation/hardhat-chai-matchers";
+import "@nomiclabs/hardhat-etherscan";
+import "@typechain/hardhat";
+import "hardhat-gas-reporter";
+
+subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(
+ async (_, __, runSuper) => {
+ const paths = await runSuper();
+
+ return paths.filter((p: any) => !p.includes("contracts/"));
+ }
+);
+
+// You need to export an object to set up your config
+// Go to https://hardhat.org/config/ to learn more
+
+const config: HardhatUserConfig = {
+ solidity: {
+ compilers: [
+ {
+ version: "0.8.17",
+ settings: {
+ viaIR: false,
+ optimizer: {
+ enabled: true,
+ runs: 1,
+ },
+ },
+ },
+ ],
+ },
+ networks: {
+ hardhat: {
+ blockGasLimit: 30_000_000,
+ throwOnCallFailures: false,
+ allowUnlimitedContractSize: true,
+ forking: {
+ enabled: true,
+ url: process.env.ETH_RPC_URL ?? "",
+ },
+ },
+ },
+ // specify separate cache for hardhat, since it could possibly conflict with foundry's
+ paths: {
+ sources: "./contracts",
+ tests: "./test/order-validator",
+ cache: "hh-cache",
+ },
+};
+
+export default config;
diff --git a/hardhat.config.ts b/hardhat.config.ts
index 575615292..b0b032f06 100644
--- a/hardhat.config.ts
+++ b/hardhat.config.ts
@@ -109,7 +109,7 @@ const config: HardhatUserConfig = {
},
},
},
- "contracts/helper/TransferHelper.sol": {
+ "contracts/helpers/TransferHelper.sol": {
version: "0.8.14",
settings: {
viaIR: true,
@@ -119,6 +119,16 @@ const config: HardhatUserConfig = {
},
},
},
+ "contracts/helpers/order-validator": {
+ version: "0.8.17",
+ settings: {
+ viaIR: false,
+ optimizer: {
+ enabled: true,
+ runs: 1,
+ },
+ },
+ },
},
},
networks: {
diff --git a/lib/ds-test b/lib/ds-test
index cd98eff28..e282159d5 160000
--- a/lib/ds-test
+++ b/lib/ds-test
@@ -1 +1 @@
-Subproject commit cd98eff28324bfac652e63a239a60632a761790b
+Subproject commit e282159d5170298eb2455a6c05280ab5a73a4ef0
diff --git a/lib/solady b/lib/solady
new file mode 160000
index 000000000..3d5752898
--- /dev/null
+++ b/lib/solady
@@ -0,0 +1 @@
+Subproject commit 3d57528984275d1746ee6597acd36277f51c091d
diff --git a/lib/solarray b/lib/solarray
new file mode 160000
index 000000000..4c3b8ff8e
--- /dev/null
+++ b/lib/solarray
@@ -0,0 +1 @@
+Subproject commit 4c3b8ff8e90c8cd11d30e02c1b6b2fcf9bc0f3db
diff --git a/package.json b/package.json
index e3b277b4c..0afdbf85d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "seaport",
- "version": "1.4.0",
+ "version": "1.5.0",
"description": "Seaport is a marketplace protocol for safely and efficiently buying and selling NFTs. Each listing contains an arbitrary number of items that the offerer is willing to give (the \"offer\") along with an arbitrary number of items that must be received along with their respective receivers (the \"consideration\").",
"main": "contracts/Seaport.sol",
"author": "0age",
@@ -14,7 +14,8 @@
"ethers": "^5.5.3",
"ethers-eip712": "^0.2.0",
"hardhat": "^2.12.1-ir.0",
- "merkletreejs": "^0.3.9"
+ "merkletreejs": "^0.3.9",
+ "solady": "^0.0.84"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
@@ -29,6 +30,7 @@
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"chai": "^4.3.4",
+ "cli-barchart": "^0.2.3",
"dotenv": "^16.0.0",
"eslint": "^8.6.0",
"eslint-config-prettier": "^8.3.0",
@@ -87,6 +89,7 @@
"coverage": "yarn clean; hardhat coverage --config ./hardhat-coverage.config.ts --solcoverjs ./config/.solcover.js",
"coverage:ref": "REFERENCE=true hardhat coverage --config ./hardhat-reference-coverage.config.ts --solcoverjs ./config/.solcover-reference.js",
"coverage:forge": "SEAPORT_COVERAGE=true forge coverage --report summary",
+ "coverage:fuzz": "SEAPORT_COVERAGE=true forge coverage --match-path test/foundry/new/FuzzCoverage.t.sol --report summary --report lcov && lcov -o lcov.info --remove lcov.info --rc lcov_branch_coverage=1 --rc lcov_function_coverage=1 'test/*' 'script/*' 'contracts/helpers/*' 'contracts/test/*' 'contracts/zones/*' 'reference/*' && genhtml lcov.info -o html --branch",
"generate:optimized-yul": "yarn build; jq -r '.output.contracts.\"contracts/Seaport.sol\".Seaport.irOptimized' artifacts/build-info/\"$(jq -r '.buildInfo[17:]' artifacts/contracts/Seaport.sol/Seaport.dbg.json)\" | cat > Seaport.yul",
"lint:check": "yarn lint:check:format && yarn lint:check:solhint && yarn lint:check:eslint",
"lint:check:format": "prettier --check **.{sol,js,ts}",
@@ -98,6 +101,11 @@
"show:headroom": "jq -r '.deployedBytecode' artifacts/contracts/Seaport.sol/Seaport.json | tr -d '\n' | wc -m | awk '{print 24577 - ($1 - 2)/2}'",
"test:forge": "FOUNDRY_PROFILE=reference forge build; FOUNDRY_PROFILE=optimized forge build; FOUNDRY_PROFILE=test forge test -vvv",
"test:forge:lite": "FOUNDRY_PROFILE=reference forge build; FOUNDRY_PROFILE=lite forge test -vvv",
+ "build:validator": "hardhat compile --config ./hardhat-validator.config.ts",
+ "test:validator": "hardhat test --config ./hardhat-validator.config.ts",
+ "test:fuzz": "forge test --mp test/foundry/new/FuzzMain.t.sol",
+ "test:fuzz:concrete": "forge test --mt test_concrete",
+ "test:fuzz:metrics": "yarn ts-node scripts/plot_metrics.ts",
"prepare": "husky install"
},
"lint-staged": {
diff --git a/reference/ReferenceConsideration.sol b/reference/ReferenceConsideration.sol
index 91bd0f569..b41a261b8 100644
--- a/reference/ReferenceConsideration.sol
+++ b/reference/ReferenceConsideration.sol
@@ -28,7 +28,7 @@ import { OrderToExecute } from "./lib/ReferenceConsiderationStructs.sol";
* @author 0age
* @custom:coauthor d1ll0n
* @custom:coauthor transmissions11
- * @custom:version 1.4-reference
+ * @custom:version 1.5-reference
* @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
* marketplace. It minimizes external calls to the greatest extent
* possible and provides lightweight methods for common routes as well
diff --git a/reference/lib/ReferenceConsiderationBase.sol b/reference/lib/ReferenceConsiderationBase.sol
index 75594705c..01391b0e4 100644
--- a/reference/lib/ReferenceConsiderationBase.sol
+++ b/reference/lib/ReferenceConsiderationBase.sol
@@ -25,7 +25,7 @@ contract ReferenceConsiderationBase is
{
// Declare constants for name, version, and reentrancy sentinel values.
string internal constant _NAME = "Consideration";
- string internal constant _VERSION = "1.4-reference";
+ string internal constant _VERSION = "1.5-reference";
uint256 internal constant _NOT_ENTERED = 1;
uint256 internal constant _ENTERED = 2;
diff --git a/reference/lib/ReferenceFulfillmentApplier.sol b/reference/lib/ReferenceFulfillmentApplier.sol
index 665f3d7d7..d3b938468 100644
--- a/reference/lib/ReferenceFulfillmentApplier.sol
+++ b/reference/lib/ReferenceFulfillmentApplier.sol
@@ -166,7 +166,7 @@ contract ReferenceFulfillmentApplier is
* @param recipient The intended recipient for all received
* items.
*
- * @return execution The transfer performed as a result of the fulfillment.
+ * @return _execution The transfer performed as a result of the fulfillment.
*/
function _aggregateAvailable(
OrderToExecute[] memory ordersToExecute,
@@ -174,10 +174,7 @@ contract ReferenceFulfillmentApplier is
FulfillmentComponent[] memory fulfillmentComponents,
bytes32 fulfillerConduitKey,
address recipient
- ) internal view returns (Execution memory execution) {
- // Retrieve orders array length and place on the stack.
- uint256 totalOrders = ordersToExecute.length;
-
+ ) internal view returns (Execution memory _execution) {
// Retrieve fulfillment components array length and place on stack.
uint256 totalFulfillmentComponents = fulfillmentComponents.length;
@@ -186,32 +183,28 @@ contract ReferenceFulfillmentApplier is
revert MissingFulfillmentComponentOnAggregation(side);
}
- // Determine component index after first available (0 implies none).
- uint256 nextComponentIndex = 0;
-
- // Iterate over components until finding one with a fulfilled order.
- for (uint256 i = 0; i < totalFulfillmentComponents; ++i) {
- // Retrieve the fulfillment component index.
- uint256 orderIndex = fulfillmentComponents[i].orderIndex;
+ Execution memory execution;
- // Ensure that the order index is in range.
- if (orderIndex >= totalOrders) {
- revert InvalidFulfillmentComponentData();
- }
-
- // If order is being fulfilled (i.e. it is still available)...
- if (ordersToExecute[orderIndex].numerator != 0) {
- // Update the next potential component index.
- nextComponentIndex = i + 1;
-
- // Exit the loop.
- break;
- }
+ // If the fulfillment components are offer components...
+ if (side == Side.OFFER) {
+ // Return execution for aggregated items provided by offerer.
+ execution = _aggregateValidFulfillmentOfferItems(
+ ordersToExecute,
+ fulfillmentComponents,
+ recipient
+ );
+ } else {
+ // Otherwise, fulfillment components are consideration
+ // components. Return execution for aggregated items provided by
+ // the fulfiller.
+ execution = _aggregateConsiderationItems(
+ ordersToExecute,
+ fulfillmentComponents,
+ fulfillerConduitKey
+ );
}
- // If no available order was located...
- if (nextComponentIndex == 0) {
- // Return with an empty execution element that will be filtered.
+ if (execution.item.amount == 0) {
return
Execution(
ReceivedItem(
@@ -226,26 +219,7 @@ contract ReferenceFulfillmentApplier is
);
}
- // If the fulfillment components are offer components...
- if (side == Side.OFFER) {
- // Return execution for aggregated items provided by offerer.
- return
- _aggregateValidFulfillmentOfferItems(
- ordersToExecute,
- fulfillmentComponents,
- recipient
- );
- } else {
- // Otherwise, fulfillment components are consideration
- // components. Return execution for aggregated items provided by
- // the fulfiller.
- return
- _aggregateConsiderationItems(
- ordersToExecute,
- fulfillmentComponents,
- fulfillerConduitKey
- );
- }
+ return execution;
}
/**
diff --git a/reference/lib/ReferenceOrderCombiner.sol b/reference/lib/ReferenceOrderCombiner.sol
index 4cb34197a..e2fbccc02 100644
--- a/reference/lib/ReferenceOrderCombiner.sol
+++ b/reference/lib/ReferenceOrderCombiner.sol
@@ -141,15 +141,18 @@ contract ReferenceOrderCombiner is
internal
returns (bool[] memory availableOrders, Execution[] memory executions)
{
- // Validate orders, apply amounts, & determine if they utilize conduits
- bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill(
- advancedOrders,
- ordersToExecute,
- criteriaResolvers,
- false, // Signifies that invalid orders should NOT revert.
- maximumFulfilled,
- recipient
- );
+ // Validate orders, apply amounts, & determine if they use conduits.
+ (
+ bytes32[] memory orderHashes,
+ bool containsNonOpen
+ ) = _validateOrdersAndPrepareToFulfill(
+ advancedOrders,
+ ordersToExecute,
+ criteriaResolvers,
+ false, // Signifies that invalid orders should NOT revert.
+ maximumFulfilled,
+ recipient
+ );
// Execute transfers.
(availableOrders, executions) = _executeAvailableFulfillments(
@@ -159,7 +162,8 @@ contract ReferenceOrderCombiner is
considerationFulfillments,
fulfillerConduitKey,
recipient,
- orderHashes
+ orderHashes,
+ containsNonOpen
);
// Return order fulfillment details and executions.
@@ -189,6 +193,9 @@ contract ReferenceOrderCombiner is
* @param recipient The intended recipient for all received items.
*
* @return orderHashes The hashes of the orders being fulfilled.
+ * @return containsNonOpen A boolean indicating whether any restricted or
+ * contract orders are present within the provided
+ * array of advanced orders.
*/
function _validateOrdersAndPrepareToFulfill(
AdvancedOrder[] memory advancedOrders,
@@ -197,12 +204,9 @@ contract ReferenceOrderCombiner is
bool revertOnInvalid,
uint256 maximumFulfilled,
address recipient
- ) internal returns (bytes32[] memory orderHashes) {
- // Read length of orders array and place on the stack.
- uint256 totalOrders = advancedOrders.length;
-
+ ) internal returns (bytes32[] memory orderHashes, bool containsNonOpen) {
// Track the order hash for each order being fulfilled.
- orderHashes = new bytes32[](totalOrders);
+ orderHashes = new bytes32[](advancedOrders.length);
// Determine whether or not order matching is underway.
bool nonMatchFn = msg.sig !=
@@ -214,11 +218,9 @@ contract ReferenceOrderCombiner is
bool anyNativeOfferItemsOnNonContractOrders;
// Iterate over each order.
- for (uint256 i = 0; i < totalOrders; ++i) {
+ for (uint256 i = 0; i < advancedOrders.length; ++i) {
// Retrieve the current order.
AdvancedOrder memory advancedOrder = advancedOrders[i];
- // Retrieve the order to execute.
- OrderToExecute memory orderToExecute = ordersToExecute[i];
// Determine if max number orders have already been fulfilled.
if (maximumFulfilled == 0) {
@@ -226,7 +228,7 @@ contract ReferenceOrderCombiner is
advancedOrder.numerator = 0;
// Mark fill fraction as zero as the order will not be used.
- orderToExecute.numerator = 0;
+ ordersToExecute[i].numerator = 0;
// Continue iterating through the remaining orders.
continue;
@@ -236,7 +238,8 @@ contract ReferenceOrderCombiner is
(
bytes32 orderHash,
uint256 numerator,
- uint256 denominator
+ uint256 denominator,
+ OrderToExecute memory orderToExecute
) = _validateOrderAndUpdateStatus(advancedOrder, revertOnInvalid);
// Do not track hash or adjust prices if order is not fulfilled.
@@ -246,6 +249,7 @@ contract ReferenceOrderCombiner is
// Mark fill fraction as zero as the order will not be used.
orderToExecute.numerator = 0;
+ ordersToExecute[i] = orderToExecute;
// Continue iterating through the remaining orders.
continue;
@@ -257,119 +261,142 @@ contract ReferenceOrderCombiner is
// Decrement the number of fulfilled orders.
maximumFulfilled--;
- // Place the start time for the order on the stack.
- uint256 startTime = advancedOrder.parameters.startTime;
+ {
+ // Retrieve array of offer items for the order in question.
+ OfferItem[] memory offer = advancedOrder.parameters.offer;
- // Place the end for the order on the stack.
- uint256 endTime = advancedOrder.parameters.endTime;
+ // Determine the order type, used to check for eligibility for
+ // native token offer items as well as for the presence of
+ // restricted and contract orders (or non-open orders).
+ OrderType orderType = advancedOrder.parameters.orderType;
- // Retrieve array of offer items for the order in question.
- OfferItem[] memory offer = advancedOrder.parameters.offer;
+ {
+ bool isNonContractOrder = orderType != OrderType.CONTRACT;
+ bool isNonOpenOrder = orderType != OrderType.FULL_OPEN &&
+ orderType != OrderType.PARTIAL_OPEN;
- // Iterate over each offer item on the order.
- for (uint256 j = 0; j < offer.length; ++j) {
- // Retrieve the offer item.
- OfferItem memory offerItem = offer[j];
+ if (containsNonOpen == true || isNonOpenOrder == true) {
+ containsNonOpen = true;
+ }
- anyNativeOfferItemsOnNonContractOrders =
- anyNativeOfferItemsOnNonContractOrders ||
- (offerItem.itemType == ItemType.NATIVE &&
- advancedOrder.parameters.orderType !=
- OrderType.CONTRACT);
+ // Iterate over each offer item on the order.
+ for (uint256 j = 0; j < offer.length; ++j) {
+ // Retrieve the offer item.
+ OfferItem memory offerItem = offer[j];
+
+ // Determine if there are any native offer items on non-contract
+ // orders.
+ anyNativeOfferItemsOnNonContractOrders =
+ anyNativeOfferItemsOnNonContractOrders ||
+ (offerItem.itemType == ItemType.NATIVE &&
+ isNonContractOrder);
+
+ // Apply order fill fraction to offer item end amount.
+ uint256 endAmount = _getFraction(
+ numerator,
+ denominator,
+ offerItem.endAmount
+ );
- // Apply order fill fraction to offer item end amount.
- uint256 endAmount = _getFraction(
- numerator,
- denominator,
- offerItem.endAmount
- );
+ // Reuse same fraction if start and end amounts are equal.
+ if (offerItem.startAmount == offerItem.endAmount) {
+ // Apply derived amount to both start and end amount.
+ offerItem.startAmount = endAmount;
+ } else {
+ // Apply order fill fraction to offer item start amount.
+ offerItem.startAmount = _getFraction(
+ numerator,
+ denominator,
+ offerItem.startAmount
+ );
+ }
+
+ // Update end amount in memory to match the derived amount.
+ offerItem.endAmount = endAmount;
+
+ // Adjust offer amount using current time; round down.
+ offerItem.startAmount = _locateCurrentAmount(
+ offerItem.startAmount,
+ offerItem.endAmount,
+ advancedOrder.parameters.startTime,
+ advancedOrder.parameters.endTime,
+ false // Round down.
+ );
- // Reuse same fraction if start and end amounts are equal.
- if (offerItem.startAmount == offerItem.endAmount) {
- // Apply derived amount to both start and end amount.
- offerItem.startAmount = endAmount;
- } else {
- // Apply order fill fraction to offer item start amount.
- offerItem.startAmount = _getFraction(
- numerator,
- denominator,
- offerItem.startAmount
- );
+ // Modify the OrderToExecute Spent Item Amount.
+ orderToExecute.spentItems[j].amount = offerItem
+ .startAmount;
+ // Modify the OrderToExecute Spent Item Original Amount.
+ orderToExecute.spentItemOriginalAmounts[j] = offerItem
+ .startAmount;
+ }
}
- // Update end amount in memory to match the derived amount.
- offerItem.endAmount = endAmount;
-
- // Adjust offer amount using current time; round down.
- offerItem.startAmount = _locateCurrentAmount(
- offerItem.startAmount,
- offerItem.endAmount,
- startTime,
- endTime,
- false // Round down.
- );
+ {
+ // Retrieve array of consideration items for order in question.
+ ConsiderationItem[] memory consideration = (
+ advancedOrder.parameters.consideration
+ );
- // Modify the OrderToExecute Spent Item Amount.
- orderToExecute.spentItems[j].amount = offerItem.startAmount;
- // Modify the OrderToExecute Spent Item Original Amount.
- orderToExecute.spentItemOriginalAmounts[j] = offerItem
- .startAmount;
- }
+ // Iterate over each consideration item on the order.
+ for (uint256 j = 0; j < consideration.length; ++j) {
+ // Retrieve the consideration item.
+ ConsiderationItem memory considerationItem = (
+ consideration[j]
+ );
- // Retrieve array of consideration items for order in question.
- ConsiderationItem[] memory consideration = (
- advancedOrder.parameters.consideration
- );
+ // Apply fraction to consideration item end amount.
+ uint256 endAmount = _getFraction(
+ numerator,
+ denominator,
+ considerationItem.endAmount
+ );
- // Iterate over each consideration item on the order.
- for (uint256 j = 0; j < consideration.length; ++j) {
- // Retrieve the consideration item.
- ConsiderationItem memory considerationItem = (consideration[j]);
+ // Reuse same fraction if start and end amounts are equal.
+ if (
+ considerationItem.startAmount ==
+ considerationItem.endAmount
+ ) {
+ // Apply derived amount to both start and end amount.
+ considerationItem.startAmount = endAmount;
+ } else {
+ // Apply fraction to consideration item start amount.
+ considerationItem.startAmount = _getFraction(
+ numerator,
+ denominator,
+ considerationItem.startAmount
+ );
+ }
+
+ // TODO: Check with 0. Appears to be no longer in optimized.
+ // // Update end amount in memory to match the derived amount.
+ // considerationItem.endAmount = endAmount;
+
+ uint256 currentAmount = (
+ _locateCurrentAmount(
+ considerationItem.startAmount,
+ endAmount,
+ advancedOrder.parameters.startTime,
+ advancedOrder.parameters.endTime,
+ true // round up
+ )
+ );
- // Apply fraction to consideration item end amount.
- uint256 endAmount = _getFraction(
- numerator,
- denominator,
- considerationItem.endAmount
- );
+ considerationItem.startAmount = currentAmount;
- // Reuse same fraction if start and end amounts are equal.
- if (
- considerationItem.startAmount == considerationItem.endAmount
- ) {
- // Apply derived amount to both start and end amount.
- considerationItem.startAmount = endAmount;
- } else {
- // Apply fraction to consideration item start amount.
- considerationItem.startAmount = _getFraction(
- numerator,
- denominator,
- considerationItem.startAmount
- );
+ // Modify the OrderToExecute Received item amount.
+ orderToExecute
+ .receivedItems[j]
+ .amount = considerationItem.startAmount;
+ // Modify the OrderToExecute Received item original amount.
+ orderToExecute.receivedItemOriginalAmounts[
+ j
+ ] = considerationItem.startAmount;
+ }
}
-
- // Update end amount in memory to match the derived amount.
- considerationItem.endAmount = endAmount;
-
- // Adjust consideration amount using current time; round up.
- considerationItem.startAmount = (
- _locateCurrentAmount(
- considerationItem.startAmount,
- considerationItem.endAmount,
- startTime,
- endTime,
- true // Round up.
- )
- );
-
- // Modify the OrderToExecute Received item amount.
- orderToExecute.receivedItems[j].amount = considerationItem
- .startAmount;
- // Modify the OrderToExecute Received item original amount.
- orderToExecute.receivedItemOriginalAmounts[
- j
- ] = considerationItem.startAmount;
}
+
+ ordersToExecute[i] = orderToExecute;
}
if (anyNativeOfferItemsOnNonContractOrders && nonMatchFn) {
@@ -381,7 +408,7 @@ contract ReferenceOrderCombiner is
// Emit an event for each order signifying that it has been fulfilled.
// Iterate over each order.
- for (uint256 i = 0; i < totalOrders; ++i) {
+ for (uint256 i = 0; i < advancedOrders.length; ++i) {
// Do not emit an event if no order hash is present.
if (orderHashes[i] == bytes32(0)) {
continue;
@@ -459,6 +486,9 @@ contract ReferenceOrderCombiner is
* @param recipient The intended recipient for all received
* items.
* @param orderHashes An array of order hashes for each order.
+ * @param containsNonOpen A boolean indicating whether any restricted or
+ * contract orders are present within the provided
+ * array of advanced orders.
*
* @return availableOrders An array of booleans indicating if each
* order with an index corresponding to the
@@ -475,7 +505,8 @@ contract ReferenceOrderCombiner is
FulfillmentComponent[][] memory considerationFulfillments,
bytes32 fulfillerConduitKey,
address recipient,
- bytes32[] memory orderHashes
+ bytes32[] memory orderHashes,
+ bool containsNonOpen
)
internal
returns (bool[] memory availableOrders, Execution[] memory executions)
@@ -498,14 +529,11 @@ contract ReferenceOrderCombiner is
// Iterate over each offer fulfillment.
for (uint256 i = 0; i < totalOfferFulfillments; ++i) {
- /// Retrieve the offer fulfillment components in question.
- FulfillmentComponent[] memory components = (offerFulfillments[i]);
-
// Derive aggregated execution corresponding with fulfillment.
Execution memory execution = _aggregateAvailable(
ordersToExecute,
Side.OFFER,
- components,
+ offerFulfillments[i],
fulfillerConduitKey,
recipient
);
@@ -526,18 +554,13 @@ contract ReferenceOrderCombiner is
// Iterate over each consideration fulfillment.
for (uint256 i = 0; i < totalConsiderationFulfillments; ++i) {
- /// Retrieve consideration fulfillment components in question.
- FulfillmentComponent[] memory components = (
- considerationFulfillments[i]
- );
-
// Derive aggregated execution corresponding with fulfillment.
Execution memory execution = _aggregateAvailable(
ordersToExecute,
Side.CONSIDERATION,
- components,
+ considerationFulfillments[i],
fulfillerConduitKey,
- recipient // unused
+ address(0) // unused
);
// If offerer and recipient on the execution are the same and the
@@ -591,7 +614,8 @@ contract ReferenceOrderCombiner is
ordersToExecute,
executions,
orderHashes,
- recipient
+ recipient,
+ containsNonOpen
);
return (availableOrders, executions);
@@ -611,6 +635,9 @@ contract ReferenceOrderCombiner is
* transfers to perform when fulfilling the given
* orders.
* @param orderHashes An array of order hashes for each order.
+ * @param containsNonOpen A boolean indicating whether any restricted or
+ * contract orders are present within the provided
+ * array of advanced orders.
*
* @return availableOrders An array of booleans indicating if each order
* with an index corresponding to the index of the
@@ -621,11 +648,9 @@ contract ReferenceOrderCombiner is
OrderToExecute[] memory ordersToExecute,
Execution[] memory executions,
bytes32[] memory orderHashes,
- address recipient
+ address recipient,
+ bool containsNonOpen
) internal returns (bool[] memory availableOrders) {
- // Put ether value supplied by the caller on the stack.
- uint256 nativeTokensRemaining = msg.value;
-
// Retrieve the length of the advanced orders array and place on stack.
uint256 totalOrders = advancedOrders.length;
@@ -645,12 +670,9 @@ contract ReferenceOrderCombiner is
// If execution transfers native tokens, reduce value available.
if (item.itemType == ItemType.NATIVE) {
// Ensure that sufficient native tokens are still available.
- if (item.amount > nativeTokensRemaining) {
+ if (item.amount > address(this).balance) {
revert InsufficientNativeTokensSupplied();
}
-
- // Reduce ether remaining by amount.
- nativeTokensRemaining -= item.amount;
}
// Transfer the item specified by the execution.
@@ -661,12 +683,9 @@ contract ReferenceOrderCombiner is
accumulatorStruct
);
}
-
- // Trigger remaining accumulated transfers via call to the conduit.
- _triggerIfArmed(accumulatorStruct);
}
- // duplicate recipient onto stack to avoid stack-too-deep
+ // Duplicate recipient onto stack to avoid stack-too-deep.
address _recipient = recipient;
// Iterate over orders to ensure all consideration items are met.
@@ -767,39 +786,45 @@ contract ReferenceOrderCombiner is
}
}
- // Trigger any remaining accumulated transfers via call to the conduit.
+ // Trigger any remaining accumulated transfers via call to the
+ // conduit.
_triggerIfArmed(accumulatorStruct);
// If any native token remains after fulfillments, return it to the
// caller.
- if (nativeTokensRemaining != 0) {
- _transferNativeTokens(payable(msg.sender), nativeTokensRemaining);
+ if (address(this).balance != 0) {
+ _transferNativeTokens(payable(msg.sender), address(this).balance);
}
- // Iterate over orders to ensure all consideration items are met.
- for (uint256 i = 0; i < ordersToExecute.length; ++i) {
- // Retrieve the order in question.
- OrderToExecute memory orderToExecute = ordersToExecute[i];
+ // If any restricted or contract orders are present in the group of
+ // orders being fulfilled, perform any validateOrder or ratifyOrder
+ // calls after all executions and related transfers are complete.
+ if (containsNonOpen) {
+ // Iterate over orders to ensure all consideration items are met.
+ for (uint256 i = 0; i < ordersToExecute.length; ++i) {
+ // Retrieve the order in question.
+ OrderToExecute memory orderToExecute = ordersToExecute[i];
+
+ // Skip consideration item checks for order if not fulfilled.
+ if (orderToExecute.numerator == 0) {
+ continue;
+ }
- // Skip consideration item checks for order if not fulfilled.
- if (orderToExecute.numerator == 0) {
- continue;
+ // Retrieve the original order in question.
+ AdvancedOrder memory advancedOrder = advancedOrders[i];
+
+ // Ensure restricted orders have valid submitter or pass check.
+ _assertRestrictedAdvancedOrderValidity(
+ advancedOrder,
+ orderToExecute,
+ orderHashes,
+ orderHashes[i],
+ advancedOrder.parameters.zoneHash,
+ advancedOrder.parameters.orderType,
+ orderToExecute.offerer,
+ advancedOrder.parameters.zone
+ );
}
-
- // Retrieve the original order in question.
- AdvancedOrder memory advancedOrder = advancedOrders[i];
-
- // Ensure restricted orders have valid submitter or pass check.
- _assertRestrictedAdvancedOrderValidity(
- advancedOrder,
- orderToExecute,
- orderHashes,
- orderHashes[i],
- advancedOrder.parameters.zoneHash,
- advancedOrder.parameters.orderType,
- orderToExecute.offerer,
- advancedOrder.parameters.zone
- );
}
// Return the array containing available orders.
@@ -886,14 +911,17 @@ contract ReferenceOrderCombiner is
);
// Validate orders, apply amounts, & determine if they utilize conduits.
- bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill(
- advancedOrders,
- ordersToExecute,
- criteriaResolvers,
- true, // Signifies that invalid orders should revert.
- advancedOrders.length,
- recipient
- );
+ (
+ bytes32[] memory orderHashes,
+ bool containsNonOpen
+ ) = _validateOrdersAndPrepareToFulfill(
+ advancedOrders,
+ ordersToExecute,
+ criteriaResolvers,
+ true, // Signifies that invalid orders should revert.
+ advancedOrders.length,
+ recipient
+ );
// Emit OrdersMatched event.
emit OrdersMatched(orderHashes);
@@ -905,7 +933,8 @@ contract ReferenceOrderCombiner is
ordersToExecute,
fulfillments,
orderHashes,
- recipient
+ recipient,
+ containsNonOpen
);
}
@@ -923,6 +952,10 @@ contract ReferenceOrderCombiner is
* be considered valid.
* @param orderHashes An array of order hashes for each order.
*
+ * @param containsNonOpen A boolean indicating whether any restricted or
+ * contract orders are present within the provided
+ * array of advanced orders.
+ *
* @return executions An array of elements indicating the sequence
* of transfers performed as part of
* matching the given orders.
@@ -932,7 +965,8 @@ contract ReferenceOrderCombiner is
OrderToExecute[] memory ordersToExecute,
Fulfillment[] calldata fulfillments,
bytes32[] memory orderHashes,
- address recipient
+ address recipient,
+ bool containsNonOpen
) internal returns (Execution[] memory executions) {
// Retrieve fulfillments array length and place on the stack.
uint256 totalFulfillments = fulfillments.length;
@@ -991,7 +1025,8 @@ contract ReferenceOrderCombiner is
ordersToExecute,
executions,
orderHashes,
- recipient
+ recipient,
+ containsNonOpen
);
// Return executions.
diff --git a/reference/lib/ReferenceOrderFulfiller.sol b/reference/lib/ReferenceOrderFulfiller.sol
index 374429887..0b3287e6b 100644
--- a/reference/lib/ReferenceOrderFulfiller.sol
+++ b/reference/lib/ReferenceOrderFulfiller.sol
@@ -89,7 +89,8 @@ contract ReferenceOrderFulfiller is
(
bytes32 orderHash,
uint256 fillNumerator,
- uint256 fillDenominator
+ uint256 fillDenominator,
+ OrderToExecute memory orderToExecute
) = _validateOrderAndUpdateStatus(advancedOrder, true);
// Apply criteria resolvers using generated orders and details arrays.
@@ -99,7 +100,7 @@ contract ReferenceOrderFulfiller is
OrderParameters memory orderParameters = advancedOrder.parameters;
// Perform each item transfer with the appropriate fractional amount.
- OrderToExecute memory orderToExecute = _applyFractionsAndTransferEach(
+ orderToExecute = _applyFractionsAndTransferEach(
orderParameters,
fillNumerator,
fillDenominator,
@@ -237,9 +238,6 @@ contract ReferenceOrderFulfiller is
orderParameters.consideration.length
);
- // Put ether value supplied by the caller on the stack.
- uint256 nativeTokensRemaining = msg.value;
-
// Declare a nested scope to minimize stack depth.
{
// Iterate over each consideration on the order.
@@ -268,14 +266,11 @@ contract ReferenceOrderFulfiller is
// Add ReceivedItem to structs array.
orderToExecute.receivedItems[i] = receivedItem;
- // Reduce available value if offer spent ETH or a native token.
if (receivedItem.itemType == ItemType.NATIVE) {
// Ensure that sufficient native tokens are still available.
- if (amount > nativeTokensRemaining) {
+ if (amount > address(this).balance) {
revert InsufficientNativeTokensSupplied();
}
- // Reduce ether remaining by amount.
- nativeTokensRemaining -= amount;
}
// Transfer item from caller to recipient specified by the item.
@@ -292,9 +287,9 @@ contract ReferenceOrderFulfiller is
_triggerIfArmed(accumulatorStruct);
// If any native token remains after fulfillments...
- if (nativeTokensRemaining != 0) {
+ if (address(this).balance != 0) {
// return it to the caller.
- _transferNativeTokens(payable(msg.sender), nativeTokensRemaining);
+ _transferNativeTokens(payable(msg.sender), address(this).balance);
}
// Return the order to execute.
return orderToExecute;
@@ -347,118 +342,4 @@ contract ReferenceOrderFulfiller is
// Return the array of advanced orders.
return advancedOrders;
}
-
- /**
- * @dev Internal pure function to convert an advanced order to an order
- * to execute with numerator of 1.
- *
- * @param advancedOrder The advanced order to convert.
- *
- * @return orderToExecute The new order to execute.
- */
- function _convertAdvancedToOrder(
- AdvancedOrder memory advancedOrder
- ) internal pure returns (OrderToExecute memory orderToExecute) {
- // Retrieve the advanced orders offers.
- OfferItem[] memory offer = advancedOrder.parameters.offer;
-
- // Create an array of spent items equal to the offer length.
- SpentItem[] memory spentItems = new SpentItem[](offer.length);
- uint256[] memory spentItemOriginalAmounts = new uint256[](offer.length);
-
- // Iterate over each offer item on the order.
- for (uint256 i = 0; i < offer.length; ++i) {
- // Retrieve the offer item.
- OfferItem memory offerItem = offer[i];
-
- // Create spent item for event based on the offer item.
- SpentItem memory spentItem = SpentItem(
- offerItem.itemType,
- offerItem.token,
- offerItem.identifierOrCriteria,
- offerItem.startAmount
- );
-
- // Add to array of spent items.
- spentItems[i] = spentItem;
- spentItemOriginalAmounts[i] = offerItem.startAmount;
- }
-
- // Retrieve the consideration array from the advanced order.
- ConsiderationItem[] memory consideration = advancedOrder
- .parameters
- .consideration;
-
- // Create an array of received items equal to the consideration length.
- ReceivedItem[] memory receivedItems = new ReceivedItem[](
- consideration.length
- );
- // Create an array of uint256 values equal in length to the
- // consideration length containing the amounts of each item.
- uint256[] memory receivedItemOriginalAmounts = new uint256[](
- consideration.length
- );
-
- // Iterate over each consideration item on the order.
- for (uint256 i = 0; i < consideration.length; ++i) {
- // Retrieve the consideration item.
- ConsiderationItem memory considerationItem = (consideration[i]);
-
- // Create received item for event based on the consideration item.
- ReceivedItem memory receivedItem = ReceivedItem(
- considerationItem.itemType,
- considerationItem.token,
- considerationItem.identifierOrCriteria,
- considerationItem.startAmount,
- considerationItem.recipient
- );
-
- // Add to array of received items.
- receivedItems[i] = receivedItem;
-
- // Add to array of received item amounts.
- receivedItemOriginalAmounts[i] = considerationItem.startAmount;
- }
-
- // Create the order to execute from the advanced order data.
- orderToExecute = OrderToExecute(
- advancedOrder.parameters.offerer,
- spentItems,
- receivedItems,
- advancedOrder.parameters.conduitKey,
- advancedOrder.numerator,
- spentItemOriginalAmounts,
- receivedItemOriginalAmounts
- );
-
- // Return the order.
- return orderToExecute;
- }
-
- /**
- * @dev Internal pure function to convert an array of advanced orders to
- * an array of orders to execute.
- *
- * @param advancedOrders The advanced orders to convert.
- *
- * @return ordersToExecute The new array of orders.
- */
- function _convertAdvancedToOrdersToExecute(
- AdvancedOrder[] memory advancedOrders
- ) internal pure returns (OrderToExecute[] memory ordersToExecute) {
- // Read the number of orders from memory and place on the stack.
- uint256 totalOrders = advancedOrders.length;
-
- // Allocate new empty array for each advanced order in memory.
- ordersToExecute = new OrderToExecute[](totalOrders);
-
- // Iterate over the given orders.
- for (uint256 i = 0; i < totalOrders; ++i) {
- // Convert and update array.
- ordersToExecute[i] = _convertAdvancedToOrder(advancedOrders[i]);
- }
-
- // Return the array of orders to execute
- return ordersToExecute;
- }
}
diff --git a/reference/lib/ReferenceOrderValidator.sol b/reference/lib/ReferenceOrderValidator.sol
index da4b506d0..ddbc0aa37 100644
--- a/reference/lib/ReferenceOrderValidator.sol
+++ b/reference/lib/ReferenceOrderValidator.sol
@@ -30,6 +30,8 @@ import {
ReferenceGenerateOrderReturndataDecoder
} from "./ReferenceGenerateOrderReturndataDecoder.sol";
+import { OrderToExecute } from "./ReferenceConsiderationStructs.sol";
+
/**
* @title OrderValidator
* @author 0age
@@ -119,7 +121,8 @@ contract ReferenceOrderValidator is
returns (
bytes32 orderHash,
uint256 newNumerator,
- uint256 newDenominator
+ uint256 newDenominator,
+ OrderToExecute memory orderToExecute
)
{
// Retrieve the parameters for the order.
@@ -134,7 +137,12 @@ contract ReferenceOrderValidator is
)
) {
// Assuming an invalid time and no revert, return zeroed out values.
- return (bytes32(0), 0, 0);
+ return (
+ bytes32(0),
+ 0,
+ 0,
+ _convertAdvancedToOrder(orderParameters, 0)
+ );
}
// Read numerator and denominator from memory and place on the stack.
@@ -156,7 +164,8 @@ contract ReferenceOrderValidator is
);
}
- // Ensure that the supplied numerator and denominator are valid.
+ // Ensure that the supplied numerator and denominator are valid. The
+ // numerator should not exceed denominator and should not be zero.
if (numerator > denominator || numerator == 0) {
revert BadFraction();
}
@@ -186,7 +195,12 @@ contract ReferenceOrderValidator is
)
) {
// Assuming an invalid order status and no revert, return zero fill.
- return (orderHash, 0, 0);
+ return (
+ orderHash,
+ 0,
+ 0,
+ _convertAdvancedToOrder(orderParameters, 0)
+ );
}
// If the order is not already validated, verify the supplied signature.
@@ -264,7 +278,30 @@ contract ReferenceOrderValidator is
}
// Return order hash, new numerator and denominator.
- return (orderHash, uint120(numerator), uint120(denominator));
+ return (
+ orderHash,
+ uint120(numerator),
+ uint120(denominator),
+ _convertAdvancedToOrder(orderParameters, uint120(numerator))
+ );
+ }
+
+ function _callGenerateOrder(
+ OrderParameters memory orderParameters,
+ bytes memory context,
+ SpentItem[] memory originalOfferItems,
+ SpentItem[] memory originalConsiderationItems
+ ) internal returns (bool success, bytes memory returnData) {
+ return
+ orderParameters.offerer.call(
+ abi.encodeWithSelector(
+ ContractOffererInterface.generateOrder.selector,
+ msg.sender,
+ originalOfferItems,
+ originalConsiderationItems,
+ context
+ )
+ );
}
/**
@@ -292,7 +329,12 @@ contract ReferenceOrderValidator is
bool revertOnInvalid
)
internal
- returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
+ returns (
+ bytes32 orderHash,
+ uint256 numerator,
+ uint256 denominator,
+ OrderToExecute memory orderToExecute
+ )
{
// Ensure that consideration array length is equal to the total original
// consideration items value.
@@ -303,18 +345,6 @@ contract ReferenceOrderValidator is
revert ConsiderationLengthNotEqualToTotalOriginal();
}
- {
- // Increment contract nonce and use it to derive order hash. Note:
- // nonce will be incremented even for skipped orders, and even if
- // generateOrder's return data does not satisfy all the constraints.
- uint256 contractNonce = _contractNonces[orderParameters.offerer]++;
- // Derive order hash from contract nonce and offerer address.
- orderHash = bytes32(
- contractNonce ^
- (uint256(uint160(orderParameters.offerer)) << 96)
- );
- }
-
// Convert offer and consideration to spent and received items.
(
SpentItem[] memory originalOfferItems,
@@ -330,17 +360,27 @@ contract ReferenceOrderValidator is
{
// Do a low-level call to get success status and any return data.
- (bool success, bytes memory returnData) = orderParameters
- .offerer
- .call(
- abi.encodeWithSelector(
- ContractOffererInterface.generateOrder.selector,
- msg.sender,
- originalOfferItems,
- originalConsiderationItems,
- context
- )
+ (bool success, bytes memory returnData) = _callGenerateOrder(
+ orderParameters,
+ context,
+ originalOfferItems,
+ originalConsiderationItems
+ );
+
+ {
+ // Increment contract nonce and use it to derive order hash.
+ // Note: nonce will be incremented even for skipped orders, and
+ // even if generateOrder's return data doesn't meet constraints.
+ uint256 contractNonce = (
+ _contractNonces[orderParameters.offerer]++
+ );
+
+ // Derive order hash from contract nonce and offerer address.
+ orderHash = bytes32(
+ contractNonce ^
+ (uint256(uint160(orderParameters.offerer)) << 96)
);
+ }
// If call succeeds, try to decode offer and consideration items.
if (success) {
@@ -366,6 +406,8 @@ contract ReferenceOrderValidator is
}
{
+ orderToExecute = _convertAdvancedToOrder(orderParameters, 1);
+
// Designate lengths.
uint256 originalOfferLength = orderParameters.offer.length;
uint256 newOfferLength = offer.length;
@@ -374,16 +416,42 @@ contract ReferenceOrderValidator is
if (originalOfferLength > newOfferLength) {
revert InvalidContractOrder(orderHash);
} else if (newOfferLength > originalOfferLength) {
- // If new offer items are added, extend the original offer.
- OfferItem[] memory extendedOffer = new OfferItem[](
- newOfferLength
- );
- // Copy original offer items to new array.
- for (uint256 i = 0; i < originalOfferLength; ++i) {
- extendedOffer[i] = orderParameters.offer[i];
+ {
+ // If new offer items are added, extend the original offer.
+ OfferItem[] memory extendedOffer = new OfferItem[](
+ newOfferLength
+ );
+ // Copy original offer items to new array.
+ for (uint256 i = 0; i < originalOfferLength; ++i) {
+ extendedOffer[i] = orderParameters.offer[i];
+ }
+ // Update order parameters with extended offer.
+ orderParameters.offer = extendedOffer;
}
- // Update order parameters with extended offer.
- orderParameters.offer = extendedOffer;
+ {
+ // Do the same for ordersToExecute arrays.
+ SpentItem[] memory extendedSpent = new SpentItem[](
+ newOfferLength
+ );
+ uint256[]
+ memory extendedSpentItemOriginalAmounts = new uint256[](
+ newOfferLength
+ );
+
+ // Copy original spent items to new array.
+ for (uint256 i = 0; i < originalOfferLength; ++i) {
+ extendedSpent[i] = orderToExecute.spentItems[i];
+ extendedSpentItemOriginalAmounts[i] = orderToExecute
+ .spentItemOriginalAmounts[i];
+ }
+
+ // Update order to execute with extended items.
+ orderToExecute.spentItems = extendedSpent;
+ orderToExecute
+ .spentItemOriginalAmounts = extendedSpentItemOriginalAmounts;
+ }
+
+ {}
}
// Loop through each new offer and ensure the new amounts are at
@@ -423,6 +491,8 @@ contract ReferenceOrderValidator is
// Update the original amounts to use the generated amounts.
originalOffer.startAmount = newOffer.amount;
originalOffer.endAmount = newOffer.amount;
+ orderToExecute.spentItems[i].amount = newOffer.amount;
+ orderToExecute.spentItemOriginalAmounts[i] = newOffer.amount;
}
// Add new offer items if there are more than original.
@@ -435,6 +505,12 @@ contract ReferenceOrderValidator is
originalOffer.identifierOrCriteria = newOffer.identifier;
originalOffer.startAmount = newOffer.amount;
originalOffer.endAmount = newOffer.amount;
+
+ orderToExecute.spentItems[i].itemType = newOffer.itemType;
+ orderToExecute.spentItems[i].token = newOffer.token;
+ orderToExecute.spentItems[i].identifier = newOffer.identifier;
+ orderToExecute.spentItems[i].amount = newOffer.amount;
+ orderToExecute.spentItemOriginalAmounts[i] = newOffer.amount;
}
}
@@ -470,7 +546,7 @@ contract ReferenceOrderValidator is
// All fields must match the originally supplied fields except
// for the amount (which may be reduced by the contract offerer)
- // and the recipient if not provided by the recipient.
+ // and the recipient if some non-zero address has been provided.
if (
originalConsideration.startAmount !=
originalConsideration.endAmount ||
@@ -491,24 +567,61 @@ contract ReferenceOrderValidator is
originalConsideration.startAmount = newConsideration.amount;
originalConsideration.endAmount = newConsideration.amount;
originalConsideration.recipient = newConsideration.recipient;
+
+ orderToExecute.receivedItems[i].amount = newConsideration
+ .amount;
+ orderToExecute.receivedItems[i].recipient = newConsideration
+ .recipient;
+ orderToExecute.receivedItemOriginalAmounts[i] = newConsideration
+ .amount;
}
- // Shorten original consideration array if longer than new array.
- ConsiderationItem[] memory shortenedConsiderationArray = (
- new ConsiderationItem[](newConsiderationLength)
- );
+ {
+ // Shorten original consideration array if longer than new array.
+ ConsiderationItem[] memory shortenedConsiderationArray = (
+ new ConsiderationItem[](newConsiderationLength)
+ );
+
+ // Iterate over original consideration array and copy to new.
+ for (uint256 i = 0; i < newConsiderationLength; ++i) {
+ shortenedConsiderationArray[i] = originalConsiderationArray[
+ i
+ ];
+ }
+
+ // Replace original consideration array with new shortend array.
+ orderParameters.consideration = shortenedConsiderationArray;
+ }
+
+ {
+ ReceivedItem[] memory shortenedReceivedItems = (
+ new ReceivedItem[](newConsiderationLength)
+ );
+
+ // Iterate over original consideration array and copy to new.
+ for (uint256 i = 0; i < newConsiderationLength; ++i) {
+ shortenedReceivedItems[i] = orderToExecute.receivedItems[i];
+ }
+
+ orderToExecute.receivedItems = shortenedReceivedItems;
+ }
+ uint256[]
+ memory shortenedReceivedItemOriginalAmounts = new uint256[](
+ newConsiderationLength
+ );
// Iterate over original consideration array and copy to new.
for (uint256 i = 0; i < newConsiderationLength; ++i) {
- shortenedConsiderationArray[i] = originalConsiderationArray[i];
+ shortenedReceivedItemOriginalAmounts[i] = orderToExecute
+ .receivedItemOriginalAmounts[i];
}
- // Replace original consideration array with new shortend array.
- orderParameters.consideration = shortenedConsiderationArray;
+ orderToExecute
+ .receivedItemOriginalAmounts = shortenedReceivedItemOriginalAmounts;
}
// Return the order hash, the numerator, and the denominator.
- return (orderHash, 1, 1);
+ return (orderHash, 1, 1, orderToExecute);
}
/**
@@ -718,13 +831,18 @@ contract ReferenceOrderValidator is
)
internal
pure
- returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
+ returns (
+ bytes32 orderHash,
+ uint256 numerator,
+ uint256 denominator,
+ OrderToExecute memory emptyOrder
+ )
{
// If invalid input should not revert...
if (!revertOnInvalid) {
// Return the contract order hash and zero values for the numerator
// and denominator.
- return (contractOrderHash, 0, 0);
+ return (contractOrderHash, 0, 0, emptyOrder);
}
// Otherwise, revert.
@@ -831,4 +949,121 @@ contract ReferenceOrderValidator is
// The "full" order types are even, while "partial" order types are odd.
isFullOrder = uint256(orderType) & 1 == 0;
}
+
+ /**
+ * @dev Internal pure function to convert an advanced order to an order
+ * to execute.
+ *
+ * @param orderParameters The order to convert.
+ *
+ * @return orderToExecute The new order to execute.
+ */
+ function _convertAdvancedToOrder(
+ OrderParameters memory orderParameters,
+ uint120 numerator
+ ) internal pure returns (OrderToExecute memory orderToExecute) {
+ // Retrieve the advanced orders offers.
+ OfferItem[] memory offer = orderParameters.offer;
+
+ // Create an array of spent items equal to the offer length.
+ SpentItem[] memory spentItems = new SpentItem[](offer.length);
+ uint256[] memory spentItemOriginalAmounts = new uint256[](offer.length);
+
+ // Iterate over each offer item on the order.
+ for (uint256 i = 0; i < offer.length; ++i) {
+ // Retrieve the offer item.
+ OfferItem memory offerItem = offer[i];
+
+ // Create spent item for event based on the offer item.
+ SpentItem memory spentItem = SpentItem(
+ offerItem.itemType,
+ offerItem.token,
+ offerItem.identifierOrCriteria,
+ offerItem.startAmount
+ );
+
+ // Add to array of spent items.
+ spentItems[i] = spentItem;
+ spentItemOriginalAmounts[i] = offerItem.startAmount;
+ }
+
+ // Retrieve the consideration array from the advanced order.
+ ConsiderationItem[] memory consideration = orderParameters
+ .consideration;
+
+ // Create an array of received items equal to the consideration length.
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ consideration.length
+ );
+ // Create an array of uint256 values equal in length to the
+ // consideration length containing the amounts of each item.
+ uint256[] memory receivedItemOriginalAmounts = new uint256[](
+ consideration.length
+ );
+
+ // Iterate over each consideration item on the order.
+ for (uint256 i = 0; i < consideration.length; ++i) {
+ // Retrieve the consideration item.
+ ConsiderationItem memory considerationItem = (consideration[i]);
+
+ // Create received item for event based on the consideration item.
+ ReceivedItem memory receivedItem = ReceivedItem(
+ considerationItem.itemType,
+ considerationItem.token,
+ considerationItem.identifierOrCriteria,
+ considerationItem.startAmount,
+ considerationItem.recipient
+ );
+
+ // Add to array of received items.
+ receivedItems[i] = receivedItem;
+
+ // Add to array of received item amounts.
+ receivedItemOriginalAmounts[i] = considerationItem.startAmount;
+ }
+
+ // Create the order to execute from the advanced order data.
+ orderToExecute = OrderToExecute(
+ orderParameters.offerer,
+ spentItems,
+ receivedItems,
+ orderParameters.conduitKey,
+ numerator,
+ spentItemOriginalAmounts,
+ receivedItemOriginalAmounts
+ );
+
+ // Return the order.
+ return orderToExecute;
+ }
+
+ /**
+ * @dev Internal pure function to convert an array of advanced orders to
+ * an array of orders to execute.
+ *
+ * @param advancedOrders The advanced orders to convert.
+ *
+ * @return ordersToExecute The new array of orders.
+ */
+ function _convertAdvancedToOrdersToExecute(
+ AdvancedOrder[] memory advancedOrders
+ ) internal pure returns (OrderToExecute[] memory ordersToExecute) {
+ // Read the number of orders from memory and place on the stack.
+ uint256 totalOrders = advancedOrders.length;
+
+ // Allocate new empty array for each advanced order in memory.
+ ordersToExecute = new OrderToExecute[](totalOrders);
+
+ // Iterate over the given orders.
+ for (uint256 i = 0; i < totalOrders; ++i) {
+ // Convert and update array.
+ ordersToExecute[i] = _convertAdvancedToOrder(
+ advancedOrders[i].parameters,
+ advancedOrders[i].numerator
+ );
+ }
+
+ // Return the array of orders to execute
+ return ordersToExecute;
+ }
}
diff --git a/reference/lib/ReferenceZoneInteraction.sol b/reference/lib/ReferenceZoneInteraction.sol
index 59f8ddbbe..f314017ee 100644
--- a/reference/lib/ReferenceZoneInteraction.sol
+++ b/reference/lib/ReferenceZoneInteraction.sol
@@ -152,7 +152,7 @@ contract ReferenceZoneInteraction is ZoneInteractionErrors {
orderToExecute.receivedItems,
advancedOrder.extraData,
orderHashes,
- uint96(uint256(orderHash))
+ uint256(orderHash) ^ (uint256(uint160(offerer)) << 96)
) != ContractOffererInterface.ratifyOrder.selector
) {
revert InvalidContractOrder(orderHash);
@@ -212,10 +212,14 @@ contract ReferenceZoneInteraction is ZoneInteractionErrors {
.additionalRecipients[i];
amount = additionalRecipient.amount;
receivedItems[i + 1] = ReceivedItem({
- itemType: considerationItemType,
- token: token,
+ itemType: offerItemType == ItemType.ERC20
+ ? ItemType.ERC20
+ : considerationItemType,
+ token: offerItemType == ItemType.ERC20
+ ? parameters.offerToken
+ : token,
amount: amount,
- identifier: identifier,
+ identifier: offerItemType == ItemType.ERC20 ? 0 : identifier,
recipient: additionalRecipient.recipient
});
}
diff --git a/script/SeaportDeployer.s.sol b/script/SeaportDeployer.s.sol
new file mode 100644
index 000000000..d9ebbe154
--- /dev/null
+++ b/script/SeaportDeployer.s.sol
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.4;
+
+import "forge-std/Script.sol";
+
+import { Seaport } from "contracts/Seaport.sol";
+
+interface ImmutableCreate2Factory {
+ function safeCreate2(
+ bytes32 salt,
+ bytes calldata initializationCode
+ ) external payable returns (address deploymentAddress);
+}
+
+// NOTE: This script assumes that the CREATE2-related contracts have already been deployed.
+contract SeaportDeployer is Script {
+ ImmutableCreate2Factory private constant IMMUTABLE_CREATE2_FACTORY =
+ ImmutableCreate2Factory(0x0000000000FFe8B47B3e2130213B802212439497);
+ address private constant CONDUIT_CONTROLLER =
+ 0x00000000F9490004C11Cef243f5400493c00Ad63;
+ address private constant SEAPORT_ADDRESS =
+ 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC;
+
+ function run() public {
+ // Utilizes the locally-defined PRIVATE_KEY environment variable to sign txs.
+ vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
+
+ // CREATE2 salt (20-byte caller or zero address + 12-byte salt).
+ bytes32 salt = 0x0000000000000000000000000000000000000000d4b6fcc21169b803f25d2210;
+
+ // Packed and ABI-encoded contract bytecode and constructor arguments.
+ // NOTE: The Seaport contract *must* be compiled using the optimized profile config.
+ bytes memory initCode = abi.encodePacked(
+ type(Seaport).creationCode,
+ abi.encode(CONDUIT_CONTROLLER)
+ );
+
+ // Deploy the Seaport contract via ImmutableCreate2Factory.
+ address seaport = IMMUTABLE_CREATE2_FACTORY.safeCreate2(salt, initCode);
+
+ // Verify that the deployed contract address matches what we're expecting.
+ assert(seaport == SEAPORT_ADDRESS);
+
+ vm.stopBroadcast();
+ }
+}
diff --git a/scripts/find_optimizer_runs.sh b/scripts/find_optimizer_runs.sh
new file mode 100755
index 000000000..9e469b1b6
--- /dev/null
+++ b/scripts/find_optimizer_runs.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# h/t @DrakeEvansV1 & ChatGPT
+
+TARGET_SIZE=24.576
+MIN_RUNS=1
+# NOTE that at time of writing, Etherscan does not support verifying contracts
+# that specify more than 10,000,000 optimizer runs.
+# Higher numbers do not always result in different bytecode output. If a
+# higher number of runs is used, it may be possible verify by spoofing with a
+# number that results in the same bytecode output. This is not guaranteed.
+MAX_RUNS=$((2**32-1))
+ENV_FILE=".env"
+FOUND_RUNS=0
+
+# Check if the optimizer is enabled
+OPTIMIZER_STATUS=$(forge config | grep optimizer | head -n 1)
+if [ "$OPTIMIZER_STATUS" != "optimizer = true" ]; then
+ echo "Error: The optimizer is not enabled. Please enable it and try again."
+ exit 1
+fi
+
+try_runs() {
+ local RUNS=$1
+ printf "Trying with FOUNDRY_OPTIMIZER_RUNS=%d\n" "$RUNS"
+ RESULT=$(FOUNDRY_OPTIMIZER_RUNS=$RUNS forge build --sizes | grep Seaport | head -n 1)
+ CONTRACT_SIZE=$(echo $RESULT | awk -F'|' '{print $3}' | awk '{print $1}')
+ [ "$(echo "$CONTRACT_SIZE<=$TARGET_SIZE" | bc)" -eq 1 ]
+}
+
+if try_runs $MAX_RUNS; then
+ FOUND_RUNS=$MAX_RUNS
+else
+ while [ $MIN_RUNS -le $MAX_RUNS ]; do
+ MID_RUNS=$(( (MIN_RUNS + MAX_RUNS) / 2 ))
+
+ if try_runs $MID_RUNS; then
+ printf "Success with FOUNDRY_OPTIMIZER_RUNS=%d and contract size %.3fKB\n" "$MID_RUNS" "$CONTRACT_SIZE"
+ MIN_RUNS=$((MID_RUNS + 1))
+ FOUND_RUNS=$MID_RUNS
+ else
+ printf "Failure with FOUNDRY_OPTIMIZER_RUNS=%d and contract size %.3fKB\n" "$MID_RUNS" "$CONTRACT_SIZE"
+ MAX_RUNS=$((MID_RUNS - 1))
+ fi
+ done
+fi
+
+printf "Highest FOUNDRY_OPTIMIZER_RUNS found: %d\n" "$FOUND_RUNS"
+
+if [ -f "$ENV_FILE" ]; then
+ if grep -q "^FOUNDRY_OPTIMIZER_RUNS=" "$ENV_FILE"; then
+ 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"
+ else
+ echo "FOUNDRY_OPTIMIZER_RUNS=$FOUND_RUNS" >> "$ENV_FILE"
+ fi
+ printf "Updated %s with FOUNDRY_OPTIMIZER_RUNS=%d\n" "$ENV_FILE" "$FOUND_RUNS"
+else
+ printf "Error: %s not found.\n" "$ENV_FILE"
+fi
diff --git a/scripts/plot_metrics.ts b/scripts/plot_metrics.ts
new file mode 100644
index 000000000..8bac4d00c
--- /dev/null
+++ b/scripts/plot_metrics.ts
@@ -0,0 +1,42 @@
+import barChart from "cli-barchart";
+import fs from "fs";
+
+function plotMetrics() {
+ const file = process.argv.length > 2 ? process.argv[2] : "call-metrics.txt";
+ const counter = new Map();
+
+ const metricsData = fs.readFileSync(file, "utf-8");
+ const lines = metricsData.split("\n");
+
+ for (const line of lines) {
+ if (line.trim() === "") continue;
+ const [metric] = line.split("|");
+ const [call] = metric.split(":");
+
+ counter.set(call, (counter.get(call) ?? 0) + 1);
+ }
+
+ const data = Array.from(counter.entries())
+ .map(([key, value]) => ({
+ key,
+ value,
+ }))
+ .sort((a, b) => a.key.localeCompare(b.key));
+ const totalRuns = data.reduce((acc, item) => acc + item.value, 0);
+
+ type Item = { key: string; value: number };
+ const renderLabel = (_item: Item, index: number) => {
+ const percent = ((data[index].value / totalRuns) * 100).toFixed(2);
+ return `${data[index].value.toString()} (${percent}%)`;
+ };
+
+ const options = {
+ renderLabel,
+ };
+
+ const chart = barChart(data, options);
+ console.log(`Fuzz test metrics (${totalRuns} runs):\n`);
+ console.log(chart);
+}
+
+plotMetrics();
diff --git a/templates/GenericEnumerableSet.template b/templates/GenericEnumerableSet.template
new file mode 100644
index 000000000..0a65a4c77
--- /dev/null
+++ b/templates/GenericEnumerableSet.template
@@ -0,0 +1,91 @@
+// // SPDX-License-Identifier: MIT
+// pragma solidity ^0.8.17;
+
+// import { } from "seaport-sol/SeaportSol.sol";
+
+// struct Set {
+// mapping(bytes32 => uint256) offByOneIndex;
+// [] enumeration;
+// }
+
+// library SetLib {
+// error NotPresent();
+
+// function add(
+// Set storage set,
+// memory value
+// ) internal returns (bool added) {
+// // add value to enumeration; hash it to set its entry in the offByOneIndex
+// bytes32 key = keccak256(abi.encode(value));
+// if (set.offByOneIndex[key] == 0) {
+// set.enumeration.push(value);
+// set.offByOneIndex[key] = set.enumeration.length;
+// added = true;
+// } else {
+// added = false;
+// }
+// }
+
+// // remove value from enumeration and replace it with last member of enumeration
+// // if not last member, update offByOneIndex of last member
+// function remove(
+// Set storage set,
+// memory value
+// ) internal returns (bool removed) {
+// bytes32 key = keccak256(abi.encode(value));
+// uint256 index = set.offByOneIndex[key];
+// if (index > 0) {
+// uint256 lastIndex = set.enumeration.length - 1;
+// memory lastValue = set.enumeration[lastIndex];
+// set.enumeration[index - 1] = lastValue;
+// bytes32 lastKey = keccak256(abi.encode(lastValue));
+// // if lastKey is the same as key, then we are removing the last element; do not update it
+// if (lastKey != key) {
+// set.offByOneIndex[lastKey] = index;
+// }
+// set.enumeration.pop();
+// delete set.offByOneIndex[key];
+// removed = true;
+// } else {
+// removed = false;
+// }
+// }
+
+// function removeAll(Set storage set, [] memory values) internal {
+// for (uint256 i = 0; i < values.length; i++) {
+// remove(set, values[i]);
+// }
+// }
+
+// function removeAll(Set storage set, [][] memory values) internal {
+// for (uint256 i = 0; i < values.length; i++) {
+// removeAll(set, values[i]);
+// }
+// }
+
+// function contains(
+// Set storage set,
+// memory value
+// ) internal view returns (bool) {
+// return set.offByOneIndex[keccak256(abi.encode(value))] > 0;
+// }
+
+// function length(Set storage set) internal view returns (uint256) {
+// return set.enumeration.length;
+// }
+
+// function at(
+// Set storage set,
+// uint256 index
+// ) internal view returns ( memory) {
+// return set.enumeration[index];
+// }
+
+// function clear(Set storage set) internal {
+// while (set.enumeration.length > 0) {
+// memory component = set.enumeration[set.enumeration.length - 1];
+// delete set.offByOneIndex[keccak256(abi.encode(component))];
+// set.enumeration.pop();
+// }
+// }
+// }
diff --git a/templates/GenericStructSortLib.template b/templates/GenericStructSortLib.template
new file mode 100644
index 000000000..e50231bac
--- /dev/null
+++ b/templates/GenericStructSortLib.template
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { } from "seaport-sol/SeaportSol.sol";
+
+library SortLib {
+ function key( memory component) internal pure returns (uint256);
+
+ function sort([] memory components) internal pure {
+ sort(components, key);
+ }
+
+ // Sorts the array in-place with intro-quicksort.
+ function sort(
+ [] memory a,
+ function( memory) internal pure returns (uint256) accessor
+ ) internal pure {
+ if (a.length < 2) {
+ return;
+ }
+
+ uint256[] memory stack = new uint256[](2 * a.length);
+ uint256 stackIndex = 0;
+
+ uint256 l = 0;
+ uint256 h = a.length - 1;
+
+ stack[stackIndex++] = l;
+ stack[stackIndex++] = h;
+
+ while (stackIndex > 0) {
+ h = stack[--stackIndex];
+ l = stack[--stackIndex];
+
+ if (h - l <= 12) {
+ // Insertion sort for small subarrays
+ for (uint256 i = l + 1; i <= h; i++) {
+ memory k = a[i];
+ uint256 j = i;
+ while (j > l && accessor(a[j - 1]) > accessor(k)) {
+ a[j] = a[j - 1];
+ j--;
+ }
+ a[j] = k;
+ }
+ } else {
+ // Intro-Quicksort
+ uint256 p = (l + h) / 2;
+
+ // Median of 3
+ if (accessor(a[l]) > accessor(a[p])) {
+ (a[l], a[p]) = (a[p], a[l]);
+ }
+ if (accessor(a[l]) > accessor(a[h])) {
+ (a[l], a[h]) = (a[h], a[l]);
+ }
+ if (accessor(a[p]) > accessor(a[h])) {
+ (a[p], a[h]) = (a[h], a[p]);
+ }
+
+ uint256 pivot = accessor(a[p]);
+ uint256 i = l;
+ uint256 j = h;
+
+ while (i <= j) {
+ while (accessor(a[i]) < pivot) {
+ i++;
+ }
+ while (accessor(a[j]) > pivot) {
+ j--;
+ }
+ if (i <= j) {
+ (a[i], a[j]) = (a[j], a[i]);
+ i++;
+ j--;
+ }
+ }
+
+ if (j > l) {
+ stack[stackIndex++] = l;
+ stack[stackIndex++] = j;
+ }
+ if (i < h) {
+ stack[stackIndex++] = i;
+ stack[stackIndex++] = h;
+ }
+ }
+ }
+ }
+}
diff --git a/templates/replace.sh b/templates/replace.sh
new file mode 100755
index 000000000..62432ed7e
--- /dev/null
+++ b/templates/replace.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# check if both arguments are supplied
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 "
+ exit 1
+fi
+
+# check if file exists
+if [ ! -f "$1" ]; then
+ echo "File $1 not found"
+ exit 1
+fi
+
+# replace all instances of XXX with second argument
+sed "s/\/$2/g" "$1"
diff --git a/test/foundry/BulkSignature.t.sol b/test/foundry/BulkSignature.t.sol
index 10dae8d99..0e667eff4 100644
--- a/test/foundry/BulkSignature.t.sol
+++ b/test/foundry/BulkSignature.t.sol
@@ -10,11 +10,11 @@ import {
} from "../../contracts/interfaces/ConsiderationInterface.sol";
import {
- OrderComponents,
- OrderParameters,
ConsiderationItem,
OfferItem,
Order,
+ OrderComponents,
+ OrderParameters,
OrderType
} from "../../contracts/lib/ConsiderationStructs.sol";
@@ -115,8 +115,6 @@ contract BulkSignatureTest is BaseOrderTest {
signature: bulkSignature
});
context.seaport.fulfillOrder{ value: 1 }(order, bytes32(0));
-
- // merkleTree.
}
function testBulkSignatureSparse() public {
@@ -175,9 +173,6 @@ contract BulkSignatureTest is BaseOrderTest {
);
configureOrderParameters(addr);
configureOrderComponents(context.seaport);
- OrderComponents[] memory orderComponents = new OrderComponents[](3);
- orderComponents[0] = baseOrderComponents;
- // The other order components can remain empty.
EIP712MerkleTree merkleTree = new EIP712MerkleTree();
bytes memory bulkSignature = merkleTree.signSparseBulkOrder(
diff --git a/test/foundry/FulfillBasicOrderTest.t.sol b/test/foundry/FulfillBasicOrderTest.t.sol
index b6e308d0b..6a3fa1824 100644
--- a/test/foundry/FulfillBasicOrderTest.t.sol
+++ b/test/foundry/FulfillBasicOrderTest.t.sol
@@ -741,7 +741,7 @@ contract FulfillBasicOrderTest is BaseOrderTest, ConsiderationEventsAndErrors {
vm.prank(alice);
- vm.expectEmit(true, true, true, false, address(context.consideration));
+ vm.expectEmit(false, true, true, false, address(context.consideration));
emit OrderCancelled(orderHash, alice, context.args.zone);
context.consideration.cancel(myBaseOrderComponents);
diff --git a/test/foundry/FulfillOrderTest.t.sol b/test/foundry/FulfillOrderTest.t.sol
index 57e4d3ffe..ce1aa0eb2 100644
--- a/test/foundry/FulfillOrderTest.t.sol
+++ b/test/foundry/FulfillOrderTest.t.sol
@@ -274,7 +274,7 @@ contract FulfillOrderTest is BaseOrderTest {
startTime + 1000,
false // don't round up offers
);
- vm.expectEmit(true, true, true, false, address(token1));
+ vm.expectEmit(true, true, false, true, address(token1));
emit Transfer(alice, address(this), expectedAmount);
context.consideration.fulfillOrder{ value: 1000 }(
Order(orderParameters, signature),
@@ -350,7 +350,7 @@ contract FulfillOrderTest is BaseOrderTest {
true // round up considerations
);
token1.mint(address(this), expectedAmount);
- vm.expectEmit(true, true, true, false, address(token1));
+ vm.expectEmit(true, true, false, true, address(token1));
emit Transfer(address(this), address(alice), expectedAmount);
context.consideration.fulfillOrder(
Order(orderParameters, signature),
diff --git a/test/foundry/GetterTests.t.sol b/test/foundry/GetterTests.t.sol
index 6697185d3..83563d8aa 100644
--- a/test/foundry/GetterTests.t.sol
+++ b/test/foundry/GetterTests.t.sol
@@ -41,7 +41,7 @@ contract TestGetters is BaseConsiderationTest {
function testGetsCorrectVersion() public {
(string memory version, , ) = consideration.information();
- assertEq(version, "1.4");
+ assertEq(version, "1.5");
}
function testGetCorrectDomainSeparator() public {
diff --git a/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol b/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol
index 1e4c56bd7..b2e42c293 100644
--- a/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol
+++ b/test/foundry/MatchAdvancedOrderUnspentOffer.t.sol
@@ -198,7 +198,8 @@ contract MatchOrderUnspentOfferTest is BaseOrderTest {
assertEq(recordedLogs[4].emitter, address(token1));
}
- function testSweepRemaining() public {
+ // TODO: look into sporadic failures here
+ function xtestSweepRemaining() public {
test(this.execSweepRemaining, Context({ seaport: consideration }));
test(
this.execSweepRemaining,
@@ -219,7 +220,6 @@ contract MatchOrderUnspentOfferTest is BaseOrderTest {
token1.mint(offerer, 10000);
vm.prank(fulfiller);
test721_1.setApprovalForAll(address(context.seaport), true);
- vm.stopPrank();
vm.prank(offerer);
token1.approve(address(context.seaport), type(uint256).max);
@@ -303,7 +303,8 @@ contract MatchOrderUnspentOfferTest is BaseOrderTest {
assertEq(endingToken1Balance, startingToken1Balance + 200);
}
- function testSweepRemainingAdvanced() public {
+ // TODO: look into sporadic failures here
+ function xtestSweepRemainingAdvanced() public {
test(
this.execSweepRemainingAdvanced,
Context({ seaport: consideration })
diff --git a/test/foundry/NonReentrant.t.sol b/test/foundry/NonReentrant.t.sol
index 67013aff7..8268fd8fd 100644
--- a/test/foundry/NonReentrant.t.sol
+++ b/test/foundry/NonReentrant.t.sol
@@ -116,10 +116,10 @@ contract NonReentrantTest is BaseOrderTest {
if (!reentering) {
shouldReenter = true;
vm.expectEmit(
- true,
false,
false,
false,
+ true,
address(address(this))
);
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
@@ -132,10 +132,10 @@ contract NonReentrantTest is BaseOrderTest {
if (!reentering) {
shouldReenter = true;
vm.expectEmit(
- true,
false,
false,
false,
+ true,
address(address(this))
);
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
@@ -151,7 +151,7 @@ contract NonReentrantTest is BaseOrderTest {
uint256 value
) = prepareOrder(tokenId);
if (!reentering) {
- vm.expectEmit(true, false, false, true, address(this));
+ vm.expectEmit(false, false, false, true, address(this));
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
}
currentConsideration.fulfillOrder{ value: value }(
@@ -166,7 +166,7 @@ contract NonReentrantTest is BaseOrderTest {
uint256 value
) = prepareAdvancedOrder(tokenId);
if (!reentering) {
- vm.expectEmit(true, false, false, true, address(this));
+ vm.expectEmit(false, false, false, true, address(this));
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
}
currentConsideration.fulfillAdvancedOrder{ value: value }(
@@ -184,7 +184,7 @@ contract NonReentrantTest is BaseOrderTest {
uint256 maximumFulfilled
) = prepareAvailableOrders(tokenId);
if (!reentering) {
- vm.expectEmit(true, false, false, true, address(this));
+ vm.expectEmit(false, false, false, true, address(this));
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
}
vm.prank(alice);
@@ -205,7 +205,7 @@ contract NonReentrantTest is BaseOrderTest {
uint256 maximumFulfilled
) = prepareFulfillAvailableAdvancedOrders(tokenId);
if (!reentering) {
- vm.expectEmit(true, false, false, true, address(this));
+ vm.expectEmit(false, false, false, true, address(this));
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
}
vm.prank(alice);
@@ -224,7 +224,7 @@ contract NonReentrantTest is BaseOrderTest {
Fulfillment[] memory _fulfillments
) = prepareMatchOrders(tokenId);
if (!reentering) {
- vm.expectEmit(true, false, false, true, address(this));
+ vm.expectEmit(false, false, false, true, address(this));
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
}
currentConsideration.matchOrders{ value: 1 }(
@@ -238,7 +238,7 @@ contract NonReentrantTest is BaseOrderTest {
Fulfillment[] memory _fulfillments
) = prepareMatchAdvancedOrders(tokenId);
if (!reentering) {
- vm.expectEmit(true, false, false, true, address(this));
+ vm.expectEmit(false, false, false, true, address(this));
emit BytesReason(abi.encodeWithSignature("NoReentrantCalls()"));
}
currentConsideration.matchAdvancedOrders{ value: 1 }(
diff --git a/test/foundry/TransferHelperMultipleRecipientsTest.sol b/test/foundry/TransferHelperMultipleRecipientsTest.sol
index c8f476cec..26e27147c 100644
--- a/test/foundry/TransferHelperMultipleRecipientsTest.sol
+++ b/test/foundry/TransferHelperMultipleRecipientsTest.sol
@@ -363,7 +363,7 @@ contract TransferHelperMultipleRecipientsTest is BaseOrderTest {
// ERC1155 has three indexed topics plus data.
if (item.itemType == ConduitItemType.ERC20) {
- vm.expectEmit(true, true, true, true, item.token);
+ vm.expectEmit(true, true, false, true, item.token);
emit Transfer(
from,
@@ -439,7 +439,7 @@ contract TransferHelperMultipleRecipientsTest is BaseOrderTest {
// but tokenId is indexed for 721 and not for ERC20 (so amount is data)
// ERC1155 has three indexed topics plus data.
if (item.itemType == ConduitItemType.ERC20) {
- vm.expectEmit(true, true, true, true, item.token);
+ vm.expectEmit(true, true, false, true, item.token);
emit Transfer(
from,
diff --git a/test/foundry/helpers/sol/BaseTest.sol b/test/foundry/helpers/sol/BaseTest.sol
index 77c0d32b8..35be61752 100644
--- a/test/foundry/helpers/sol/BaseTest.sol
+++ b/test/foundry/helpers/sol/BaseTest.sol
@@ -2,31 +2,35 @@
pragma solidity ^0.8.17;
import { Test } from "forge-std/Test.sol";
+
import {
- OrderParameters,
- OfferItem,
- ConsiderationItem,
- SpentItem,
- ReceivedItem,
- Execution,
AdditionalRecipient,
+ ConsiderationItem,
CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
+ OfferItem,
+ Order,
OrderComponents,
OrderParameters,
- Order
+ ReceivedItem,
+ SpentItem
} from "../../../../contracts/lib/ConsiderationStructs.sol";
+
import {
ItemType,
OrderType
} from "../../../../contracts/lib/ConsiderationEnums.sol";
+
import {
OrderComponentsLib
} from "../../../../contracts/helpers/sol/lib/OrderComponentsLib.sol";
+
import {
OrderParametersLib
} from "../../../../contracts/helpers/sol/lib/OrderParametersLib.sol";
+
import { OrderLib } from "../../../../contracts/helpers/sol/lib/OrderLib.sol";
contract BaseTest is Test {
diff --git a/test/foundry/helpers/sol/lib/AdditionalRecipientLib.t.sol b/test/foundry/helpers/sol/lib/AdditionalRecipientLib.t.sol
index 4910503e6..2fa6d75bc 100644
--- a/test/foundry/helpers/sol/lib/AdditionalRecipientLib.t.sol
+++ b/test/foundry/helpers/sol/lib/AdditionalRecipientLib.t.sol
@@ -18,7 +18,9 @@ contract AdditionalRecipientLibTest is BaseTest {
address payable recipient
) public {
AdditionalRecipient memory additionalRecipient = AdditionalRecipient({
- amount: amount,
+ // Make sure the the amount is not 0, otherwise it will be
+ // considered empty and trigger the revert.
+ amount: amount == 0 ? 1 : amount,
recipient: recipient
});
AdditionalRecipientLib.saveDefault(additionalRecipient, "default");
@@ -28,6 +30,14 @@ contract AdditionalRecipientLibTest is BaseTest {
assertEq(additionalRecipient, defaultAdditionalRecipient);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty AdditionalRecipient selected.");
+ AdditionalRecipientLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty AdditionalRecipient array selected.");
+ AdditionalRecipientLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint256 amount,
address payable recipient
diff --git a/test/foundry/helpers/sol/lib/AdvancedOrderLib.t.sol b/test/foundry/helpers/sol/lib/AdvancedOrderLib.t.sol
index 9a2096a9a..b39ed182b 100644
--- a/test/foundry/helpers/sol/lib/AdvancedOrderLib.t.sol
+++ b/test/foundry/helpers/sol/lib/AdvancedOrderLib.t.sol
@@ -40,6 +40,14 @@ contract AdvancedOrderLibTest is BaseTest {
assertEq(advancedOrder, defaultAdvancedOrder);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty AdvancedOrder selected.");
+ AdvancedOrderLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty AdvancedOrder array selected.");
+ AdvancedOrderLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint120 numerator,
uint120 denominator,
diff --git a/test/foundry/helpers/sol/lib/BasicOrderParametersLib.t.sol b/test/foundry/helpers/sol/lib/BasicOrderParametersLib.t.sol
index 1383ddb5e..9eab69301 100644
--- a/test/foundry/helpers/sol/lib/BasicOrderParametersLib.t.sol
+++ b/test/foundry/helpers/sol/lib/BasicOrderParametersLib.t.sol
@@ -106,6 +106,14 @@ contract BasicOrderParametersLibTest is BaseTest {
assertEq(basicOrderParameters, defaultBasicOrderParameters);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty BasicOrderParameters selected.");
+ BasicOrderParametersLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty BasicOrderParameters array selected.");
+ BasicOrderParametersLib.fromDefaultMany("nonexistent");
+ }
+
function testCopy() public {
AdditionalRecipient[] memory additionalRecipients = SeaportArrays
.AdditionalRecipients(
diff --git a/test/foundry/helpers/sol/lib/ConsiderationItemLib.t.sol b/test/foundry/helpers/sol/lib/ConsiderationItemLib.t.sol
index 85e13900d..ae5effe11 100644
--- a/test/foundry/helpers/sol/lib/ConsiderationItemLib.t.sol
+++ b/test/foundry/helpers/sol/lib/ConsiderationItemLib.t.sol
@@ -27,7 +27,7 @@ contract ConsiderationItemLibTest is BaseTest {
token: token,
identifierOrCriteria: identifier,
startAmount: startAmount,
- endAmount: endAmount,
+ endAmount: endAmount == 0 ? 1 : endAmount,
recipient: recipient
});
ConsiderationItemLib.saveDefault(considerationItem, "default");
@@ -36,6 +36,14 @@ contract ConsiderationItemLibTest is BaseTest {
assertEq(considerationItem, defaultConsiderationItem);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty ConsiderationItem selected.");
+ ConsiderationItemLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty ConsiderationItem array selected.");
+ ConsiderationItemLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint8 itemType,
address token,
diff --git a/test/foundry/helpers/sol/lib/CriteriaResolverLib.t.sol b/test/foundry/helpers/sol/lib/CriteriaResolverLib.t.sol
index 9c801adf9..2e4bbe4f4 100644
--- a/test/foundry/helpers/sol/lib/CriteriaResolverLib.t.sol
+++ b/test/foundry/helpers/sol/lib/CriteriaResolverLib.t.sol
@@ -21,7 +21,7 @@ contract CriteriaResolverLibTest is BaseTest {
bytes32[] memory criteriaProof
) public {
CriteriaResolver memory criteriaResolver = CriteriaResolver({
- orderIndex: orderIndex,
+ orderIndex: orderIndex == 0 ? 1 : orderIndex,
side: Side(side ? 1 : 0),
index: index,
identifier: identifier,
@@ -33,6 +33,14 @@ contract CriteriaResolverLibTest is BaseTest {
assertEq(criteriaResolver, defaultCriteriaResolver);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty CriteriaResolver selected.");
+ CriteriaResolverLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty CriteriaResolver array selected.");
+ CriteriaResolverLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint256 orderIndex,
bool side,
diff --git a/test/foundry/helpers/sol/lib/ExecutionsLib.t.sol b/test/foundry/helpers/sol/lib/ExecutionsLib.t.sol
index 609b33a38..52a50b076 100644
--- a/test/foundry/helpers/sol/lib/ExecutionsLib.t.sol
+++ b/test/foundry/helpers/sol/lib/ExecutionsLib.t.sol
@@ -34,7 +34,7 @@ contract ExecutionLibTest is BaseTest {
itemType: toItemType(blob.itemType),
token: blob.token,
identifier: blob.identifier,
- amount: blob.amount,
+ amount: blob.amount == 0 ? 1 : blob.amount,
recipient: blob.recipient
});
Execution memory execution = Execution({
@@ -47,6 +47,14 @@ contract ExecutionLibTest is BaseTest {
assertEq(execution, defaultExecution);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty Execution selected.");
+ ExecutionLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty Execution array selected.");
+ ExecutionLib.fromDefaultMany("nonexistent");
+ }
+
function _fromBlob(
ReceivedItemBlob memory blob
) internal view returns (ReceivedItem memory) {
diff --git a/test/foundry/helpers/sol/lib/OfferItemLib.t.sol b/test/foundry/helpers/sol/lib/OfferItemLib.t.sol
index e9a7e0a90..195e2fa93 100644
--- a/test/foundry/helpers/sol/lib/OfferItemLib.t.sol
+++ b/test/foundry/helpers/sol/lib/OfferItemLib.t.sol
@@ -26,13 +26,21 @@ contract OfferItemLibTest is BaseTest {
token: token,
identifierOrCriteria: identifier,
startAmount: startAmount,
- endAmount: endAmount
+ endAmount: endAmount == 0 ? 1 : endAmount
});
OfferItemLib.saveDefault(offerItem, "default");
OfferItem memory defaultOfferItem = OfferItemLib.fromDefault("default");
assertEq(offerItem, defaultOfferItem);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty OfferItem selected.");
+ OfferItemLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty OfferItem array selected.");
+ OfferItemLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint8 itemType,
address token,
diff --git a/test/foundry/helpers/sol/lib/OrderComponentsLib.t.sol b/test/foundry/helpers/sol/lib/OrderComponentsLib.t.sol
index 46caa9f70..f8edca61b 100644
--- a/test/foundry/helpers/sol/lib/OrderComponentsLib.t.sol
+++ b/test/foundry/helpers/sol/lib/OrderComponentsLib.t.sol
@@ -53,6 +53,14 @@ contract OrderComponentsLibTest is BaseTest {
assertEq(orderComponents, defaultOrderComponents);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty OrderComponents selected.");
+ OrderComponentsLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty OrderComponents array selected.");
+ OrderComponentsLib.fromDefaultMany("nonexistent");
+ }
+
function testCopy() public {
OrderComponents memory orderComponents = OrderComponentsLib.empty();
orderComponents = orderComponents.withOfferer(address(1));
diff --git a/test/foundry/helpers/sol/lib/OrderLib.t.sol b/test/foundry/helpers/sol/lib/OrderLib.t.sol
index ad0df9c3e..2240af428 100644
--- a/test/foundry/helpers/sol/lib/OrderLib.t.sol
+++ b/test/foundry/helpers/sol/lib/OrderLib.t.sol
@@ -30,6 +30,14 @@ contract OrderLibTest is BaseTest {
assertEq(order, defaultOrder);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty Order selected.");
+ OrderLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty Order array selected.");
+ OrderLib.fromDefaultMany("nonexistent");
+ }
+
function testCopy() public {
OrderParameters memory parameters = OrderParametersLib
.empty()
diff --git a/test/foundry/helpers/sol/lib/OrderParametersLib.t.sol b/test/foundry/helpers/sol/lib/OrderParametersLib.t.sol
index 01e6be5c7..81209751b 100644
--- a/test/foundry/helpers/sol/lib/OrderParametersLib.t.sol
+++ b/test/foundry/helpers/sol/lib/OrderParametersLib.t.sol
@@ -54,6 +54,14 @@ contract OrderParametersLibTest is BaseTest {
assertEq(orderParameters, defaultOrderParameters);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty OrderParameters selected.");
+ OrderParametersLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty OrderParameters array selected.");
+ OrderParametersLib.fromDefaultMany("nonexistent");
+ }
+
function testCopy() public {
OrderParameters memory orderParameters = OrderParametersLib.empty();
orderParameters = orderParameters.withOfferer(address(1));
diff --git a/test/foundry/helpers/sol/lib/ReceivedItemLib.t.sol b/test/foundry/helpers/sol/lib/ReceivedItemLib.t.sol
index eeea25735..e76584b70 100644
--- a/test/foundry/helpers/sol/lib/ReceivedItemLib.t.sol
+++ b/test/foundry/helpers/sol/lib/ReceivedItemLib.t.sol
@@ -25,7 +25,7 @@ contract ReceivedItemLibTest is BaseTest {
ItemType(itemType),
token,
identifier,
- amount,
+ amount == 0 ? 1 : amount,
recipient
);
ReceivedItemLib.saveDefault(receivedItem, "default");
@@ -35,6 +35,14 @@ contract ReceivedItemLibTest is BaseTest {
assertEq(receivedItem, defaultReceivedItem);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty ReceivedItem selected.");
+ ReceivedItemLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty ReceivedItem array selected.");
+ ReceivedItemLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint8 itemType,
address token,
diff --git a/test/foundry/helpers/sol/lib/SpentItemLib.t.sol b/test/foundry/helpers/sol/lib/SpentItemLib.t.sol
index e2d56c24b..e95f456c2 100644
--- a/test/foundry/helpers/sol/lib/SpentItemLib.t.sol
+++ b/test/foundry/helpers/sol/lib/SpentItemLib.t.sol
@@ -24,13 +24,21 @@ contract SpentItemLibTest is BaseTest {
ItemType(itemType),
token,
identifier,
- amount
+ amount == 0 ? 1 : amount
);
SpentItemLib.saveDefault(spentItem, "default");
SpentItem memory defaultSpentItem = SpentItemLib.fromDefault("default");
assertEq(spentItem, defaultSpentItem);
}
+ function testRetrieveNonexistentDefault() public {
+ vm.expectRevert("Empty SpentItem selected.");
+ SpentItemLib.fromDefault("nonexistent");
+
+ vm.expectRevert("Empty SpentItem array selected.");
+ SpentItemLib.fromDefaultMany("nonexistent");
+ }
+
function testComposeEmpty(
uint8 itemType,
address token,
diff --git a/test/foundry/new/BaseOrderTest.sol b/test/foundry/new/BaseOrderTest.sol
new file mode 100644
index 000000000..90fac504a
--- /dev/null
+++ b/test/foundry/new/BaseOrderTest.sol
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol";
+
+import { LibString } from "solady/src/utils/LibString.sol";
+
+import {
+ FulfillAvailableHelper
+} from "seaport-sol/fulfillments/available/FulfillAvailableHelper.sol";
+
+import {
+ MatchFulfillmentHelper
+} from "seaport-sol/fulfillments/match/MatchFulfillmentHelper.sol";
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ FulfillmentComponentLib,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib,
+ SeaportArrays
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { setLabel, BaseSeaportTest } from "./helpers/BaseSeaportTest.sol";
+
+import { ArithmeticUtil } from "./helpers/ArithmeticUtil.sol";
+
+import { CriteriaResolverHelper } from "./helpers/CriteriaResolverHelper.sol";
+
+import { ERC1155Recipient } from "./helpers/ERC1155Recipient.sol";
+
+import { ERC721Recipient } from "./helpers/ERC721Recipient.sol";
+
+import { ExpectedBalances } from "./helpers/ExpectedBalances.sol";
+
+import { PreapprovedERC721 } from "./helpers/PreapprovedERC721.sol";
+
+import { AmountDeriver } from "../../../contracts/lib/AmountDeriver.sol";
+
+import { TestERC20 } from "../../../contracts/test/TestERC20.sol";
+
+import { TestERC721 } from "../../../contracts/test/TestERC721.sol";
+
+import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol";
+
+import {
+ SeaportValidatorHelper,
+ SeaportValidator
+} from "../../../contracts/helpers/order-validator/SeaportValidator.sol";
+
+/**
+ * @dev used to store address and key outputs from makeAddrAndKey(name)
+ */
+struct Account {
+ address addr;
+ uint256 key;
+}
+
+/**
+ * @dev This is a base test class for cases that depend on pre-deployed token
+ * contracts. Note that it is different from the BaseOrderTest in the
+ * legacy test suite.
+ */
+contract BaseOrderTest is
+ BaseSeaportTest,
+ AmountDeriver,
+ ERC721Recipient,
+ ERC1155Recipient
+{
+ using ArithmeticUtil for *;
+ using Strings for uint256;
+
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using FulfillmentComponentLib for FulfillmentComponent[];
+ using FulfillmentLib for Fulfillment;
+ using FulfillmentLib for Fulfillment[];
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderLib for Order[];
+ using OrderParametersLib for OrderParameters;
+
+ event Transfer(address indexed from, address indexed to, uint256 value);
+ event TransferSingle(
+ address indexed operator,
+ address indexed from,
+ address indexed to,
+ uint256 id,
+ uint256 value
+ );
+
+ struct Context {
+ SeaportInterface seaport;
+ }
+
+ SeaportValidatorHelper validatorHelper;
+ SeaportValidator validator;
+ FulfillAvailableHelper fulfill;
+ MatchFulfillmentHelper matcher;
+
+ Account offerer1;
+ Account offerer2;
+
+ Account dillon;
+ Account eve;
+ Account frank;
+
+ PreapprovedERC721 internal preapproved721;
+
+ TestERC20[] erc20s;
+ TestERC721[] erc721s;
+ TestERC1155[] erc1155s;
+
+ ExpectedBalances public balanceChecker;
+ CriteriaResolverHelper public criteriaResolverHelper;
+
+ address[] preapprovals;
+
+ string constant SINGLE_ERC721 = "single erc721";
+ string constant STANDARD = "standard";
+ string constant STANDARD_CONDUIT = "standard conduit";
+ string constant FULL = "full";
+ string constant FIRST_FIRST = "first first";
+ string constant FIRST_SECOND = "first second";
+ string constant SECOND_FIRST = "second first";
+ string constant SECOND_SECOND = "second second";
+ string constant FF_SF = "ff to sf";
+ string constant SF_FF = "sf to ff";
+
+ function setUp() public virtual override {
+ super.setUp();
+
+ balanceChecker = new ExpectedBalances();
+
+ // TODO: push to 24 if performance allows
+ criteriaResolverHelper = new CriteriaResolverHelper(6);
+
+ preapprovals = [
+ address(seaport),
+ address(referenceSeaport),
+ address(conduit),
+ address(referenceConduit)
+ ];
+
+ _deployTestTokenContracts();
+
+ offerer1 = makeAndAllocateAccount("alice");
+ offerer2 = makeAndAllocateAccount("bob");
+
+ dillon = makeAndAllocateAccount("dillon");
+ eve = makeAndAllocateAccount("eve");
+ frank = makeAndAllocateAccount("frank");
+
+ // allocate funds and tokens to test addresses
+ allocateTokensAndApprovals(address(this), type(uint128).max);
+
+ _configureStructDefaults();
+
+ validatorHelper = new SeaportValidatorHelper();
+
+ validator = new SeaportValidator(
+ address(validatorHelper),
+ address(getConduitController())
+ );
+
+ fulfill = new FulfillAvailableHelper();
+ matcher = new MatchFulfillmentHelper();
+ }
+
+ /**
+ * @dev Creates a set of globally available default structs for use in
+ * tests.
+ */
+ function _configureStructDefaults() internal {
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withStartAmount(1)
+ .withEndAmount(1)
+ .saveDefault(SINGLE_ERC721);
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withStartAmount(1)
+ .withEndAmount(1)
+ .saveDefault(SINGLE_ERC721);
+
+ OrderComponentsLib
+ .empty()
+ .withOrderType(OrderType.FULL_OPEN)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 100)
+ .saveDefault(STANDARD);
+
+ OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withConduitKey(conduitKey)
+ .saveDefault(STANDARD_CONDUIT);
+
+ AdvancedOrderLib
+ .empty()
+ .withNumerator(1)
+ .withDenominator(1)
+ .saveDefault(FULL);
+
+ FulfillmentComponentLib
+ .empty()
+ .withOrderIndex(0)
+ .withItemIndex(0)
+ .saveDefault(FIRST_FIRST);
+ FulfillmentComponentLib
+ .empty()
+ .withOrderIndex(0)
+ .withItemIndex(1)
+ .saveDefault(FIRST_SECOND);
+ FulfillmentComponentLib
+ .empty()
+ .withOrderIndex(1)
+ .withItemIndex(0)
+ .saveDefault(SECOND_FIRST);
+ FulfillmentComponentLib
+ .empty()
+ .withOrderIndex(1)
+ .withItemIndex(1)
+ .saveDefault(SECOND_SECOND);
+
+ SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponentLib.fromDefault(FIRST_FIRST)
+ )
+ .saveDefaultMany(FIRST_FIRST);
+ SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponentLib.fromDefault(FIRST_SECOND)
+ )
+ .saveDefaultMany(FIRST_SECOND);
+ SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponentLib.fromDefault(SECOND_FIRST)
+ )
+ .saveDefaultMany(SECOND_FIRST);
+ SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponentLib.fromDefault(SECOND_SECOND)
+ )
+ .saveDefaultMany(SECOND_SECOND);
+
+ FulfillmentLib
+ .empty()
+ .withOfferComponents(
+ FulfillmentComponentLib.fromDefaultMany(SECOND_FIRST)
+ )
+ .withConsiderationComponents(
+ FulfillmentComponentLib.fromDefaultMany(FIRST_FIRST)
+ )
+ .saveDefault(SF_FF);
+ FulfillmentLib
+ .empty()
+ .withOfferComponents(
+ FulfillmentComponentLib.fromDefaultMany(FIRST_FIRST)
+ )
+ .withConsiderationComponents(
+ FulfillmentComponentLib.fromDefaultMany(SECOND_FIRST)
+ )
+ .saveDefault(FF_SF);
+ }
+
+ function test(
+ function(Context memory) external fn,
+ Context memory context
+ ) internal {
+ try fn(context) {
+ fail("Differential test should have reverted with failure status");
+ } catch (bytes memory reason) {
+ assertPass(reason);
+ }
+ }
+
+ /**
+ * @dev convenience wrapper for makeAddrAndKey
+ */
+ function makeAccount(string memory name) public returns (Account memory) {
+ (address addr, uint256 key) = makeAddrAndKey(name);
+ setLabel(addr, name);
+ return Account(addr, key);
+ }
+
+ /**
+ * @dev Convenience wrapper for makeAddrAndKey that also allocates tokens,
+ * ether, and approvals.
+ */
+ function makeAndAllocateAccount(
+ string memory name
+ ) internal returns (Account memory) {
+ Account memory account = makeAccount(name);
+ allocateTokensAndApprovals(account.addr, type(uint128).max);
+ return account;
+ }
+
+ /**
+ * @dev Sets up a new address and sets up token approvals for it.
+ */
+ function makeAddrWithAllocationsAndApprovals(
+ string memory label
+ ) internal returns (address) {
+ address addr = makeAddr(label);
+ allocateTokensAndApprovals(addr, type(uint128).max);
+ return addr;
+ }
+
+ /**
+ * @dev Deploy test token contracts.
+ */
+ function _deployTestTokenContracts() internal {
+ for (uint256 i; i < 3; i++) {
+ createErc20Token();
+ createErc721Token();
+ createErc1155Token();
+ }
+ preapproved721 = new PreapprovedERC721(preapprovals);
+ }
+
+ /**
+ * @dev Creates a new ERC20 token contract and stores it in the erc20s
+ * array.
+ */
+ function createErc20Token() internal returns (uint256 i) {
+ i = erc20s.length;
+ TestERC20 token = new TestERC20();
+ erc20s.push(token);
+ setLabel(address(token), string.concat("ERC20", LibString.toString(i)));
+ }
+
+ /**
+ * @dev Creates a new ERC721 token contract and stores it in the erc721s
+ * array.
+ */
+ function createErc721Token() internal returns (uint256 i) {
+ i = erc721s.length;
+ TestERC721 token = new TestERC721();
+ erc721s.push(token);
+ setLabel(
+ address(token),
+ string.concat("ERC721", LibString.toString(i))
+ );
+ }
+
+ /**
+ * @dev Creates a new ERC1155 token contract and stores it in the erc1155s
+ * array.
+ */
+ function createErc1155Token() internal returns (uint256 i) {
+ i = erc1155s.length;
+ TestERC1155 token = new TestERC1155();
+ erc1155s.push(token);
+ setLabel(
+ address(token),
+ string.concat("ERC1155", LibString.toString(i))
+ );
+ }
+
+ /**
+ * @dev Allocate amount of ether and each erc20 token; set approvals for all
+ * tokens.
+ */
+ function allocateTokensAndApprovals(address _to, uint128 _amount) public {
+ vm.deal(_to, _amount);
+ for (uint256 i = 0; i < erc20s.length; ++i) {
+ erc20s[i].mint(_to, _amount);
+ }
+ _setApprovals(_to);
+ }
+
+ /**
+ * @dev Set approvals for all tokens.
+ *
+ * @param _owner The address to set approvals for.
+ */
+ function _setApprovals(address _owner) internal virtual {
+ vm.startPrank(_owner);
+ for (uint256 i = 0; i < erc20s.length; ++i) {
+ erc20s[i].approve(address(seaport), type(uint256).max);
+ erc20s[i].approve(address(referenceSeaport), type(uint256).max);
+ erc20s[i].approve(address(conduit), type(uint256).max);
+ erc20s[i].approve(address(referenceConduit), type(uint256).max);
+ }
+ for (uint256 i = 0; i < erc721s.length; ++i) {
+ erc721s[i].setApprovalForAll(address(seaport), true);
+ erc721s[i].setApprovalForAll(address(referenceSeaport), true);
+ erc721s[i].setApprovalForAll(address(conduit), true);
+ erc721s[i].setApprovalForAll(address(referenceConduit), true);
+ }
+ for (uint256 i = 0; i < erc1155s.length; ++i) {
+ erc1155s[i].setApprovalForAll(address(seaport), true);
+ erc1155s[i].setApprovalForAll(address(referenceSeaport), true);
+ erc1155s[i].setApprovalForAll(address(conduit), true);
+ erc1155s[i].setApprovalForAll(address(referenceConduit), true);
+ }
+
+ vm.stopPrank();
+ }
+
+ receive() external payable virtual {}
+}
diff --git a/test/foundry/new/CriteriaResolverHelper.t.sol b/test/foundry/new/CriteriaResolverHelper.t.sol
new file mode 100644
index 000000000..f1eef99f5
--- /dev/null
+++ b/test/foundry/new/CriteriaResolverHelper.t.sol
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+// import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import { Test } from "forge-std/Test.sol";
+
+// import {
+// AdvancedOrderLib,
+// ConsiderationItemLib,
+// OfferItemLib,
+// OrderParametersLib,
+// SeaportArrays
+// } from "seaport-sol/SeaportSol.sol";
+
+import { CriteriaResolverHelper } from "./helpers/CriteriaResolverHelper.sol";
+
+contract CriteriaResolverHelperTest is Test {
+ // using LibPRNG for LibPRNG.PRNG;
+ // using OfferItemLib for OfferItem;
+ // using OfferItemLib for OfferItem[];
+ // using ConsiderationItemLib for ConsiderationItem;
+ // using ConsiderationItemLib for ConsiderationItem[];
+ // using OrderParametersLib for OrderParameters;
+ // using AdvancedOrderLib for AdvancedOrder;
+ // using AdvancedOrderLib for AdvancedOrder[];
+
+ CriteriaResolverHelper test;
+
+ function setUp() public {
+ test = new CriteriaResolverHelper(100);
+ }
+
+ // function testCanVerify(uint256 seed) public {
+ // LibPRNG.PRNG memory prng = LibPRNG.PRNG({ state: 0 });
+ // uint256[] memory identifiers = test.generateIdentifiers(prng);
+ // uint256 criteria = test.generateCriteriaMetadata(prng, seed);
+
+ // uint256 resolvedIdentifier = test
+ // .resolvableIdentifierForGivenCriteria(criteria)
+ // .resolvedIdentifier;
+ // bytes32[] memory proof = test
+ // .resolvableIdentifierForGivenCriteria(criteria)
+ // .proof;
+ // bytes32 hashedIdentifier = keccak256(abi.encode(resolvedIdentifier));
+ // bytes32[] memory leaves = test.hashIdentifiersToLeaves(identifiers);
+ // bytes32 root = test.MERKLE().getRoot(leaves);
+ // assertTrue(test.MERKLE().verifyProof(root, proof, hashedIdentifier));
+ // }
+
+ // function testDeriveCriteriaResolvers(
+ // uint256 seed,
+ // uint256 desiredId
+ // ) public {
+ // vm.assume(desiredId < type(uint256).max);
+
+ // LibPRNG.PRNG memory prng = LibPRNG.PRNG(seed);
+ // uint256 criteria = test.generateCriteriaMetadata(prng, desiredId);
+
+ // // Create the offer and consideration for the order
+ // OfferItem[] memory offer = SeaportArrays.OfferItems(
+ // OfferItemLib
+ // .empty()
+ // .withItemType(ItemType.ERC721_WITH_CRITERIA)
+ // .withStartAmount(1)
+ // .withEndAmount(1)
+ // .withToken(address(1234))
+ // .withIdentifierOrCriteria(criteria)
+ // );
+
+ // ConsiderationItem[] memory consideration = SeaportArrays
+ // .ConsiderationItems(
+ // ConsiderationItemLib
+ // .empty()
+ // .withItemType(ItemType.ERC20)
+ // .withStartAmount(100)
+ // .withEndAmount(100)
+ // .withToken(address(1234))
+ // .withIdentifierOrCriteria(0)
+ // .withRecipient(address(this))
+ // );
+
+ // OrderParameters memory orderParameters = OrderParametersLib
+ // .empty()
+ // .withOffer(offer)
+ // .withConsideration(consideration);
+
+ // AdvancedOrder[] memory orders = SeaportArrays.AdvancedOrders(
+ // AdvancedOrderLib.empty().withParameters(orderParameters)
+ // );
+
+ // CriteriaResolver[] memory criteriaResolvers = test
+ // .deriveCriteriaResolvers(orders);
+
+ // assertEq(
+ // criteriaResolvers.length,
+ // 1,
+ // "Invalid criteria resolvers length"
+ // );
+ // assertEq(
+ // criteriaResolvers[0].identifier,
+ // desiredId,
+ // "Criteria resolver should have desired id"
+ // );
+
+ // uint256[] memory identifiers = test.generateIdentifiers(prng);
+ // uint256 resolvedIdentifier = test
+ // .resolvableIdentifierForGivenCriteria(criteria)
+ // .resolvedIdentifier;
+ // bytes32[] memory proof = test
+ // .resolvableIdentifierForGivenCriteria(criteria)
+ // .proof;
+ // bytes32 hashedIdentifier = keccak256(abi.encode(resolvedIdentifier));
+ // bytes32[] memory leaves = test.hashIdentifiersToLeaves(identifiers);
+ // bytes32 root = test.MERKLE().getRoot(leaves);
+
+ // test.MERKLE().verifyProof(root, proof, hashedIdentifier);
+ // }
+}
diff --git a/test/foundry/new/ExpectedBalanceSerializer.sol b/test/foundry/new/ExpectedBalanceSerializer.sol
new file mode 100644
index 000000000..446084351
--- /dev/null
+++ b/test/foundry/new/ExpectedBalanceSerializer.sol
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+// import { vm } from "./VmUtils.sol"";
+
+// import { ExpectedBalances } from "./helpers/ExpectedBalances.sol";
+
+// function tojsonAddress(
+// string memory objectKey,
+// string memory valueKey,
+// address value
+// ) returns (string memory) {
+// return vm.serializeAddress(objectKey, valueKey, value);
+// }
+
+// function tojsonUint256(
+// string memory objectKey,
+// string memory valueKey,
+// uint256 value
+// ) returns (string memory) {
+// return vm.serializeUint(objectKey, valueKey, value);
+// }
+
+// function tojsonERC20AccountDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC20AccountDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonAddress(obj, "account", value.account);
+// string memory finalJson = tojsonUint256(obj, "balance", value.balance);
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonDynArrayERC20AccountDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC20AccountDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// string memory out;
+// for (uint256 i; i < value.length; i++) {
+// out = tojsonERC20AccountDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonERC20TokenDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC20TokenDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonAddress(obj, "token", value.token);
+// string memory finalJson = tojsonDynArrayERC20AccountDump(
+// obj,
+// "accounts",
+// value.accounts
+// );
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonDynArrayUint256(
+// string memory objectKey,
+// string memory valueKey,
+// uint256[] memory value
+// ) returns (string memory) {
+// return vm.serializeUint(objectKey, valueKey, value);
+// }
+
+// function tojsonERC721AccountDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC721AccountDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonAddress(obj, "account", value.account);
+// string memory finalJson = tojsonDynArrayUint256(
+// obj,
+// "identifiers",
+// value.identifiers
+// );
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonDynArrayERC721AccountDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC721AccountDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// uint256 length = value.length;
+// string memory out;
+// for (uint256 i; i < length; i++) {
+// out = tojsonERC721AccountDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonERC721TokenDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC721TokenDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonAddress(obj, "token", value.token);
+// string memory finalJson = tojsonDynArrayERC721AccountDump(
+// obj,
+// "accounts",
+// value.accounts
+// );
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonERC1155IdentifierDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC1155IdentifierDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonUint256(obj, "identifier", value.identifier);
+// string memory finalJson = tojsonUint256(obj, "balance", value.balance);
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonDynArrayERC1155IdentifierDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC1155IdentifierDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// uint256 length = value.length;
+// string memory out;
+// for (uint256 i; i < length; i++) {
+// out = tojsonERC1155IdentifierDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonERC1155AccountDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC1155AccountDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonAddress(obj, "account", value.account);
+// string memory finalJson = tojsonDynArrayERC1155IdentifierDump(
+// obj,
+// "identifiers",
+// value.identifiers
+// );
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonDynArrayERC1155AccountDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC1155AccountDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// uint256 length = value.length;
+// string memory out;
+// for (uint256 i; i < length; i++) {
+// out = tojsonERC1155AccountDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonERC1155TokenDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC1155TokenDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonAddress(obj, "token", value.token);
+// string memory finalJson = tojsonDynArrayERC1155AccountDump(
+// obj,
+// "accounts",
+// value.accounts
+// );
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
+
+// function tojsonDynArrayERC20TokenDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC20TokenDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// uint256 length = value.length;
+// string memory out;
+// for (uint256 i; i < length; i++) {
+// out = tojsonERC20TokenDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonDynArrayERC721TokenDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC721TokenDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// uint256 length = value.length;
+// string memory out;
+// for (uint256 i; i < length; i++) {
+// out = tojsonERC721TokenDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonDynArrayERC1155TokenDump(
+// string memory objectKey,
+// string memory valueKey,
+// ERC1155TokenDump[] memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// uint256 length = value.length;
+// string memory out;
+// for (uint256 i; i < length; i++) {
+// out = tojsonERC1155TokenDump(obj, vm.toString(i), value[i]);
+// }
+// return vm.serializeString(objectKey, valueKey, out);
+// }
+
+// function tojsonExpectedBalancesDump(
+// string memory objectKey,
+// string memory valueKey,
+// ExpectedBalancesDump memory value
+// ) returns (string memory) {
+// string memory obj = string.concat(objectKey, valueKey);
+// tojsonDynArrayERC20TokenDump(obj, "erc20", value.erc20);
+// tojsonDynArrayERC721TokenDump(obj, "erc721", value.erc721);
+// string memory finalJson = tojsonDynArrayERC1155TokenDump(
+// obj,
+// "erc1155",
+// value.erc1155
+// );
+// return vm.serializeString(objectKey, valueKey, finalJson);
+// }
diff --git a/test/foundry/new/ExpectedBalances.t.sol b/test/foundry/new/ExpectedBalances.t.sol
new file mode 100644
index 000000000..db639d61e
--- /dev/null
+++ b/test/foundry/new/ExpectedBalances.t.sol
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { stdError, Test } from "forge-std/Test.sol";
+
+import { Execution, ReceivedItem } from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ BalanceErrorMessages,
+ ERC721TokenDump,
+ ExpectedBalances
+} from "./helpers/ExpectedBalances.sol";
+
+import { TestERC20 } from "../../../contracts/test/TestERC20.sol";
+
+import { TestERC721 } from "../../../contracts/test/TestERC721.sol";
+
+import { TestERC1155 } from "../../../contracts/test/TestERC1155.sol";
+
+contract ExpectedBalancesTest is Test {
+ TestERC20 internal erc20;
+ TestERC721 internal erc721;
+ TestERC1155 internal erc1155;
+
+ ExpectedBalances internal balances;
+
+ address payable internal alice = payable(address(0xa11ce));
+ address payable internal bob = payable(address(0xb0b));
+
+ function setUp() public virtual {
+ balances = new ExpectedBalances();
+ _deployTestTokenContracts();
+ }
+
+ function testAddTransfers() external {
+ erc20.mint(alice, 500);
+ erc721.mint(bob, 1);
+ erc1155.mint(bob, 1, 100);
+ vm.deal(alice, 1 ether);
+ Execution[] memory executions = new Execution[](4);
+
+ executions[0] = Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.NATIVE,
+ address(0),
+ 0,
+ 0.5 ether,
+ payable(bob)
+ )
+ });
+ executions[1] = Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 250,
+ payable(bob)
+ )
+ });
+ executions[2] = Execution({
+ offerer: bob,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 1,
+ 1,
+ payable(alice)
+ )
+ });
+ executions[3] = Execution({
+ offerer: bob,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC1155,
+ address(erc1155),
+ 1,
+ 50,
+ payable(alice)
+ )
+ });
+ balances.addTransfers(executions);
+ vm.prank(alice);
+ erc20.transfer(bob, 250);
+
+ vm.prank(bob);
+ erc721.transferFrom(bob, alice, 1);
+
+ vm.prank(bob);
+ erc1155.safeTransferFrom(bob, alice, 1, 50, "");
+
+ vm.prank(alice);
+ bob.transfer(0.5 ether);
+
+ balances.checkBalances();
+ }
+
+ function testCheckBalances() external {
+ erc20.mint(alice, 500);
+ erc721.mint(bob, 1);
+ erc1155.mint(bob, 1, 100);
+ vm.deal(alice, 1 ether);
+
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.NATIVE,
+ address(0),
+ 0,
+ 0.5 ether,
+ payable(bob)
+ )
+ })
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 250,
+ payable(bob)
+ )
+ })
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: bob,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 1,
+ 1,
+ payable(alice)
+ )
+ })
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: bob,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC1155,
+ address(erc1155),
+ 1,
+ 50,
+ payable(alice)
+ )
+ })
+ );
+ vm.prank(alice);
+ erc20.transfer(bob, 250);
+
+ vm.prank(bob);
+ erc721.transferFrom(bob, alice, 1);
+
+ vm.prank(bob);
+ erc1155.safeTransferFrom(bob, alice, 1, 50, "");
+
+ vm.prank(alice);
+ bob.transfer(0.5 ether);
+
+ balances.checkBalances();
+ }
+
+ // =====================================================================//
+ // NATIVE TESTS //
+ // =====================================================================//
+
+ function testNativeInsufficientBalance() external {
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.insufficientNativeBalance(
+ alice,
+ bob,
+ 0,
+ 1,
+ false
+ )
+ )
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.NATIVE,
+ address(0),
+ 0,
+ alice.balance + 1,
+ payable(bob)
+ )
+ })
+ );
+ }
+
+ function testNativeExtraBalance() external {
+ vm.deal(alice, 0.5 ether);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.NATIVE,
+ address(0),
+ 0,
+ 0.5 ether,
+ payable(bob)
+ )
+ })
+ );
+ vm.deal(bob, 0.5 ether);
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.nativeUnexpectedBalance(
+ alice,
+ 0,
+ 0.5 ether
+ )
+ )
+ );
+
+ balances.checkBalances();
+ }
+
+ function testNativeNotTransferred() external {
+ vm.deal(alice, 0.5 ether);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.NATIVE,
+ address(0),
+ 0,
+ 0.5 ether,
+ payable(bob)
+ )
+ })
+ );
+ vm.prank(alice);
+ payable(address(1000)).transfer(0.5 ether);
+
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.nativeUnexpectedBalance(bob, 0.5 ether, 0)
+ )
+ );
+ balances.checkBalances();
+ }
+
+ // =====================================================================//
+ // ERC20 TESTS //
+ // =====================================================================//
+
+ function testERC20InsufficientBalance() external {
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.insufficientERC20Balance(
+ address(erc20),
+ alice,
+ bob,
+ 0,
+ 200,
+ false
+ )
+ )
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 200,
+ payable(bob)
+ )
+ })
+ );
+ }
+
+ function testERC20ExtraBalance() external {
+ erc20.mint(alice, 10);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 5,
+ payable(bob)
+ )
+ })
+ );
+ vm.prank(alice);
+ erc20.transfer(bob, 5);
+ erc20.mint(alice, 1);
+
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.erc20UnexpectedBalance(
+ address(erc20),
+ alice,
+ 5,
+ 6
+ )
+ )
+ );
+ balances.checkBalances();
+ }
+
+ function testERC20NotTransferred() external {
+ erc20.mint(alice, 10);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 5,
+ payable(bob)
+ )
+ })
+ );
+
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.erc20UnexpectedBalance(
+ address(erc20),
+ alice,
+ 5,
+ 10
+ )
+ )
+ );
+ balances.checkBalances();
+ }
+
+ function testERC20MultipleSenders() external {
+ erc20.mint(alice, 100);
+ erc20.mint(bob, 200);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 50,
+ payable(bob)
+ )
+ })
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: bob,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC20,
+ address(erc20),
+ 0,
+ 50,
+ payable(alice)
+ )
+ })
+ );
+ balances.checkBalances();
+ }
+
+ // =====================================================================//
+ // ERC721 TESTS //
+ // =====================================================================//
+
+ function xtestERC721InsufficientBalance() external {
+ erc721.mint(bob, 1);
+ vm.expectRevert(stdError.arithmeticError);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 1,
+ 1,
+ payable(bob)
+ )
+ })
+ );
+ }
+
+ function testERC721ExtraBalance() external {
+ erc721.mint(alice, 1);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 1,
+ 1,
+ payable(bob)
+ )
+ })
+ );
+ erc721.mint(alice, 2);
+
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.erc721UnexpectedBalance(
+ address(erc721),
+ alice,
+ 0,
+ 2
+ )
+ )
+ );
+ balances.checkBalances();
+ }
+
+ function testERC721NotTransferred() external {
+ erc721.mint(alice, 1);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 1,
+ 1,
+ payable(bob)
+ )
+ })
+ );
+ erc721.mint(bob, 2);
+ vm.prank(alice);
+ erc721.transferFrom(alice, address(1000), 1);
+ vm.expectRevert(
+ "ExpectedBalances: account does not own expected token"
+ );
+ balances.checkBalances();
+ }
+
+ function testERC721MultipleIdentifiers() external {
+ erc721.mint(alice, 1);
+ erc721.mint(alice, 2);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 1,
+ 1,
+ payable(bob)
+ )
+ })
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC721,
+ address(erc721),
+ 2,
+ 1,
+ payable(bob)
+ )
+ })
+ );
+ vm.prank(alice);
+ erc721.transferFrom(alice, bob, 1);
+ vm.prank(alice);
+ erc721.transferFrom(alice, bob, 2);
+ balances.checkBalances();
+ }
+
+ // =====================================================================//
+ // ERC1155 TESTS //
+ // =====================================================================//
+
+ function testERC1155InsufficientBalance() external {
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.insufficientERC1155Balance(
+ address(erc1155),
+ 0,
+ alice,
+ bob,
+ 0,
+ 200,
+ false
+ )
+ )
+ );
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC1155,
+ address(erc1155),
+ 0,
+ 200,
+ payable(bob)
+ )
+ })
+ );
+ }
+
+ function testERC1155ExtraBalance() external {
+ erc1155.mint(alice, 1, 10);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC1155,
+ address(erc1155),
+ 1,
+ 5,
+ payable(bob)
+ )
+ })
+ );
+ vm.prank(alice);
+ erc1155.safeTransferFrom(alice, bob, 1, 5, "");
+ erc1155.mint(alice, 1, 1);
+
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.erc1155UnexpectedBalance(
+ address(erc1155),
+ alice,
+ 1,
+ 5,
+ 6
+ )
+ )
+ );
+ balances.checkBalances();
+ }
+
+ function testERC1155NotTransferred() external {
+ erc1155.mint(alice, 1, 10);
+ balances.addTransfer(
+ Execution({
+ offerer: alice,
+ conduitKey: bytes32(0),
+ item: ReceivedItem(
+ ItemType.ERC1155,
+ address(erc1155),
+ 1,
+ 5,
+ payable(bob)
+ )
+ })
+ );
+ vm.prank(alice);
+ erc1155.safeTransferFrom(alice, address(1000), 1, 5, "");
+ vm.expectRevert(
+ bytes(
+ BalanceErrorMessages.erc1155UnexpectedBalance(
+ address(erc1155),
+ bob,
+ 1,
+ 5,
+ 0
+ )
+ )
+ );
+ balances.checkBalances();
+ }
+
+ /**
+ * @dev Deploy test token contracts.
+ */
+ function _deployTestTokenContracts() internal {
+ createErc20Token();
+ createErc721Token();
+ createErc1155Token();
+ }
+
+ function createErc20Token() internal {
+ TestERC20 token = new TestERC20();
+ erc20 = token;
+ vm.label(address(token), "ERC20");
+ }
+
+ function createErc721Token() internal {
+ TestERC721 token = new TestERC721();
+ erc721 = token;
+ vm.label(address(token), "ERC721");
+ }
+
+ function createErc1155Token() internal {
+ TestERC1155 token = new TestERC1155();
+ erc1155 = token;
+ vm.label(address(token), "ERC1155");
+ }
+}
diff --git a/test/foundry/new/FractionUtil.t.sol b/test/foundry/new/FractionUtil.t.sol
new file mode 100644
index 000000000..53c74706c
--- /dev/null
+++ b/test/foundry/new/FractionUtil.t.sol
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+import {
+ FractionUtil,
+ FractionResults,
+ FractionStatus
+} from "./helpers/FractionUtil.sol";
+
+contract FractionUtilTest is Test {
+ function testGetWholeFillResults() public {
+ uint120 currentStatusNumerator = 2;
+ uint120 currentStatusDenominator = 3;
+ uint120 numeratorToFill = 1;
+ uint120 denominatorToFill = 4;
+
+ FractionResults memory results = FractionUtil.getPartialFillResults(
+ currentStatusNumerator,
+ currentStatusDenominator,
+ numeratorToFill,
+ denominatorToFill
+ );
+
+ assertEq(
+ results.realizedNumerator,
+ 3,
+ "Realized numerator should be 3"
+ );
+ assertEq(
+ results.realizedDenominator,
+ 12,
+ "Realized denominator should be 12"
+ );
+ assertEq(
+ results.finalFilledNumerator,
+ 11,
+ "Final filled numerator should be 11"
+ );
+ assertEq(
+ results.finalFilledDenominator,
+ 12,
+ "Final filled denominator should be 12"
+ );
+ assertEq(
+ uint256(results.status),
+ uint256(FractionStatus.WHOLE_FILL),
+ "Status should be WHOLE_FILL"
+ );
+ }
+
+ function testGetPartialFillResults() public {
+ // Test for PARTIAL_FILL
+ uint120 currentStatusNumerator1 = 2;
+ uint120 currentStatusDenominator1 = 3;
+ uint120 numeratorToFill1 = 1;
+ uint120 denominatorToFill1 = 2;
+
+ FractionResults memory results1 = FractionUtil.getPartialFillResults(
+ currentStatusNumerator1,
+ currentStatusDenominator1,
+ numeratorToFill1,
+ denominatorToFill1
+ );
+
+ assertEq(
+ results1.realizedNumerator,
+ 2,
+ "Realized numerator should be 2"
+ );
+ assertEq(
+ results1.realizedDenominator,
+ 6,
+ "Realized denominator should be 6"
+ );
+ assertEq(
+ results1.finalFilledNumerator,
+ 6,
+ "Final filled numerator should be 6"
+ );
+ assertEq(
+ results1.finalFilledDenominator,
+ 6,
+ "Final filled denominator should be 6"
+ );
+ assertEq(
+ uint256(results1.status),
+ uint256(FractionStatus.PARTIAL_FILL),
+ "Status should be PARTIAL_FILL"
+ );
+ }
+
+ function testGetWholeFillResultsGCD() public {
+ // Test for WHOLE_FILL_GCD
+ uint120 currentStatusDenominator = type(uint120).max -
+ (type(uint120).max % 3);
+ uint120 currentStatusNumerator = (currentStatusDenominator / 3) * 2;
+
+ uint120 numeratorToFill = 2;
+ uint120 denominatorToFill = 6;
+
+ FractionResults memory results2 = FractionUtil.getPartialFillResults(
+ currentStatusNumerator,
+ currentStatusDenominator,
+ numeratorToFill,
+ denominatorToFill
+ );
+
+ assertEq(
+ results2.realizedNumerator,
+ 1,
+ "Realized numerator should be 1"
+ );
+ assertEq(
+ results2.realizedDenominator,
+ 3,
+ "Realized denominator should be 3"
+ );
+ assertEq(
+ results2.finalFilledNumerator,
+ 3,
+ "Final filled numerator should be 3"
+ );
+ assertEq(
+ results2.finalFilledDenominator,
+ 3,
+ "Final filled denominator should be 3"
+ );
+ assertEq(
+ uint256(results2.status),
+ uint256(FractionStatus.WHOLE_FILL_GCD),
+ "Status should be WHOLE_FILL_GCD"
+ );
+ }
+
+ function testGetPartialFillResultsGCD() public {
+ // Test for PARTIAL_FILL_GCD
+ uint120 currentStatusDenominator = type(uint120).max -
+ (type(uint120).max % 3);
+ uint120 currentStatusNumerator = (currentStatusDenominator / 3) * 2;
+
+ uint120 numeratorToFill = 1;
+ uint120 denominatorToFill = 2;
+
+ FractionResults memory results3 = FractionUtil.getPartialFillResults(
+ currentStatusNumerator,
+ currentStatusDenominator,
+ numeratorToFill,
+ denominatorToFill
+ );
+
+ assertEq(
+ results3.realizedNumerator,
+ 1,
+ "Realized numerator should be 1"
+ );
+ assertEq(
+ results3.realizedDenominator,
+ 3,
+ "Realized denominator should be 3"
+ );
+ assertEq(
+ results3.finalFilledNumerator,
+ 3,
+ "Final filled numerator should be 3"
+ );
+ assertEq(
+ results3.finalFilledDenominator,
+ 3,
+ "Final filled denominator should be 3"
+ );
+ assertEq(
+ uint256(results3.status),
+ uint256(FractionStatus.PARTIAL_FILL_GCD),
+ "Status should be PARTIAL_FILL_GCD"
+ );
+ }
+
+ function testGetInvalidResults() public {
+ // Test for INVALID
+ // prime?s generated using Miller-Rabin test
+ uint120 currentStatusNumerator = 1;
+ // 2 ** 119 < prime1 < 2 ** 120
+ uint120 currentStatusDenominator = 664613997892457936451903530140172393;
+ uint120 numeratorToFill = 1;
+ // prime1 < prime2 < 2 ** 120
+ uint120 denominatorToFill = 664613997892457936451903530140172297;
+
+ FractionResults memory results4 = FractionUtil.getPartialFillResults(
+ currentStatusNumerator,
+ currentStatusDenominator,
+ numeratorToFill,
+ denominatorToFill
+ );
+
+ assertEq(
+ results4.realizedNumerator,
+ 0,
+ "Realized numerator should be 0"
+ );
+ assertEq(
+ results4.realizedDenominator,
+ 0,
+ "Realized denominator should be 0"
+ );
+ assertEq(
+ results4.finalFilledNumerator,
+ 0,
+ "Final filled numerator should be 0"
+ );
+ assertEq(
+ results4.finalFilledDenominator,
+ 0,
+ "Final filled denominator should be 0"
+ );
+ assertEq(
+ uint256(results4.status),
+ uint256(FractionStatus.INVALID),
+ "Status should be INVALID"
+ );
+ }
+}
diff --git a/test/foundry/new/FuzzCoverage.t.sol b/test/foundry/new/FuzzCoverage.t.sol
new file mode 100644
index 000000000..d2bdecfc0
--- /dev/null
+++ b/test/foundry/new/FuzzCoverage.t.sol
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import { FuzzEngine } from "./helpers/FuzzEngine.sol";
+import { FuzzParams } from "./helpers/FuzzTestContextLib.sol";
+
+contract FuzzCoverageTestSuite is FuzzEngine {
+ using LibPRNG for LibPRNG.PRNG;
+
+ function test_fuzzCoverage_1() public {
+ _run(LibPRNG.PRNG({ state: 1 }));
+ }
+
+ function test_fuzzCoverage_2() public {
+ _run(LibPRNG.PRNG({ state: 2 }));
+ }
+
+ // NOTE: this state trips an assume; skip it
+ function xtest_fuzzCoverage_3() public {
+ _run(LibPRNG.PRNG({ state: 3 }));
+ }
+
+ function test_fuzzCoverage_4() public {
+ _run(LibPRNG.PRNG({ state: 4 }));
+ }
+
+ // NOTE: this state trips an assume; skip it
+ function xtest_fuzzCoverage_5() public {
+ _run(LibPRNG.PRNG({ state: 5 }));
+ }
+
+ function test_fuzzCoverage_6() public {
+ _run(LibPRNG.PRNG({ state: 6 }));
+ }
+
+ function test_fuzzCoverage_7() public {
+ _run(LibPRNG.PRNG({ state: 7 }));
+ }
+
+ function test_fuzzCoverage_8() public {
+ _run(LibPRNG.PRNG({ state: 8 }));
+ }
+
+ // NOTE: this state trips an assume; skip it
+ function xtest_fuzzCoverage_9() public {
+ _run(LibPRNG.PRNG({ state: 9 }));
+ }
+
+ // NOTE: this state trips an assume; skip it
+ function xtest_fuzzCoverage_10() public {
+ _run(LibPRNG.PRNG({ state: 10 }));
+ }
+
+ function test_fuzzCoverage_11() public {
+ _run(LibPRNG.PRNG({ state: 11 }));
+ }
+
+ function test_fuzzCoverage_12() public {
+ _run(LibPRNG.PRNG({ state: 12 }));
+ }
+
+ // NOTE: this state trips an assume; skip it
+ function xtest_fuzzCoverage_13() public {
+ _run(LibPRNG.PRNG({ state: 13 }));
+ }
+
+ // NOTE: this state trips a `no_explicit_executions_match` assume; skip it
+ function xtest_fuzzCoverage_14() public {
+ _run(LibPRNG.PRNG({ state: 14 }));
+ }
+
+ function test_fuzzCoverage_15() public {
+ _run(LibPRNG.PRNG({ state: 15 }));
+ }
+
+ function test_fuzzCoverage_16() public {
+ _run(LibPRNG.PRNG({ state: 16 }));
+ }
+
+ // NOTE: this state trips a `no_explicit_executions_match` assume; skip it
+ function xtest_fuzzCoverage_17() public {
+ _run(LibPRNG.PRNG({ state: 17 }));
+ }
+
+ function test_fuzzCoverage_18() public {
+ _run(LibPRNG.PRNG({ state: 18 }));
+ }
+
+ function test_fuzzCoverage_19() public {
+ _run(LibPRNG.PRNG({ state: 19 }));
+ }
+
+ function test_fuzzCoverage_20() public {
+ _run(LibPRNG.PRNG({ state: 20 }));
+ }
+
+ function test_fuzzCoverage_x() public {
+ _runConcrete(0, 0, 0, 0);
+ }
+
+ function _run(LibPRNG.PRNG memory prng) internal {
+ uint256 seed = prng.next();
+ uint256 totalOrders = prng.next();
+ uint256 maxOfferItems = prng.next();
+ uint256 maxConsiderationItems = prng.next();
+ _runConcrete(seed, totalOrders, maxOfferItems, maxConsiderationItems);
+ }
+
+ function _runConcrete(
+ uint256 seed,
+ uint256 totalOrders,
+ uint256 maxOfferItems,
+ uint256 maxConsiderationItems
+ ) internal {
+ run(
+ FuzzParams({
+ seed: seed,
+ totalOrders: bound(totalOrders, 1, 10),
+ maxOfferItems: bound(maxOfferItems, 0, 10),
+ maxConsiderationItems: bound(maxConsiderationItems, 0, 10),
+ seedInput: abi.encodePacked(
+ seed,
+ totalOrders,
+ maxOfferItems,
+ maxConsiderationItems
+ )
+ })
+ );
+ }
+}
diff --git a/test/foundry/new/FuzzEngine.t.sol b/test/foundry/new/FuzzEngine.t.sol
new file mode 100644
index 000000000..e0a5631e9
--- /dev/null
+++ b/test/foundry/new/FuzzEngine.t.sol
@@ -0,0 +1,2015 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ FulfillmentComponentLib,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib,
+ SeaportArrays,
+ ZoneParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ ConsiderationItem,
+ CriteriaResolver,
+ Fulfillment,
+ FulfillmentComponent,
+ ItemType,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ OrderType
+} from "seaport-sol/SeaportStructs.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../contracts/test/HashValidationZoneOfferer.sol";
+
+import {
+ TestCalldataHashContractOfferer
+} from "../../../contracts/test/TestCalldataHashContractOfferer.sol";
+
+import {
+ FuzzEngine,
+ FuzzEngineLib,
+ FuzzParams,
+ FuzzTestContext,
+ FuzzTestContextLib
+} from "./helpers/FuzzEngine.sol";
+
+import { AdvancedOrder, FuzzHelpers } from "./helpers/FuzzHelpers.sol";
+
+import { BaseOrderTest } from "./BaseOrderTest.sol";
+
+contract FuzzEngineTest is FuzzEngine {
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using FulfillmentComponentLib for FulfillmentComponent[];
+ using FulfillmentLib for Fulfillment;
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+ using ZoneParametersLib for AdvancedOrder[];
+
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for FuzzTestContext;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for AdvancedOrder[];
+ using FuzzTestContextLib for FuzzTestContext;
+
+ error ExampleErrorWithContextData(bytes signature);
+
+ function setUp() public virtual override {
+ super.setUp();
+
+ OrderParameters memory standardOrderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters();
+ OrderLib.empty().withParameters(standardOrderParameters).saveDefault(
+ STANDARD
+ );
+ }
+
+ /// @dev Get all actions for a single, standard order.
+ function test_actions_Single_Standard() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory expectedActions = new bytes4[](4);
+ expectedActions[0] = SeaportInterface.fulfillOrder.selector;
+ expectedActions[1] = SeaportInterface.fulfillAdvancedOrder.selector;
+ expectedActions[2] = SeaportInterface.fulfillAvailableOrders.selector;
+ expectedActions[3] = SeaportInterface
+ .fulfillAvailableAdvancedOrders
+ .selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(1);
+ assertEq(context.actions(), expectedActions);
+ }
+
+ /// @dev Get one action for a single, standard order.
+ function test_action_Single_Standard() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(1);
+ assertEq(context.action(), SeaportInterface.fulfillOrder.selector);
+
+ context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 1,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(1);
+ assertEq(
+ context.action(),
+ SeaportInterface.fulfillAdvancedOrder.selector
+ );
+ }
+
+ /// @dev Get all actions for a single, advanced order.
+ function test_actions_Single_Advanced() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("extra data")
+ });
+
+ bytes4[] memory expectedActions = new bytes4[](2);
+ expectedActions[0] = SeaportInterface.fulfillAdvancedOrder.selector;
+ expectedActions[1] = SeaportInterface
+ .fulfillAvailableAdvancedOrders
+ .selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length);
+ assertEq(context.actions(), expectedActions);
+ }
+
+ /// @dev Get one action for a single, advanced order.
+ function test_action_Single_Advanced() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("extra data")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(1);
+ assertEq(
+ context.action(),
+ SeaportInterface.fulfillAdvancedOrder.selector
+ );
+ }
+
+ /// @dev Get one action for a single, basic order.
+ function test_action_Single_Basic() public {
+ AdvancedOrder[] memory orders = _setUpBasicOrder();
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 2,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(2),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(1);
+ assertEq(context.action(), SeaportInterface.fulfillBasicOrder.selector);
+
+ context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 3,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(3),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(1);
+ assertEq(
+ context.action(),
+ getSeaport().fulfillBasicOrder_efficient_6GL6yc.selector
+ );
+ }
+
+ /// @dev Get all actions for a single, basic order.
+ function test_actions_Single_Basic() public {
+ AdvancedOrder[] memory orders = _setUpBasicOrder();
+
+ bytes4[] memory expectedActions = new bytes4[](6);
+ expectedActions[0] = SeaportInterface.fulfillOrder.selector;
+ expectedActions[1] = SeaportInterface.fulfillAdvancedOrder.selector;
+ expectedActions[2] = SeaportInterface.fulfillBasicOrder.selector;
+ expectedActions[3] = SeaportInterface
+ .fulfillBasicOrder_efficient_6GL6yc
+ .selector;
+ expectedActions[4] = SeaportInterface.fulfillAvailableOrders.selector;
+ expectedActions[5] = SeaportInterface
+ .fulfillAvailableAdvancedOrders
+ .selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length);
+ assertEq(context.actions(), expectedActions);
+ }
+
+ /// @dev Get all actions for a combined order.
+ function test_actions_Combined() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](2);
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ orders[1] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory expectedActions = new bytes4[](4);
+ expectedActions[0] = SeaportInterface.fulfillAvailableOrders.selector;
+ expectedActions[1] = SeaportInterface
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ expectedActions[2] = SeaportInterface.matchOrders.selector;
+ expectedActions[3] = SeaportInterface.matchAdvancedOrders.selector;
+ // TODO: undo pended actions (cancel, validate)
+ /** expectedActions[4] = SeaportInterface.cancel.selector;
+ expectedActions[5] = SeaportInterface.validate.selector; */
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length);
+ assertEq(context.actions(), expectedActions);
+ }
+
+ /// @dev Get a single action for a combined order.
+ function test_action_Combined() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](2);
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ });
+ orders[1] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(2);
+ assertEq(
+ context.action(),
+ SeaportInterface.fulfillAvailableOrders.selector
+ );
+
+ context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 1,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(1),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(2);
+ assertEq(
+ context.action(),
+ getSeaport().fulfillAvailableAdvancedOrders.selector
+ );
+
+ context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 2,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(2),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(2);
+ assertEq(context.action(), SeaportInterface.matchOrders.selector);
+
+ context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 3,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(3),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(2);
+ assertEq(
+ context.action(),
+ SeaportInterface.matchAdvancedOrders.selector
+ );
+
+ // TODO: undo pended actions (match, cancel, validate)
+ /** context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this),
+ fuzzParams: FuzzParams({ seed: 4 })
+ });
+ assertEq(context.action(), SeaportInterface.cancel.selector);
+
+ context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this),
+ fuzzParams: FuzzParams({ seed: 5 })
+ });
+ assertEq(context.action(), SeaportInterface.validate.selector); */
+ }
+
+ /// @dev Call exec for a single standard order.
+ function test_exec_StandardOrder() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length);
+
+ exec(context);
+ assertEq(context.returnValues.fulfilled, true);
+ }
+
+ /// @dev Call exec for a single advanced order.
+ function test_exec_AdvancedOrder() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("extra data")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withRecipient(address(0xbeef));
+
+ exec(context);
+ assertEq(context.returnValues.fulfilled, true);
+ }
+
+ function _setUpBasicOrder() internal returns (AdvancedOrder[] memory) {
+ erc721s[0].mint(offerer1.addr, 1);
+
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+
+ offerItems[0] = offerItem;
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 1
+ );
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(1)
+ .withRecipient(offerer1.addr);
+
+ considerationItems[0] = considerationItem;
+
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(
+ orderComponents.toOrderParameters().withOrderType(
+ OrderType.FULL_OPEN
+ )
+ )
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ return orders;
+ }
+
+ /// @dev Call exec for a single basic order. Stub the fuzz seed so that it
+ /// always calls Seaport.fulfillBasicOrder.
+ function test_exec_FulfillBasicOrder() public {
+ AdvancedOrder[] memory orders = _setUpBasicOrder();
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_orderFulfilled.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(offerer1.addr)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 2,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(2),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withBasicOrderParameters(
+ orders[0].toBasicOrderParameters(orders[0].getBasicOrderType())
+ );
+
+ exec(context);
+ }
+
+ /// @dev Call exec for a single basic order. Stub the fuzz seed so that it
+ /// always calls Seaport.fulfillBasicOrder_efficient_6GL6yc.
+ function test_exec_FulfillBasicOrder_efficient_6GL6yc() public {
+ AdvancedOrder[] memory orders = _setUpBasicOrder();
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_orderFulfilled.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(offerer1.addr)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 3,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(3),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withBasicOrderParameters(
+ orders[0].toBasicOrderParameters(orders[0].getBasicOrderType())
+ );
+
+ exec(context);
+ }
+
+ /// @dev Call exec for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.fulfillAvailableOrders.
+ function test_exec_Combined_FulfillAvailable() public {
+ // Offer ERC20
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(1);
+ offerItems[0] = offerItem;
+
+ // Consider single ERC721 to offerer1
+ erc721s[0].mint(address(this), 1);
+ ConsiderationItem[]
+ memory considerationItems1 = new ConsiderationItem[](1);
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ considerationItems1[0] = considerationItem;
+
+ // Consider single ERC721 to offerer1
+ erc721s[0].mint(address(this), 2);
+ ConsiderationItem[]
+ memory considerationItems2 = new ConsiderationItem[](1);
+ considerationItem = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1);
+ considerationItems2[0] = considerationItem;
+
+ OrderComponents memory orderComponents1 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems1);
+
+ OrderComponents memory orderComponents2 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems2);
+
+ bytes memory signature1 = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents1)
+ );
+
+ Order memory order1 = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents1.toOrderParameters())
+ .withSignature(signature1);
+
+ bytes memory signature2 = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents2)
+ );
+
+ Order memory order2 = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents2.toOrderParameters())
+ .withSignature(signature2);
+
+ Order[] memory orders = new Order[](2);
+ orders[0] = order1;
+ orders[1] = order2;
+
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2);
+ advancedOrders[0] = order1.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ advancedOrders[1] = order2.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ (
+ FulfillmentComponent[][] memory offerComponents,
+ FulfillmentComponent[][] memory considerationComponents
+ ) = getNaiveFulfillmentComponents(orders);
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: advancedOrders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(advancedOrders.length)
+ .withOfferFulfillments(offerComponents)
+ .withConsiderationFulfillments(considerationComponents)
+ .withMaximumFulfilled(2);
+
+ exec(context);
+
+ assertEq(context.returnValues.availableOrders.length, 2);
+ assertEq(context.returnValues.availableOrders[0], true);
+ assertEq(context.returnValues.availableOrders[1], true);
+
+ assertEq(context.returnValues.executions.length, 4);
+ assertEq(
+ context.returnValues.executions[0].item.itemType,
+ ItemType.ERC20
+ );
+ assertEq(
+ context.returnValues.executions[0].item.token,
+ address(erc20s[0])
+ );
+ assertEq(context.returnValues.executions[0].item.identifier, 0);
+ assertEq(context.returnValues.executions[0].item.amount, 1);
+ assertEq(
+ context.returnValues.executions[0].item.recipient,
+ address(this)
+ );
+
+ assertEq(
+ context.returnValues.executions[1].item.itemType,
+ ItemType.ERC20
+ );
+ assertEq(
+ context.returnValues.executions[1].item.token,
+ address(erc20s[0])
+ );
+ assertEq(context.returnValues.executions[1].item.identifier, 0);
+ assertEq(context.returnValues.executions[1].item.amount, 1);
+ assertEq(
+ context.returnValues.executions[1].item.recipient,
+ address(this)
+ );
+
+ assertEq(
+ context.returnValues.executions[2].item.itemType,
+ ItemType.ERC721
+ );
+ assertEq(
+ context.returnValues.executions[2].item.token,
+ address(erc721s[0])
+ );
+ assertEq(context.returnValues.executions[2].item.identifier, 1);
+ assertEq(context.returnValues.executions[2].item.amount, 1);
+ assertEq(
+ context.returnValues.executions[2].item.recipient,
+ offerer1.addr
+ );
+
+ assertEq(
+ context.returnValues.executions[3].item.itemType,
+ ItemType.ERC721
+ );
+ assertEq(
+ context.returnValues.executions[3].item.token,
+ address(erc721s[0])
+ );
+ assertEq(context.returnValues.executions[3].item.identifier, 2);
+ assertEq(context.returnValues.executions[3].item.amount, 1);
+ assertEq(
+ context.returnValues.executions[3].item.recipient,
+ offerer1.addr
+ );
+
+ assertEq(context.returnValues.executions[0].offerer, offerer1.addr);
+ assertEq(context.returnValues.executions[1].offerer, offerer1.addr);
+ assertEq(context.returnValues.executions[2].offerer, address(this));
+ assertEq(context.returnValues.executions[3].offerer, address(this));
+
+ assertEq(
+ context.returnValues.executions[0].conduitKey,
+ context.executionState.fulfillerConduitKey
+ );
+ assertEq(
+ context.returnValues.executions[1].conduitKey,
+ context.executionState.fulfillerConduitKey
+ );
+ assertEq(
+ context.returnValues.executions[2].conduitKey,
+ context.executionState.fulfillerConduitKey
+ );
+ assertEq(
+ context.returnValues.executions[3].conduitKey,
+ context.executionState.fulfillerConduitKey
+ );
+ }
+
+ /// @dev Call exec for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.fulfillAvailableAdvancedOrders.
+ function test_exec_Combined_FulfillAvailableAdvanced() public {
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2);
+
+ {
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ ConsiderationItem[]
+ memory considerationItems1 = new ConsiderationItem[](1);
+ ConsiderationItem[]
+ memory considerationItems2 = new ConsiderationItem[](1);
+ {
+ // Offer ERC20
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(1);
+ offerItems[0] = offerItem;
+
+ // Consider single ERC721 to offerer1
+ erc721s[0].mint(address(this), 1);
+ ConsiderationItem
+ memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ considerationItems1[0] = considerationItem;
+
+ // Consider single ERC721 to offerer1
+ erc721s[0].mint(address(this), 2);
+ considerationItem = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1);
+ considerationItems2[0] = considerationItem;
+ }
+
+ OrderComponents memory orderComponents1 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems1);
+
+ OrderComponents memory orderComponents2 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems2);
+
+ Order memory order1 = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents1.toOrderParameters())
+ .withSignature(
+ signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents1)
+ )
+ );
+
+ Order memory order2 = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents2.toOrderParameters())
+ .withSignature(
+ signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents2)
+ )
+ );
+
+ advancedOrders[0] = order1.toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ });
+ advancedOrders[1] = order2.toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ });
+ }
+
+ (
+ FulfillmentComponent[][] memory offerComponents,
+ FulfillmentComponent[][] memory considerationComponents
+ ) = getNaiveFulfillmentComponents(advancedOrders);
+
+ bytes4[] memory checks = new bytes4[](2);
+ checks[0] = this.check_allOrdersFilled.selector;
+ checks[1] = this.check_executionsPresent.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: advancedOrders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 1,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(1),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(advancedOrders.length);
+
+ context = context
+ .withChecks(checks)
+ .withOfferFulfillments(offerComponents)
+ .withConsiderationFulfillments(considerationComponents)
+ .withMaximumFulfilled(2);
+
+ exec(context);
+ checkAll(context);
+ }
+
+ /// @dev Call run for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.matchOrders.
+ function test_exec_Combined_matchOrders() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](2);
+ {
+ OfferItem[] memory offerItemsPrime = new OfferItem[](1);
+ OfferItem[] memory offerItemsMirror = new OfferItem[](1);
+ ConsiderationItem[]
+ memory considerationItemsPrime = new ConsiderationItem[](1);
+ ConsiderationItem[]
+ memory considerationItemsMirror = new ConsiderationItem[](1);
+ {
+ // Offer ERC20
+ OfferItem memory offerItemPrime = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(1);
+ offerItemsPrime[0] = offerItemPrime;
+
+ // Consider single ERC721 to offerer1
+ erc721s[0].mint(offerer2.addr, 1);
+ ConsiderationItem
+ memory considerationItemPrime = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ considerationItemsPrime[0] = considerationItemPrime;
+
+ offerItemsMirror[0] = considerationItemsPrime[0].toOfferItem();
+
+ considerationItemsMirror[0] = offerItemsPrime[0]
+ .toConsiderationItem(offerer2.addr);
+ }
+
+ OrderComponents memory orderComponentsPrime = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItemsPrime)
+ .withConsideration(considerationItemsPrime);
+
+ OrderComponents memory orderComponentsMirror = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer2.addr)
+ .withOffer(offerItemsMirror)
+ .withConsideration(considerationItemsMirror);
+
+ Order memory orderPrime = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponentsPrime.toOrderParameters())
+ .withSignature(
+ signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponentsPrime)
+ )
+ );
+
+ Order memory orderMirror = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponentsMirror.toOrderParameters())
+ .withSignature(
+ signOrder(
+ getSeaport(),
+ offerer2.key,
+ getSeaport().getOrderHash(orderComponentsMirror)
+ )
+ );
+
+ orders[0] = orderPrime.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ orders[1] = orderMirror.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ }
+
+ Fulfillment[] memory fulfillments;
+
+ SeaportInterface seaport = getSeaport();
+
+ {
+ CriteriaResolver[] memory resolvers;
+ bytes32[] memory orderHashes = orders.getOrderHashes(
+ address(seaport)
+ );
+
+ (fulfillments, , ) = matcher.getMatchedFulfillments(
+ orders,
+ resolvers,
+ orderHashes,
+ new UnavailableReason[](orders.length)
+ );
+ }
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_executionsPresent.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({ orders: orders, seaport: seaport, caller: offerer1.addr })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 2,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(2),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withChecks(checks)
+ .withFulfillments(fulfillments);
+
+ exec(context);
+ checkAll(context);
+ }
+
+ /// @dev Call run for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.matchAdvancedOrders.
+ function test_exec_Combined_matchAdvancedOrders() public {
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2);
+ {
+ OfferItem[] memory offerItemsPrime = new OfferItem[](1);
+ OfferItem[] memory offerItemsMirror = new OfferItem[](1);
+ ConsiderationItem[]
+ memory considerationItemsPrime = new ConsiderationItem[](1);
+ ConsiderationItem[]
+ memory considerationItemsMirror = new ConsiderationItem[](1);
+ {
+ // Offer ERC20
+ OfferItem memory offerItemPrime = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(1);
+ offerItemsPrime[0] = offerItemPrime;
+
+ // Consider single ERC721 to offerer1
+ erc721s[0].mint(offerer2.addr, 1);
+ ConsiderationItem
+ memory considerationItemPrime = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ considerationItemsPrime[0] = considerationItemPrime;
+
+ offerItemsMirror[0] = considerationItemsPrime[0].toOfferItem();
+
+ considerationItemsMirror[0] = offerItemsPrime[0]
+ .toConsiderationItem(offerer2.addr);
+ }
+
+ OrderComponents memory orderComponentsPrime = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItemsPrime)
+ .withConsideration(considerationItemsPrime);
+
+ OrderComponents memory orderComponentsMirror = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer2.addr)
+ .withOffer(offerItemsMirror)
+ .withConsideration(considerationItemsMirror);
+
+ Order memory orderPrime = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponentsPrime.toOrderParameters())
+ .withSignature(
+ signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponentsPrime)
+ )
+ );
+
+ Order memory orderMirror = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponentsMirror.toOrderParameters())
+ .withSignature(
+ signOrder(
+ getSeaport(),
+ offerer2.key,
+ getSeaport().getOrderHash(orderComponentsMirror)
+ )
+ );
+
+ advancedOrders[0] = orderPrime.toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ });
+ advancedOrders[1] = orderMirror.toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ });
+ }
+
+ Fulfillment[] memory fulfillments;
+
+ SeaportInterface seaport = getSeaport();
+
+ {
+ bytes32[] memory orderHashes = advancedOrders.getOrderHashes(
+ address(seaport)
+ );
+
+ CriteriaResolver[] memory resolvers;
+ (fulfillments, , ) = matcher.getMatchedFulfillments(
+ advancedOrders,
+ resolvers,
+ orderHashes,
+ new UnavailableReason[](advancedOrders.length)
+ );
+ }
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_executionsPresent.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: advancedOrders,
+ seaport: seaport,
+ caller: offerer1.addr
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 3,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(3),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(advancedOrders.length)
+ .withChecks(checks)
+ .withFulfillments(fulfillments);
+
+ exec(context);
+ checkAll(context);
+ }
+
+ /// @dev Call exec for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.validate.
+ function xtest_exec_Combined_Validate() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](2);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ orders[1] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_orderValidated.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 5,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(5),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withChecks(checks);
+
+ exec(context);
+ checkAll(context);
+ }
+
+ /// @dev Call exec for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.cancel.
+ function xtest_exec_Combined_Cancel() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](2);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ orders[1] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_orderCancelled.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: offerer1.addr
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 4,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(4),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withChecks(checks);
+
+ exec(context);
+ checkAll(context);
+ }
+
+ /// @dev Call checkAll to run a simple check that always reverts.
+ function test_check_StandardOrder_SimpleCheck() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_alwaysRevert.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withChecks(checks);
+
+ exec(context);
+
+ vm.expectRevert("this check always reverts");
+ checkAll(context);
+ }
+
+ /// @dev Call checkAll to run a check that uses the FuzzTestContext.
+ function test_check_StandardOrder_checkWithContext() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_revertWithContextData.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withChecks(checks);
+
+ exec(context);
+
+ vm.expectRevert(
+ abi.encodeWithSelector(
+ ExampleErrorWithContextData.selector,
+ context.executionState.orders[0].signature
+ )
+ );
+ checkAll(context);
+ }
+
+ // TODO: unskip
+ function xtest_check_validateOrderExpectedDataHash() public {
+ Order[] memory orders = new Order[](2);
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](2);
+
+ // New scope for setup
+ {
+ HashValidationZoneOfferer zone = new HashValidationZoneOfferer(
+ address(this)
+ );
+ // Offer ERC20
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(1);
+ offerItems[0] = offerItem;
+
+ // Consider single ERC721 to offerer1
+ ConsiderationItem[]
+ memory considerationItems1 = new ConsiderationItem[](1);
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ considerationItems1[0] = considerationItem;
+
+ // Consider single ERC721 to offerer1
+ ConsiderationItem[]
+ memory considerationItems2 = new ConsiderationItem[](1);
+ considerationItem = ConsiderationItemLib
+ .empty()
+ .withRecipient(offerer1.addr)
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1);
+ considerationItems2[0] = considerationItem;
+
+ OrderComponents memory orderComponents1 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withZone(address(zone))
+ .withOrderType(OrderType.FULL_RESTRICTED)
+ .withConsideration(considerationItems1);
+
+ OrderComponents memory orderComponents2 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withZone(address(zone))
+ .withOrderType(OrderType.FULL_RESTRICTED)
+ .withConsideration(considerationItems2);
+
+ bytes memory signature1 = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents1)
+ );
+
+ Order memory order1 = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents1.toOrderParameters())
+ .withSignature(signature1);
+
+ bytes memory signature2 = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents2)
+ );
+
+ Order memory order2 = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents2.toOrderParameters())
+ .withSignature(signature2);
+
+ orders[0] = order1;
+ orders[1] = order2;
+
+ advancedOrders[0] = order1.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ advancedOrders[1] = order2.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ }
+
+ (
+ FulfillmentComponent[][] memory offerComponents,
+ FulfillmentComponent[][] memory considerationComponents
+ ) = getNaiveFulfillmentComponents(orders);
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_validateOrderExpectedDataHash.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: advancedOrders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withOfferFulfillments(offerComponents)
+ .withConsiderationFulfillments(considerationComponents)
+ .withChecks(checks)
+ .withMaximumFulfilled(2);
+
+ context.expectations.expectedZoneCalldataHash = advancedOrders
+ .getExpectedZoneCalldataHash(
+ address(getSeaport()),
+ address(this),
+ new CriteriaResolver[](0),
+ 2,
+ new UnavailableReason[](advancedOrders.length)
+ );
+
+ run(context);
+ }
+
+ function _prepareContractOfferers()
+ internal
+ returns (
+ TestCalldataHashContractOfferer contractOfferer1,
+ TestCalldataHashContractOfferer contractOfferer2
+ )
+ {
+ contractOfferer1 = new TestCalldataHashContractOfferer(
+ address(getSeaport())
+ );
+ contractOfferer2 = new TestCalldataHashContractOfferer(
+ address(getSeaport())
+ );
+ contractOfferer1.setExpectedOfferRecipient(address(this));
+ contractOfferer2.setExpectedOfferRecipient(address(this));
+
+ // Mint the erc20 to the test contract to be transferred to the contract offerers
+ // in the call to activate
+ erc20s[0].mint(address(this), 2);
+
+ // Approve the contract offerers to transfer tokens from the test contract
+ erc20s[0].approve(address(contractOfferer1), 1);
+ erc20s[0].approve(address(contractOfferer2), 1);
+ }
+
+ function _getAdvancedOrdersAndFulfillmentComponents(
+ TestCalldataHashContractOfferer contractOfferer1,
+ TestCalldataHashContractOfferer contractOfferer2
+ )
+ internal
+ returns (
+ AdvancedOrder[] memory,
+ FulfillmentComponent[][] memory,
+ FulfillmentComponent[][] memory
+ )
+ {
+ AdvancedOrder[] memory orders;
+ {
+ OrderComponents memory orderComponents1 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(address(contractOfferer1))
+ .withOrderType(OrderType.CONTRACT);
+ {
+ TestCalldataHashContractOfferer _temp = contractOfferer1;
+ {
+ ConsiderationItem[]
+ memory considerationItems = SeaportArrays
+ .ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withRecipient(address(_temp))
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1)
+ );
+ orderComponents1 = orderComponents1.withConsideration(
+ considerationItems
+ );
+ }
+
+ // Offer ERC20
+ {
+ OfferItem[] memory offerItems = SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(1)
+ );
+ orderComponents1 = orderComponents1.withOffer(offerItems);
+ }
+ }
+
+ OrderComponents memory orderComponents2;
+
+ {
+ TestCalldataHashContractOfferer _temp = contractOfferer2;
+
+ // Overwrite existing ConsiderationItem[] for order2
+ ConsiderationItem[] memory considerationItems = SeaportArrays
+ .ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withRecipient(address(_temp))
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1)
+ );
+
+ orderComponents2 = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(address(_temp))
+ .withOffer(orderComponents1.offer)
+ .withOrderType(OrderType.CONTRACT)
+ .withConsideration(considerationItems);
+ }
+ orders = SeaportArrays.AdvancedOrders(
+ AdvancedOrderLib.fromDefault(FULL).withParameters(
+ orderComponents1.toOrderParameters()
+ ),
+ AdvancedOrderLib.fromDefault(FULL).withParameters(
+ orderComponents2.toOrderParameters()
+ )
+ );
+ }
+
+ // Activate the contract orders
+ contractOfferer1.activate(
+ address(this),
+ orders[0].parameters.offer.toSpentItemArray(),
+ orders[0].parameters.consideration.toSpentItemArray(),
+ ""
+ );
+ contractOfferer2.activate(
+ address(this),
+ orders[1].parameters.offer.toSpentItemArray(),
+ orders[1].parameters.consideration.toSpentItemArray(),
+ ""
+ );
+
+ (
+ FulfillmentComponent[][] memory offerComponents,
+ FulfillmentComponent[][] memory considerationComponents
+ ) = getNaiveFulfillmentComponents(orders);
+
+ return (orders, offerComponents, considerationComponents);
+ }
+
+ // TODO: unskip
+ function xtest_check_contractOrderExpectedDataHashes() public {
+ (
+ TestCalldataHashContractOfferer contractOfferer1,
+ TestCalldataHashContractOfferer contractOfferer2
+ ) = _prepareContractOfferers();
+
+ AdvancedOrder[] memory advancedOrders;
+ FulfillmentComponent[][] memory offerComponents;
+ FulfillmentComponent[][] memory considerationComponents;
+
+ (
+ advancedOrders,
+ offerComponents,
+ considerationComponents
+ ) = _getAdvancedOrdersAndFulfillmentComponents(
+ contractOfferer1,
+ contractOfferer2
+ );
+
+ {
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_contractOrderExpectedDataHashes.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: advancedOrders,
+ seaport: getSeaport(),
+ caller: address(this)
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(0),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ );
+
+ context = context
+ .withMaximumFulfilled(advancedOrders.length)
+ .withOfferFulfillments(offerComponents)
+ .withConsiderationFulfillments(considerationComponents)
+ .withChecks(checks)
+ .withMaximumFulfilled(2);
+
+ bytes32[2][] memory expectedContractOrderCalldataHashes;
+ expectedContractOrderCalldataHashes = context
+ .getExpectedContractOffererCalldataHashes();
+ context
+ .expectations
+ .expectedContractOrderCalldataHashes = expectedContractOrderCalldataHashes;
+
+ run(context);
+ }
+ }
+
+ /// @dev Call run for a combined order. Stub the fuzz seed so that it
+ /// always calls Seaport.cancel.
+ function xtest_run_Combined_Cancel() public {
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr);
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](2);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ orders[1] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ bytes4[] memory checks = new bytes4[](1);
+ checks[0] = this.check_orderCancelled.selector;
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: offerer1.addr
+ })
+ .withFuzzParams(
+ FuzzParams({
+ seed: 4,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: abi.encodePacked(
+ uint256(4),
+ uint256(0),
+ uint256(0),
+ uint256(0)
+ )
+ })
+ )
+ .withMaximumFulfilled(orders.length)
+ .withChecks(checks);
+
+ run(context);
+ }
+
+ /// @dev Example of a simple "check" function. This one takes no args.
+ function check_alwaysRevert() public pure {
+ revert("this check always reverts");
+ }
+
+ /// @dev Example of a "check" function that uses the test context.
+ function check_revertWithContextData(
+ FuzzTestContext memory context
+ ) public pure {
+ revert ExampleErrorWithContextData(
+ context.executionState.orders[0].signature
+ );
+ }
+
+ function assertEq(bytes4[] memory a, bytes4[] memory b) internal {
+ if (a.length != b.length) revert("Array length mismatch");
+ for (uint256 i; i < a.length; ++i) {
+ assertEq(a[i], b[i]);
+ }
+ }
+
+ function assertEq(ItemType a, ItemType b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+}
diff --git a/test/foundry/new/FuzzGenerators.t.sol b/test/foundry/new/FuzzGenerators.t.sol
new file mode 100644
index 000000000..82ce643d8
--- /dev/null
+++ b/test/foundry/new/FuzzGenerators.t.sol
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import {
+ AdvancedOrdersSpace,
+ ConsiderationItemSpace,
+ OfferItemSpace,
+ OrderComponentsSpace
+} from "seaport-sol/StructSpace.sol";
+
+import { AdvancedOrder, ItemType } from "seaport-sol/SeaportStructs.sol";
+
+import {
+ Amount,
+ BasicOrderCategory,
+ BroadOrderType,
+ Caller,
+ ConduitChoice,
+ ContractOrderRebate,
+ Criteria,
+ EOASignature,
+ ExtraData,
+ FulfillmentRecipient,
+ Offerer,
+ Recipient,
+ SignatureMethod,
+ Time,
+ Tips,
+ TokenIndex,
+ UnavailableReason,
+ Zone,
+ ZoneHash
+} from "seaport-sol/SpaceEnums.sol";
+
+import { BaseOrderTest } from "./BaseOrderTest.sol";
+
+import {
+ AdvancedOrdersSpaceGenerator,
+ FuzzGeneratorContext,
+ PRNGHelpers,
+ TestConduit
+} from "./helpers/FuzzGenerators.sol";
+
+import { TestHelpers } from "./helpers/FuzzTestContextLib.sol";
+
+import { EIP1271Offerer } from "../new/helpers/EIP1271Offerer.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../contracts/test/HashValidationZoneOfferer.sol";
+
+import {
+ HashCalldataContractOfferer
+} from "../../../contracts/test/HashCalldataContractOfferer.sol";
+
+import {
+ FulfillmentGeneratorLib
+} from "seaport-sol/fulfillments/lib/FulfillmentLib.sol";
+
+contract FuzzGeneratorsTest is BaseOrderTest {
+ using LibPRNG for LibPRNG.PRNG;
+ using PRNGHelpers for FuzzGeneratorContext;
+
+ /// @dev Note: the FuzzGeneratorContext must be a struct in *memory* in order
+ /// for the PRNG to work properly, so we can't declare it as a storage
+ /// variable in setUp. Instead, use this function to create a context.
+ function createContext() internal returns (FuzzGeneratorContext memory) {
+ LibPRNG.PRNG memory prng = LibPRNG.PRNG({ state: 0 });
+
+ uint256[] memory potential1155TokenIds = new uint256[](3);
+ potential1155TokenIds[0] = 1;
+ potential1155TokenIds[1] = 2;
+ potential1155TokenIds[2] = 3;
+
+ return
+ FuzzGeneratorContext({
+ vm: vm,
+ testHelpers: TestHelpers(address(this)),
+ prng: prng,
+ timestamp: block.timestamp,
+ seaport: getSeaport(),
+ conduitController: getConduitController(),
+ validatorZone: new HashValidationZoneOfferer(address(0)),
+ contractOfferer: new HashCalldataContractOfferer(address(0)),
+ eip1271Offerer: new EIP1271Offerer(),
+ erc20s: erc20s,
+ erc721s: erc721s,
+ erc1155s: erc1155s,
+ self: address(this),
+ caller: address(this),
+ alice: makeAccount("alice"),
+ bob: makeAccount("bob"),
+ carol: makeAccount("carol"),
+ dillon: makeAccount("dillon"),
+ eve: makeAccount("eve"),
+ frank: makeAccount("frank"),
+ starting721offerIndex: 1,
+ starting721considerationIndex: 1,
+ potential1155TokenIds: potential1155TokenIds,
+ conduits: new TestConduit[](2),
+ basicOrderCategory: BasicOrderCategory.NONE,
+ basicOfferSpace: OfferItemSpace(
+ ItemType.NATIVE,
+ TokenIndex.ONE,
+ Criteria.MERKLE,
+ Amount.FIXED
+ ),
+ counter: 0,
+ contractOffererNonce: 0
+ });
+ }
+
+ // NOTE: empty order space is not supported for now
+ function xtest_emptySpace() public {
+ FuzzGeneratorContext memory context = createContext();
+ AdvancedOrdersSpace memory space = AdvancedOrdersSpace({
+ orders: new OrderComponentsSpace[](0),
+ isMatchable: false,
+ maximumFulfilled: 0,
+ recipient: FulfillmentRecipient.ZERO,
+ conduit: ConduitChoice.NONE,
+ caller: Caller.TEST_CONTRACT,
+ strategy: FulfillmentGeneratorLib.getDefaultFulfillmentStrategy()
+ });
+ AdvancedOrder[] memory orders = AdvancedOrdersSpaceGenerator.generate(
+ space,
+ context
+ );
+ assertEq(orders.length, 0);
+ }
+
+ function test_emptyOfferConsideration() public {
+ FuzzGeneratorContext memory context = createContext();
+ OfferItemSpace[] memory offer = new OfferItemSpace[](0);
+ ConsiderationItemSpace[]
+ memory consideration = new ConsiderationItemSpace[](0);
+
+ OrderComponentsSpace memory component = OrderComponentsSpace({
+ offerer: Offerer.ALICE,
+ zone: Zone.NONE,
+ offer: offer,
+ consideration: consideration,
+ orderType: BroadOrderType.FULL,
+ time: Time.ONGOING,
+ zoneHash: ZoneHash.NONE,
+ signatureMethod: SignatureMethod.EOA,
+ eoaSignatureType: EOASignature.STANDARD,
+ bulkSigHeight: 0,
+ bulkSigIndex: 0,
+ conduit: ConduitChoice.NONE,
+ tips: Tips.NONE,
+ unavailableReason: UnavailableReason.AVAILABLE,
+ extraData: ExtraData.NONE,
+ rebate: ContractOrderRebate.NONE
+ });
+
+ OrderComponentsSpace[] memory components = new OrderComponentsSpace[](
+ 1
+ );
+ components[0] = component;
+
+ AdvancedOrdersSpace memory space = AdvancedOrdersSpace({
+ orders: components,
+ isMatchable: false,
+ maximumFulfilled: 1,
+ recipient: FulfillmentRecipient.ZERO,
+ conduit: ConduitChoice.NONE,
+ caller: Caller.TEST_CONTRACT,
+ strategy: FulfillmentGeneratorLib.getDefaultFulfillmentStrategy()
+ });
+ AdvancedOrder[] memory orders = AdvancedOrdersSpaceGenerator.generate(
+ space,
+ context
+ );
+ assertEq(orders.length, 1);
+ assertEq(orders[0].parameters.offer.length, 0);
+ // Empty order groups have a consideration item inserted on some order
+ assertEq(orders[0].parameters.consideration.length, 1);
+ }
+
+ function test_singleOffer_emptyConsideration() public {
+ FuzzGeneratorContext memory context = createContext();
+ OfferItemSpace[] memory offer = new OfferItemSpace[](1);
+ offer[0] = OfferItemSpace({
+ itemType: ItemType.ERC20,
+ tokenIndex: TokenIndex.ONE,
+ criteria: Criteria.MERKLE,
+ amount: Amount.FIXED
+ });
+ ConsiderationItemSpace[]
+ memory consideration = new ConsiderationItemSpace[](0);
+
+ OrderComponentsSpace memory component = OrderComponentsSpace({
+ offerer: Offerer.ALICE,
+ zone: Zone.NONE,
+ offer: offer,
+ consideration: consideration,
+ orderType: BroadOrderType.FULL,
+ time: Time.ONGOING,
+ zoneHash: ZoneHash.NONE,
+ signatureMethod: SignatureMethod.EOA,
+ eoaSignatureType: EOASignature.STANDARD,
+ bulkSigHeight: 0,
+ bulkSigIndex: 0,
+ conduit: ConduitChoice.NONE,
+ tips: Tips.NONE,
+ unavailableReason: UnavailableReason.AVAILABLE,
+ extraData: ExtraData.NONE,
+ rebate: ContractOrderRebate.NONE
+ });
+
+ OrderComponentsSpace[] memory components = new OrderComponentsSpace[](
+ 1
+ );
+ components[0] = component;
+
+ AdvancedOrdersSpace memory space = AdvancedOrdersSpace({
+ orders: components,
+ isMatchable: false,
+ maximumFulfilled: 1,
+ recipient: FulfillmentRecipient.ZERO,
+ conduit: ConduitChoice.NONE,
+ caller: Caller.TEST_CONTRACT,
+ strategy: FulfillmentGeneratorLib.getDefaultFulfillmentStrategy()
+ });
+ AdvancedOrder[] memory orders = AdvancedOrdersSpaceGenerator.generate(
+ space,
+ context
+ );
+ assertEq(orders.length, 1);
+ assertEq(orders[0].parameters.offer.length, 1);
+
+ assertEq(orders[0].parameters.offer[0].itemType, ItemType.ERC20);
+ assertEq(orders[0].parameters.offer[0].token, address(erc20s[0]));
+ assertGt(orders[0].parameters.offer[0].startAmount, 0);
+
+ assertEq(
+ orders[0].parameters.offer[0].startAmount,
+ orders[0].parameters.offer[0].endAmount
+ );
+
+ // Empty order groups have a consideration item inserted on some order
+ assertEq(orders[0].parameters.consideration.length, 1);
+ }
+
+ function test_emptyOffer_singleConsideration() public {
+ FuzzGeneratorContext memory context = createContext();
+ OfferItemSpace[] memory offer = new OfferItemSpace[](0);
+ ConsiderationItemSpace[]
+ memory consideration = new ConsiderationItemSpace[](1);
+ consideration[0] = ConsiderationItemSpace({
+ itemType: ItemType.ERC20,
+ tokenIndex: TokenIndex.ONE,
+ criteria: Criteria.MERKLE,
+ amount: Amount.ASCENDING,
+ recipient: Recipient.OFFERER
+ });
+
+ OrderComponentsSpace memory component = OrderComponentsSpace({
+ offerer: Offerer.ALICE,
+ zone: Zone.NONE,
+ offer: offer,
+ consideration: consideration,
+ orderType: BroadOrderType.FULL,
+ time: Time.ONGOING,
+ zoneHash: ZoneHash.NONE,
+ signatureMethod: SignatureMethod.EOA,
+ eoaSignatureType: EOASignature.STANDARD,
+ bulkSigHeight: 0,
+ bulkSigIndex: 0,
+ conduit: ConduitChoice.NONE,
+ tips: Tips.NONE,
+ unavailableReason: UnavailableReason.AVAILABLE,
+ extraData: ExtraData.NONE,
+ rebate: ContractOrderRebate.NONE
+ });
+
+ OrderComponentsSpace[] memory components = new OrderComponentsSpace[](
+ 1
+ );
+ components[0] = component;
+
+ AdvancedOrdersSpace memory space = AdvancedOrdersSpace({
+ orders: components,
+ isMatchable: false,
+ maximumFulfilled: 1,
+ recipient: FulfillmentRecipient.ZERO,
+ conduit: ConduitChoice.NONE,
+ caller: Caller.TEST_CONTRACT,
+ strategy: FulfillmentGeneratorLib.getDefaultFulfillmentStrategy()
+ });
+ AdvancedOrder[] memory orders = AdvancedOrdersSpaceGenerator.generate(
+ space,
+ context
+ );
+ assertEq(orders.length, 1);
+ assertEq(orders[0].parameters.offer.length, 0);
+ assertEq(orders[0].parameters.consideration.length, 1);
+
+ assertGt(orders[0].parameters.consideration[0].startAmount, 0);
+ assertGt(
+ orders[0].parameters.consideration[0].endAmount,
+ orders[0].parameters.consideration[0].startAmount
+ );
+ assertEq(
+ orders[0].parameters.consideration[0].recipient,
+ orders[0].parameters.offerer
+ );
+
+ assertEq(
+ orders[0].parameters.consideration[0].itemType,
+ ItemType.ERC20
+ );
+ }
+
+ function assertEq(ItemType a, ItemType b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+
+ function assertEq(ItemType a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(Offerer a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(Zone a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(BroadOrderType a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(Time a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(ZoneHash a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(TokenIndex a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(Criteria a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(Amount a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+
+ function assertEq(Recipient a, uint8 b) internal {
+ assertEq(uint8(a), b);
+ }
+}
diff --git a/test/foundry/new/FuzzHelpers.t.sol b/test/foundry/new/FuzzHelpers.t.sol
new file mode 100644
index 000000000..d5288f091
--- /dev/null
+++ b/test/foundry/new/FuzzHelpers.t.sol
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ FulfillmentComponentLib,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ ConsiderationItem,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import {
+ BasicOrderType,
+ ItemType,
+ OrderType
+} from "seaport-sol/SeaportEnums.sol";
+
+import { BaseOrderTest } from "./BaseOrderTest.sol";
+
+import {
+ AdvancedOrder,
+ Family,
+ FuzzHelpers,
+ State,
+ Structure,
+ Type
+} from "./helpers/FuzzHelpers.sol";
+
+contract FuzzHelpersTest is BaseOrderTest {
+ using AdvancedOrderLib for AdvancedOrder;
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using FulfillmentComponentLib for FulfillmentComponent[];
+ using FulfillmentLib for Fulfillment;
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for AdvancedOrder[];
+
+ function setUp() public virtual override {
+ super.setUp();
+
+ OrderParameters memory standardOrderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters();
+
+ OrderLib.empty().withParameters(standardOrderParameters).saveDefault(
+ STANDARD
+ );
+ }
+
+ /// @dev An order with no advanced order parameters is STANDARD
+ function test_getStructure_Standard() public {
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.STANDARD);
+ }
+
+ /// @dev An order with no advanced order parameters that meets various
+ /// criteria is BASIC.
+ function test_getStructure_Basic() public {
+ erc721s[0].mint(offerer1.addr, 1);
+
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+
+ offerItems[0] = offerItem;
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 1
+ );
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(1)
+ .withRecipient(offerer1.addr);
+
+ considerationItems[0] = considerationItem;
+
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems);
+
+ Order memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(
+ orderComponents.toOrderParameters().withOrderType(
+ OrderType.FULL_OPEN
+ )
+ )
+ .withSignature("");
+
+ AdvancedOrder memory advancedOrder = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(
+ advancedOrder.getStructure(address(getSeaport())),
+ Structure.BASIC
+ );
+ }
+
+ /// @dev An order with numerator, denominator, or extraData is ADVANCED
+ function test_getStructure_Advanced(
+ uint120 numerator,
+ uint120 denominator,
+ bytes memory extraData
+ ) public {
+ vm.assume(numerator != 0);
+ vm.assume(denominator != 0);
+ vm.assume(extraData.length != 0);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .toAdvancedOrder({
+ numerator: numerator,
+ denominator: denominator,
+ extraData: extraData
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.ADVANCED);
+ }
+
+ /// @dev A non-contract order with offer item criteria is ADVANCED
+ function test_getStructure_Advanced_OfferERC721Criteria() public {
+ OfferItem[] memory offer = new OfferItem[](1);
+ offer[0] = OfferItemLib.empty().withItemType(
+ ItemType.ERC721_WITH_CRITERIA
+ );
+
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOffer(offer);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.ADVANCED);
+ }
+
+ /// @dev A non-contract order with offer item criteria is ADVANCED
+ function test_getStructure_Advanced_OfferERC1155Criteria() public {
+ OfferItem[] memory offer = new OfferItem[](1);
+ offer[0] = OfferItemLib.empty().withItemType(
+ ItemType.ERC1155_WITH_CRITERIA
+ );
+
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOffer(offer);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.ADVANCED);
+ }
+
+ /// @dev A non-contract order with consideration item criteria is ADVANCED
+ function test_getStructure_Advanced_ConsiderationERC721Criteria() public {
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](1);
+ consideration[0] = ConsiderationItemLib.empty().withItemType(
+ ItemType.ERC721_WITH_CRITERIA
+ );
+
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withConsideration(consideration);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.ADVANCED);
+ }
+
+ /// @dev A non-contract order with consideration item criteria is ADVANCED
+ function test_getStructure_Advanced_ConsiderationERC1155Criteria() public {
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](1);
+ consideration[0] = ConsiderationItemLib.empty().withItemType(
+ ItemType.ERC1155_WITH_CRITERIA
+ );
+
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withConsideration(consideration);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.ADVANCED);
+ }
+
+ /// @dev A contract order with consideration item criteria is STANDARD if
+ /// identifierOrCriteria == 0 for all items
+ function test_getStructure_Standard_ConsiderationCriteria_ContractOrder()
+ public
+ {
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](1);
+ consideration[0] = ConsiderationItemLib.empty().withItemType(
+ ItemType.ERC721_WITH_CRITERIA
+ );
+
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withConsideration(consideration)
+ .withOrderType(OrderType.CONTRACT);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.STANDARD);
+ }
+
+ /// @dev A contract order with consideration item criteria is ADVANCED if
+ /// identifierOrCriteria != 0 for any item
+ function test_getStructure_Advanced_ConsiderationCriteria_ContractOrder()
+ public
+ {
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](1);
+ consideration[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721_WITH_CRITERIA)
+ .withIdentifierOrCriteria(1);
+
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withConsideration(consideration)
+ .withOrderType(OrderType.CONTRACT);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getStructure(address(getSeaport())), Structure.ADVANCED);
+ }
+
+ /// @dev An order with type FULL_OPEN is OPEN
+ function test_getType_FullOpen() public {
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOrderType(OrderType.FULL_OPEN);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getType(), Type.OPEN);
+ }
+
+ /// @dev An order with type PARTIAL_OPEN is OPEN
+ function test_getType_PartialOpen() public {
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOrderType(OrderType.PARTIAL_OPEN);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getType(), Type.OPEN);
+ }
+
+ /// @dev An order with type FULL_RESTRICTED is RESTRICTED
+ function test_getType_FullRestricted() public {
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOrderType(OrderType.FULL_RESTRICTED);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getType(), Type.RESTRICTED);
+ }
+
+ /// @dev An order with type PARTIAL_RESTRICTED is RESTRICTED
+ function test_getType_PartialRestricted() public {
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOrderType(OrderType.PARTIAL_RESTRICTED);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getType(), Type.RESTRICTED);
+ }
+
+ /// @dev An order with type CONTRACT is CONTRACT
+ function test_getType_Contract() public {
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOrderType(OrderType.CONTRACT);
+
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getType(), Type.CONTRACT);
+ }
+
+ /// @dev A validated order is in state VALIDATED
+ function test_getState_ValidatedOrder() public {
+ uint256 counter = getSeaport().getCounter(offerer1.addr);
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withCounter(counter)
+ .withOrderType(OrderType.FULL_OPEN)
+ .toOrderParameters();
+ bytes32 orderHash = getSeaport().getOrderHash(
+ orderParameters.toOrderComponents(counter)
+ );
+
+ Order[] memory orders = new Order[](1);
+ orders[0] = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .withSignature(signOrder(getSeaport(), offerer1.key, orderHash));
+
+ assertEq(getSeaport().validate(orders), true);
+
+ AdvancedOrder memory order = orders[0].toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getState(getSeaport()), State.VALIDATED);
+ }
+
+ /// @dev A cancelled order is in state CANCELLED
+ function test_getState_CancelledOrder() public {
+ uint256 counter = getSeaport().getCounter(offerer1.addr);
+ OrderParameters memory orderParameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withCounter(counter)
+ .withOrderType(OrderType.FULL_OPEN)
+ .toOrderParameters();
+ bytes32 orderHash = getSeaport().getOrderHash(
+ orderParameters.toOrderComponents(counter)
+ );
+
+ Order[] memory orders = new Order[](1);
+ orders[0] = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderParameters)
+ .withSignature(signOrder(getSeaport(), offerer1.key, orderHash));
+
+ OrderComponents[] memory orderComponents = new OrderComponents[](1);
+ orderComponents[0] = orderParameters.toOrderComponents(counter);
+
+ assertEq(getSeaport().validate(orders), true);
+
+ vm.prank(offerer1.addr);
+ assertEq(getSeaport().cancel(orderComponents), true);
+
+ AdvancedOrder memory order = orders[0].toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getState(getSeaport()), State.CANCELLED);
+ }
+
+ /// @dev A new order is in state UNUSED
+ function test_getState_NewOrder() public {
+ AdvancedOrder memory order = OrderLib
+ .fromDefault(STANDARD)
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(order.getState(getSeaport()), State.UNUSED);
+ }
+
+ /// @dev An order[] quantity is its length
+ function test_getQuantity(uint8 n) public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](n);
+
+ for (uint256 i; i < n; ++i) {
+ orders[i] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ }
+
+ assertEq(orders.getQuantity(), n);
+ }
+
+ /// @dev An order[] of quantity 1 uses a SINGLE family method
+ function test_getFamily_Single() public {
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+
+ orders[0] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ assertEq(orders.getFamily(), Family.SINGLE);
+ }
+
+ /// @dev An order[] of quantity > 1 uses a COMBINED family method
+ function test_getFamily_Combined(uint8 n) public {
+ vm.assume(n > 1);
+ AdvancedOrder[] memory orders = new AdvancedOrder[](n);
+
+ for (uint256 i; i < n; ++i) {
+ orders[i] = OrderLib.fromDefault(STANDARD).toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ }
+
+ assertEq(orders.getFamily(), Family.COMBINED);
+ }
+
+ function _createOrder(
+ ItemType offerItemType,
+ ItemType considerationItemType,
+ OrderType orderType
+ ) internal view returns (AdvancedOrder memory order) {
+ bool nftOffered = offerItemType == ItemType.ERC721 ||
+ offerItemType == ItemType.ERC1155;
+
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(offerItemType)
+ .withToken(nftOffered ? address(erc721s[0]) : address(0))
+ .withIdentifierOrCriteria(nftOffered ? 1 : 0)
+ .withAmount(1);
+
+ offerItems[0] = offerItem;
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 1
+ );
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withItemType(considerationItemType)
+ .withIdentifierOrCriteria(nftOffered ? 0 : 1)
+ .withAmount(1);
+
+ considerationItems[0] = considerationItem;
+
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems)
+ .withOrderType(orderType);
+
+ order = OrderLib
+ .fromDefault(STANDARD)
+ .withParameters(orderComponents.toOrderParameters())
+ .toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC721
+ * item. No partial fills, anyone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC721_FULL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.NATIVE,
+ OrderType.FULL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC721_FULL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC721
+ * item. Partial fills supported, anyone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC721_PARTIAL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.NATIVE,
+ OrderType.PARTIAL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC721_PARTIAL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC721
+ * item. No partial fills, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC721_FULL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.NATIVE,
+ OrderType.FULL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC721_FULL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC721
+ * item. Partial fills supported, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC721_PARTIAL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.NATIVE,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC721_PARTIAL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC1155
+ * item. No partial fills, anyone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC1155_FULL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.NATIVE,
+ OrderType.FULL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC1155_FULL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC1155
+ * item. Partial fills supported, anyone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC1155_PARTIAL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.NATIVE,
+ OrderType.PARTIAL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC1155_PARTIAL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC1155
+ * item. No partial fills, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC1155_FULL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.NATIVE,
+ OrderType.FULL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC1155_FULL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide Ether (or other native token) to receive offered ERC1155
+ * item. Partial fills supported, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ETH_TO_ERC1155_PARTIAL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.NATIVE,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ETH_TO_ERC1155_PARTIAL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC721 item. No partial fills,
+ * anyone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC721_FULL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.ERC20,
+ OrderType.FULL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC721_FULL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC721 item. Partial fills
+ * supported, anyone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC721_PARTIAL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.ERC20,
+ OrderType.PARTIAL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC721_PARTIAL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC721 item. No partial fills,
+ * only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC721_FULL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.ERC20,
+ OrderType.FULL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC721_FULL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC721 item. Partial fills
+ * supported, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC721_PARTIAL_RESTRICTED()
+ public
+ {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC721,
+ ItemType.ERC20,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC721_PARTIAL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC1155 item. no partial
+ * fills, anyone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC1155_FULL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.ERC20,
+ OrderType.FULL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC1155_FULL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC1155 item. Partial fills
+ * supported, anyone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC1155_PARTIAL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.ERC20,
+ OrderType.PARTIAL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC1155_PARTIAL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC1155 item. no partial
+ * fills, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC1155_FULL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.ERC20,
+ OrderType.FULL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC1155_FULL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC20 item to receive offered ERC1155 item. Partial fills
+ * supported, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC20_TO_ERC1155_PARTIAL_RESTRICTED()
+ public
+ {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC1155,
+ ItemType.ERC20,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC20_TO_ERC1155_PARTIAL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC721 item to receive offered ERC20 item. No partial fills,
+ * anyone can execute.
+ */
+ function test_getBasicOrderType_ERC721_TO_ERC20_FULL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC721,
+ OrderType.FULL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC721_TO_ERC20_FULL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC721 item to receive offered ERC20 item. Partial fills
+ * supported, anyone can execute.
+ */
+ function test_getBasicOrderType_ERC721_TO_ERC20_PARTIAL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC721,
+ OrderType.PARTIAL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC721_TO_ERC20_PARTIAL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC721 item to receive offered ERC20 item. No partial fills,
+ * only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC721_TO_ERC20_FULL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC721,
+ OrderType.FULL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC721_TO_ERC20_FULL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC721 item to receive offered ERC20 item. Partial fills
+ * supported, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC721_TO_ERC20_PARTIAL_RESTRICTED()
+ public
+ {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC721,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC721_TO_ERC20_PARTIAL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC1155 item to receive offered ERC20 item. no partial
+ * fills, anyone can execute.
+ */
+ function test_getBasicOrderType_ERC1155_TO_ERC20_FULL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.FULL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC1155_TO_ERC20_FULL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC1155 item to receive offered ERC20 item. Partial fills
+ * supported, anyone can execute.
+ */
+ function test_getBasicOrderType_ERC1155_TO_ERC20_PARTIAL_OPEN() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_OPEN
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC1155_TO_ERC20_PARTIAL_OPEN
+ );
+ }
+
+ /**
+ * @dev Provide ERC1155 item to receive offered ERC20 item. no partial
+ * fills, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC1155_TO_ERC20_FULL_RESTRICTED() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.FULL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC1155_TO_ERC20_FULL_RESTRICTED
+ );
+ }
+
+ /**
+ * @dev Provide ERC1155 item to receive offered ERC20 item. Partial fills
+ * supported, only offerer or zone can execute.
+ */
+ function test_getBasicOrderType_ERC1155_TO_ERC20_PARTIAL_RESTRICTED()
+ public
+ {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.getBasicOrderTypeEligibility(address(getSeaport()));
+ assertEq(
+ order.getBasicOrderType(),
+ BasicOrderType.ERC1155_TO_ERC20_PARTIAL_RESTRICTED
+ );
+ }
+
+ function test_getBasicOrderTypeEligibility_failure_criteria() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.parameters.consideration[0].itemType = ItemType
+ .ERC1155_WITH_CRITERIA;
+
+ assertFalse(order.getBasicOrderTypeEligibility(address(getSeaport())));
+ }
+
+ function test_getBasicOrderTypeEligibility_failure_extraData() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.extraData = bytes("extraData");
+
+ assertFalse(order.getBasicOrderTypeEligibility(address(getSeaport())));
+ }
+
+ function test_getBasicOrderTypeEligibility_failure_offerItemLength()
+ public
+ {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ OfferItem[] memory offer = new OfferItem[](2);
+
+ offer[0] = order.parameters.offer[0];
+ offer[1] = order.parameters.offer[0];
+
+ order.parameters.offer = offer;
+
+ assertFalse(order.getBasicOrderTypeEligibility(address(getSeaport())));
+
+ order.parameters.offer = new OfferItem[](0);
+
+ assertFalse(order.getBasicOrderTypeEligibility(address(getSeaport())));
+ }
+
+ function test_getBasicOrderTypeEligibility_failure_considerationItemLength()
+ public
+ {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ order.parameters.consideration = new ConsiderationItem[](0);
+
+ assertFalse(order.getBasicOrderTypeEligibility(address(getSeaport())));
+ }
+
+ function test_getBasicOrderTypeEligibility_failure_nftCount() public {
+ AdvancedOrder memory order = _createOrder(
+ ItemType.ERC20,
+ ItemType.ERC1155,
+ OrderType.PARTIAL_RESTRICTED
+ );
+
+ OfferItem[] memory offer = new OfferItem[](1);
+ offer[0].itemType = ItemType.ERC721;
+
+ order.parameters.offer = offer;
+
+ assertFalse(order.getBasicOrderTypeEligibility(address(getSeaport())));
+ }
+
+ function assertEq(State a, State b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+
+ function assertEq(Family a, Family b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+
+ function assertEq(Structure a, Structure b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+
+ function assertEq(Type a, Type b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+
+ function assertEq(BasicOrderType a, BasicOrderType b) internal {
+ assertEq(uint8(a), uint8(b));
+ }
+}
diff --git a/test/foundry/new/FuzzInscribers.t.sol b/test/foundry/new/FuzzInscribers.t.sol
new file mode 100644
index 000000000..685fafcbe
--- /dev/null
+++ b/test/foundry/new/FuzzInscribers.t.sol
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Vm } from "forge-std/Vm.sol";
+
+import {
+ ConsiderationItemLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ OfferItem,
+ OrderComponents
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import { BaseOrderTest } from "./BaseOrderTest.sol";
+
+import { FuzzHelpers } from "./helpers/FuzzHelpers.sol";
+
+import { FuzzInscribers } from "./helpers/FuzzInscribers.sol";
+
+import {
+ FuzzTestContext,
+ FuzzTestContextLib
+} from "./helpers/FuzzTestContextLib.sol";
+
+contract FuzzHelpersTest is BaseOrderTest {
+ using ConsiderationItemLib for ConsiderationItem;
+ using OfferItemLib for OfferItem;
+ using OrderComponentsLib for OrderComponents;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzInscribers for AdvancedOrder;
+
+ struct RawStorageValues {
+ bytes32 rawOrganicOrderStatusBeforeCalls;
+ bytes32 rawOrganicOrderStatusAfterValidation;
+ bytes32 rawOrganicOrderStatusAfterPartialFulfillment;
+ bytes32 rawOrganicOrderStatusAfterFullFulfillment;
+ bytes32 rawOrganicOrderStatusAfterCancellation;
+ bytes32 rawSyntheticOrderStatusBeforeCalls;
+ bytes32 rawSyntheticOrderStatusAfterValidation;
+ bytes32 rawSyntheticOrderStatusAfterPartialFulfillment;
+ bytes32 rawSyntheticOrderStatusAfterFullFulfillment;
+ bytes32 rawSyntheticOrderStatusAfterCancellation;
+ }
+
+ function test_inscribeOrderStatus() public {
+ RawStorageValues memory rawStorageValues = RawStorageValues({
+ rawOrganicOrderStatusBeforeCalls: 0,
+ rawOrganicOrderStatusAfterValidation: 0,
+ rawOrganicOrderStatusAfterPartialFulfillment: 0,
+ rawOrganicOrderStatusAfterFullFulfillment: 0,
+ rawOrganicOrderStatusAfterCancellation: 0,
+ rawSyntheticOrderStatusBeforeCalls: 0,
+ rawSyntheticOrderStatusAfterValidation: 0,
+ rawSyntheticOrderStatusAfterPartialFulfillment: 0,
+ rawSyntheticOrderStatusAfterFullFulfillment: 0,
+ rawSyntheticOrderStatusAfterCancellation: 0
+ });
+
+ (
+ bytes32 orderHash,
+ FuzzTestContext memory context,
+ AdvancedOrder memory advancedOrder,
+ OrderComponents[] memory orderComponentsArray
+ ) = _setUpOrderAndContext();
+
+ _setRawOrganicStorageValues(
+ orderHash,
+ context,
+ advancedOrder,
+ orderComponentsArray,
+ rawStorageValues
+ );
+
+ // Wipe the slot.
+ advancedOrder.inscribeOrderStatusDenominator(0, context.seaport);
+ advancedOrder.inscribeOrderStatusNumerator(0, context.seaport);
+ advancedOrder.inscribeOrderStatusCancelled(false, context.seaport);
+ advancedOrder.inscribeOrderStatusValidated(false, context.seaport);
+
+ // Populate the raw synthetic storage values. These are the storage
+ // values produced by using the inscription helpers.
+ _setRawSyntheticStorageValues(
+ orderHash,
+ context,
+ advancedOrder,
+ rawStorageValues
+ );
+
+ _compareOrganicAndSyntheticRawStorageValues(rawStorageValues);
+ }
+
+ function test_inscribeContractOffererNonce() public {
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](0);
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: advancedOrders,
+ seaport: seaport,
+ caller: address(this)
+ });
+
+ bytes32 contractNonceStorageSlot = _getStorageSlotForContractNonce(
+ address(this),
+ context
+ );
+ bytes32 rawContractOffererNonceValue = vm.load(
+ address(context.seaport),
+ contractNonceStorageSlot
+ );
+
+ assertEq(rawContractOffererNonceValue, bytes32(0));
+
+ FuzzInscribers.inscribeContractOffererNonce(
+ address(this),
+ 1,
+ context.seaport
+ );
+
+ bytes32 newContractOffererNonceValue = vm.load(
+ address(context.seaport),
+ contractNonceStorageSlot
+ );
+
+ assertEq(newContractOffererNonceValue, bytes32(uint256(1)));
+ }
+
+ function test_inscribeCounter() public {
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: new AdvancedOrder[](0),
+ seaport: seaport,
+ caller: address(this)
+ });
+
+ bytes32 counterStorageSlot = _getStorageSlotForCounter(
+ address(this),
+ context
+ );
+ bytes32 rawCounterValue = vm.load(
+ address(context.seaport),
+ counterStorageSlot
+ );
+
+ assertEq(rawCounterValue, bytes32(0));
+
+ FuzzInscribers.inscribeCounter(address(this), 1, context.seaport);
+
+ bytes32 newCounterValue = vm.load(
+ address(context.seaport),
+ counterStorageSlot
+ );
+
+ assertEq(newCounterValue, bytes32(uint256(1)));
+ }
+
+ function _setUpOrderAndContext()
+ internal
+ returns (
+ bytes32 _orderHash,
+ FuzzTestContext memory _context,
+ AdvancedOrder memory _advancedOrder,
+ OrderComponents[] memory _orderComponentsArray
+ )
+ {
+ // Set up the order.
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(10e34);
+
+ offerItems[0] = offerItem;
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 1
+ );
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.NATIVE)
+ .withAmount(10e34);
+
+ considerationItems[0] = considerationItem;
+
+ OrderComponents memory orderComponents = OrderComponentsLib
+ .empty()
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 100)
+ .withOrderType(OrderType.PARTIAL_OPEN)
+ .withOfferer(offerer1.addr)
+ .withOffer(offerItems)
+ .withConsideration(considerationItems);
+
+ // Set this up to use later for canceling.
+ OrderComponents[] memory orderComponentsArray = new OrderComponents[](
+ 1
+ );
+
+ orderComponentsArray[0] = orderComponents;
+
+ bytes memory signature = signOrder(
+ getSeaport(),
+ offerer1.key,
+ getSeaport().getOrderHash(orderComponents)
+ );
+
+ AdvancedOrder memory advancedOrder = AdvancedOrder({
+ parameters: orderComponents.toOrderParameters(),
+ signature: signature,
+ numerator: 10e34 / 2,
+ denominator: 10e34,
+ extraData: bytes("")
+ });
+
+ AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](1);
+ advancedOrders[0] = advancedOrder;
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: advancedOrders,
+ seaport: seaport,
+ caller: address(this)
+ });
+
+ bytes32 orderHash = context.seaport.getOrderHash(orderComponents);
+
+ return (orderHash, context, advancedOrder, orderComponentsArray);
+ }
+
+ function _setRawOrganicStorageValues(
+ bytes32 orderHash,
+ FuzzTestContext memory context,
+ AdvancedOrder memory advancedOrder,
+ OrderComponents[] memory orderComponentsArray,
+ RawStorageValues memory rawStorageValues
+ ) internal {
+ // Populate the raw organic storage values. These are the storage
+ // values produced by actualy calling Seaport.
+ bytes32 orderHashStorageSlot = _getStorageSlotForOrderHash(
+ orderHash,
+ context
+ );
+ rawStorageValues.rawOrganicOrderStatusBeforeCalls = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ advancedOrder.validateTipNeutralizedOrder(context);
+
+ rawStorageValues.rawOrganicOrderStatusAfterValidation = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ context.seaport.fulfillAdvancedOrder{ value: 10e34 / 2 }({
+ advancedOrder: advancedOrder,
+ criteriaResolvers: new CriteriaResolver[](0),
+ fulfillerConduitKey: bytes32(0),
+ recipient: address(this)
+ });
+
+ rawStorageValues.rawOrganicOrderStatusAfterPartialFulfillment = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ context.seaport.fulfillAdvancedOrder{ value: 10e34 / 2 }({
+ advancedOrder: advancedOrder,
+ criteriaResolvers: new CriteriaResolver[](0),
+ fulfillerConduitKey: bytes32(0),
+ recipient: address(this)
+ });
+
+ rawStorageValues.rawOrganicOrderStatusAfterFullFulfillment = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ vm.prank(address(offerer1.addr));
+ context.seaport.cancel(orderComponentsArray);
+
+ rawStorageValues.rawOrganicOrderStatusAfterCancellation = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+ }
+
+ function _setRawSyntheticStorageValues(
+ bytes32 orderHash,
+ FuzzTestContext memory context,
+ AdvancedOrder memory advancedOrder,
+ RawStorageValues memory rawStorageValues
+ ) internal {
+ // Populate the raw organic storage values. These are the storage
+ // values produced by actualy calling Seaport.
+ bytes32 orderHashStorageSlot = _getStorageSlotForOrderHash(
+ orderHash,
+ context
+ );
+ rawStorageValues.rawSyntheticOrderStatusBeforeCalls = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ advancedOrder.inscribeOrderStatusValidated(true, context.seaport);
+
+ rawStorageValues.rawSyntheticOrderStatusAfterValidation = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ advancedOrder.inscribeOrderStatusNumerator(10e34 / 2, context.seaport);
+ advancedOrder.inscribeOrderStatusDenominator(10e34, context.seaport);
+
+ rawStorageValues.rawSyntheticOrderStatusAfterPartialFulfillment = vm
+ .load(address(context.seaport), orderHashStorageSlot);
+
+ advancedOrder.inscribeOrderStatusNumerator(10e34, context.seaport);
+ advancedOrder.inscribeOrderStatusDenominator(10e34, context.seaport);
+
+ rawStorageValues.rawSyntheticOrderStatusAfterFullFulfillment = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+
+ advancedOrder.inscribeOrderStatusCancelled(true, context.seaport);
+
+ rawStorageValues.rawSyntheticOrderStatusAfterCancellation = vm.load(
+ address(context.seaport),
+ orderHashStorageSlot
+ );
+ }
+
+ function _compareOrganicAndSyntheticRawStorageValues(
+ RawStorageValues memory rawStorageValues
+ ) internal {
+ assertEq(rawStorageValues.rawOrganicOrderStatusBeforeCalls, 0);
+
+ assertEq(
+ rawStorageValues.rawOrganicOrderStatusBeforeCalls,
+ rawStorageValues.rawSyntheticOrderStatusBeforeCalls
+ );
+
+ assertEq(
+ rawStorageValues.rawOrganicOrderStatusAfterValidation,
+ rawStorageValues.rawSyntheticOrderStatusAfterValidation
+ );
+
+ assertEq(
+ rawStorageValues.rawOrganicOrderStatusAfterPartialFulfillment,
+ rawStorageValues.rawSyntheticOrderStatusAfterPartialFulfillment
+ );
+
+ assertEq(
+ rawStorageValues.rawOrganicOrderStatusAfterFullFulfillment,
+ rawStorageValues.rawSyntheticOrderStatusAfterFullFulfillment
+ );
+
+ assertEq(
+ rawStorageValues.rawOrganicOrderStatusAfterCancellation,
+ rawStorageValues.rawSyntheticOrderStatusAfterCancellation
+ );
+ }
+
+ function _getStorageSlotForOrderHash(
+ bytes32 orderHash,
+ FuzzTestContext memory context
+ ) internal returns (bytes32) {
+ vm.record();
+ context.seaport.getOrderStatus(orderHash);
+ (bytes32[] memory readAccesses, ) = vm.accesses(
+ address(context.seaport)
+ );
+
+ uint256 expectedReadAccessCount = 4;
+
+ string memory profile = vm.envOr(
+ "FOUNDRY_PROFILE",
+ string("optimized")
+ );
+
+ if (
+ keccak256(abi.encodePacked(profile)) ==
+ keccak256(abi.encodePacked("optimized")) ||
+ keccak256(abi.encodePacked(profile)) ==
+ keccak256(abi.encodePacked("test")) ||
+ keccak256(abi.encodePacked(profile)) ==
+ keccak256(abi.encodePacked("lite"))
+ ) {
+ expectedReadAccessCount = 1;
+ }
+
+ require(
+ readAccesses.length == expectedReadAccessCount,
+ "Expected a different number of read accesses."
+ );
+
+ return readAccesses[0];
+ }
+
+ function _getStorageSlotForContractNonce(
+ address contractOfferer,
+ FuzzTestContext memory context
+ ) private returns (bytes32) {
+ vm.record();
+ context.seaport.getContractOffererNonce(contractOfferer);
+ (bytes32[] memory readAccesses, ) = vm.accesses(
+ address(context.seaport)
+ );
+
+ require(readAccesses.length == 1, "Expected 1 read access.");
+
+ return readAccesses[0];
+ }
+
+ function _getStorageSlotForCounter(
+ address offerer,
+ FuzzTestContext memory context
+ ) private returns (bytes32) {
+ vm.record();
+ context.seaport.getCounter(offerer);
+ (bytes32[] memory readAccesses, ) = vm.accesses(
+ address(context.seaport)
+ );
+
+ require(readAccesses.length == 1, "Expected 1 read access.");
+
+ return readAccesses[0];
+ }
+}
diff --git a/test/foundry/new/FuzzMain.t.sol b/test/foundry/new/FuzzMain.t.sol
new file mode 100644
index 000000000..c61c3ee80
--- /dev/null
+++ b/test/foundry/new/FuzzMain.t.sol
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { FuzzEngine } from "./helpers/FuzzEngine.sol";
+
+import { FuzzParams } from "./helpers/FuzzTestContextLib.sol";
+
+contract FuzzMainTest is FuzzEngine {
+ /**
+ * @dev FuzzEngine entry point. Generates a random order configuration,
+ * selects and calls a Seaport method, and runs all registered checks.
+ * This test should never revert. For more details on the lifecycle of
+ * this test, see `FuzzEngine.sol`.
+ */
+ function test_fuzz_generateOrders(
+ uint256 seed,
+ uint256 orders,
+ uint256 maxOfferItemsPerOrder,
+ uint256 maxConsiderationItemsPerOrder
+ ) public {
+ run(
+ FuzzParams({
+ seed: seed,
+ totalOrders: bound(orders, 1, 10),
+ maxOfferItems: bound(maxOfferItemsPerOrder, 0, 10),
+ maxConsiderationItems: bound(
+ maxConsiderationItemsPerOrder,
+ 0,
+ 10
+ ),
+ seedInput: abi.encodePacked(
+ seed,
+ orders,
+ maxOfferItemsPerOrder,
+ maxConsiderationItemsPerOrder
+ )
+ })
+ );
+ }
+
+ /**
+ * @dev A helper to convert a fuzz test failure into a concrete test.
+ * Copy/paste fuzz run parameters into the tuple below and remove the
+ * leading "x" to run a fuzz failure as a concrete test.
+ */
+ function xtest_concrete() public {
+ (
+ uint256 seed,
+ uint256 orders,
+ uint256 maxOfferItemsPerOrder,
+ uint256 maxConsiderationItemsPerOrder
+ ) = (0, 0, 0, 0);
+ bytes memory callData = abi.encodeCall(
+ this.test_fuzz_generateOrders,
+ (seed, orders, maxOfferItemsPerOrder, maxConsiderationItemsPerOrder)
+ );
+ (bool success, bytes memory result) = address(this).call(callData);
+ if (!success) {
+ if (result.length == 0) revert();
+ assembly {
+ revert(add(0x20, result), mload(result))
+ }
+ }
+ }
+}
diff --git a/test/foundry/new/FuzzSetup.t.sol b/test/foundry/new/FuzzSetup.t.sol
new file mode 100644
index 000000000..276ebf7df
--- /dev/null
+++ b/test/foundry/new/FuzzSetup.t.sol
@@ -0,0 +1,624 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ FulfillmentComponentLib,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import { OrderComponentsSpace } from "seaport-sol/StructSpace.sol";
+
+import { OrderDetails } from "seaport-sol/fulfillments/lib/Structs.sol";
+
+import { OrderStatusEnum, UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import { Account, BaseOrderTest } from "./BaseOrderTest.sol";
+
+import {
+ FuzzParams,
+ FuzzTestContext,
+ FuzzTestContextLib
+} from "./helpers/FuzzTestContextLib.sol";
+
+import { FuzzEngineLib } from "./helpers/FuzzEngineLib.sol";
+
+import { FuzzSetup } from "./helpers/FuzzSetup.sol";
+
+import { FuzzDerivers } from "./helpers/FuzzDerivers.sol";
+
+contract FuzzSetupTest is BaseOrderTest, FuzzSetup {
+ using AdvancedOrderLib for AdvancedOrder;
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using FulfillmentComponentLib for FulfillmentComponent[];
+ using FulfillmentLib for Fulfillment;
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ using FuzzTestContextLib for FuzzTestContext;
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzDerivers for FuzzTestContext;
+
+ Account charlie = makeAccount("charlie");
+
+ function test_setUpOfferItems_erc20() public {
+ assertEq(erc20s[0].balanceOf(charlie.addr), 0);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 0);
+
+ OfferItem[] memory offerItems = new OfferItem[](2);
+ offerItems[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(100);
+
+ offerItems[1] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(100);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withOffer(offerItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpOfferItems(context);
+
+ assertEq(erc20s[0].balanceOf(charlie.addr), 200);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 200);
+ }
+
+ function test_setUpOfferItems_erc20_ascending() public {
+ assertEq(erc20s[0].balanceOf(charlie.addr), 0);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 0);
+
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ offerItems[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(500)
+ .withEndAmount(1000);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withOffer(offerItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1000);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ vm.warp(block.timestamp + 500);
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpOfferItems(context);
+
+ assertEq(erc20s[0].balanceOf(charlie.addr), 750);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 750);
+ }
+
+ function test_setUpOfferItems_erc20_descending() public {
+ assertEq(erc20s[0].balanceOf(charlie.addr), 0);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 0);
+
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ offerItems[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1000)
+ .withEndAmount(500);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withOffer(offerItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1000);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ vm.warp(block.timestamp + 500);
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpOfferItems(context);
+
+ assertEq(erc20s[0].balanceOf(charlie.addr), 750);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 750);
+ }
+
+ function test_setUpOfferItems_erc721() public {
+ assertEq(erc721s[0].balanceOf(charlie.addr), 0);
+ assertEq(erc721s[1].balanceOf(charlie.addr), 0);
+ assertFalse(
+ erc721s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+ assertFalse(
+ erc721s[1].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+
+ OfferItem[] memory offerItems = new OfferItem[](2);
+ offerItems[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+
+ offerItems[1] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[1]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withOffer(offerItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpOfferItems(context);
+
+ assertEq(erc721s[0].balanceOf(charlie.addr), 1);
+ assertEq(erc721s[1].balanceOf(charlie.addr), 1);
+ assertTrue(
+ erc721s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+ assertTrue(
+ erc721s[1].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+ }
+
+ function test_setUpOfferItems_erc1155() public {
+ assertEq(erc1155s[0].balanceOf(charlie.addr, 1), 0);
+ assertFalse(
+ erc1155s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+
+ OfferItem[] memory offerItems = new OfferItem[](2);
+ offerItems[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(erc1155s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(100);
+
+ offerItems[1] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(erc1155s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(100);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withOffer(offerItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpOfferItems(context);
+
+ assertEq(erc1155s[0].balanceOf(charlie.addr, 1), 200);
+ assertTrue(
+ erc1155s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+ }
+
+ function test_setUpOfferItems_erc1155_ascending() public {
+ assertEq(erc1155s[0].balanceOf(charlie.addr, 1), 0);
+ assertFalse(
+ erc1155s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+
+ OfferItem[] memory offerItems = new OfferItem[](1);
+ offerItems[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(erc1155s[0]))
+ .withIdentifierOrCriteria(1)
+ .withStartAmount(500)
+ .withStartAmount(1000);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withOffer(offerItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1000);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ vm.warp(block.timestamp + 500);
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpOfferItems(context);
+
+ assertEq(erc1155s[0].balanceOf(charlie.addr, 1), 500);
+ assertTrue(
+ erc1155s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+ }
+
+ function test_setUpConsiderationItems_erc20() public {
+ assertEq(erc20s[0].balanceOf(charlie.addr), 0);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 0);
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 2
+ );
+ considerationItems[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(100);
+
+ considerationItems[1] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(100);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withConsideration(considerationItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpConsiderationItems(context);
+
+ assertEq(erc20s[0].balanceOf(charlie.addr), 200);
+ assertEq(erc20s[0].allowance(charlie.addr, address(getSeaport())), 200);
+ }
+
+ function test_setUpConsiderationItems_erc721() public {
+ assertEq(erc721s[0].balanceOf(charlie.addr), 0);
+ assertEq(
+ erc721s[0].isApprovedForAll(charlie.addr, address(getSeaport())),
+ false
+ );
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 2
+ );
+ considerationItems[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+
+ considerationItems[1] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withConsideration(considerationItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpConsiderationItems(context);
+
+ assertEq(erc721s[0].balanceOf(charlie.addr), 2);
+ assertEq(
+ erc721s[0].isApprovedForAll(charlie.addr, address(getSeaport())),
+ true
+ );
+ }
+
+ function test_setUpConsiderationItems_erc1155() public {
+ assertEq(erc1155s[0].balanceOf(charlie.addr, 1), 0);
+ assertFalse(
+ erc1155s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ 2
+ );
+ considerationItems[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(erc1155s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(100);
+
+ considerationItems[1] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(erc1155s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(100);
+
+ OrderParameters memory orderParams = OrderParametersLib
+ .empty()
+ .withOfferer(charlie.addr)
+ .withConsideration(considerationItems)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1);
+ Order memory order = OrderLib.empty().withParameters(orderParams);
+
+ AdvancedOrder[] memory orders = new AdvancedOrder[](1);
+ orders[0] = order.toAdvancedOrder({
+ numerator: 0,
+ denominator: 0,
+ extraData: bytes("")
+ });
+
+ FuzzTestContext memory context = FuzzTestContextLib.from({
+ orders: orders,
+ seaport: getSeaport(),
+ caller: charlie.addr
+ });
+
+ // Provision arrays to avoid index errors.
+ context.advancedOrdersSpace.orders = new OrderComponentsSpace[](1);
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](1);
+
+ context = context.withDerivedOrderDetails();
+
+ // Do some surgery on the context so that the setup function thinks
+ // that the order is available and worth providing balance and approvals
+ // for.
+ context
+ .executionState
+ .orderDetails[0]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ setUpConsiderationItems(context);
+
+ assertEq(erc1155s[0].balanceOf(charlie.addr, 1), 200);
+ assertTrue(
+ erc1155s[0].isApprovedForAll(charlie.addr, address(getSeaport()))
+ );
+ }
+}
diff --git a/test/foundry/new/SeaportValidator.t.sol b/test/foundry/new/SeaportValidator.t.sol
new file mode 100644
index 000000000..4caad7caa
--- /dev/null
+++ b/test/foundry/new/SeaportValidator.t.sol
@@ -0,0 +1,947 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ ConduitIssue,
+ ConsiderationIssue,
+ ErrorsAndWarnings,
+ ErrorsAndWarningsLib,
+ GenericIssue,
+ ERC20Issue,
+ ERC721Issue,
+ ERC1155Issue,
+ IssueParser,
+ OfferIssue,
+ SeaportValidator,
+ SignatureIssue,
+ StatusIssue,
+ TimeIssue,
+ NativeIssue
+} from "../../../contracts/helpers/order-validator/SeaportValidator.sol";
+
+import {
+ SeaportValidatorHelper
+} from "../../../contracts/helpers/order-validator/lib/SeaportValidatorHelper.sol";
+
+import {
+ IssueStringHelpers
+} from "../../../contracts/helpers/order-validator/lib/SeaportValidatorTypes.sol";
+
+import {
+ ConsiderationItemLib,
+ OfferItemLib,
+ OrderParametersLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderType,
+ AdvancedOrderLib,
+ ItemType
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ ConsiderationItem,
+ OfferItem,
+ OrderParameters,
+ OrderComponents,
+ Order,
+ AdvancedOrder
+} from "seaport-sol/SeaportStructs.sol";
+
+import { BaseOrderTest } from "./BaseOrderTest.sol";
+
+contract SeaportValidatorTest is BaseOrderTest {
+ using ConsiderationItemLib for ConsiderationItem;
+ using OfferItemLib for OfferItem;
+ using OrderParametersLib for OrderParameters;
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using AdvancedOrderLib for AdvancedOrder;
+
+ using IssueParser for ConduitIssue;
+ using IssueParser for ConsiderationIssue;
+ using IssueParser for ERC20Issue;
+ using IssueParser for ERC721Issue;
+ using IssueParser for GenericIssue;
+ using IssueParser for OfferIssue;
+ using IssueParser for SignatureIssue;
+ using IssueParser for StatusIssue;
+ using IssueParser for TimeIssue;
+
+ using IssueStringHelpers for uint16;
+ using ErrorsAndWarningsLib for ErrorsAndWarnings;
+
+ string constant SINGLE_ERC20 = "SINGLE_ERC20";
+ string constant SINGLE_ERC1155 = "SINGLE_ERC1155";
+ string constant SINGLE_NATIVE = "SINGLE_NATIVE";
+ string constant SINGLE_ERC721_SINGLE_ERC20 = "SINGLE_ERC721_SINGLE_ERC20";
+ string constant SINGLE_ERC721_SINGLE_NATIVE = "SINGLE_ERC721_SINGLE_NATIVE";
+ string constant SINGLE_ERC721_SINGLE_ERC721 = "SINGLE_ERC721_SINGLE_ERC721";
+
+ address internal noTokens = makeAddr("no tokens/approvals");
+
+ function setUp() public override {
+ super.setUp();
+
+ OrderLib
+ .empty()
+ .withParameters(
+ OrderComponentsLib.fromDefault(STANDARD).toOrderParameters()
+ )
+ .saveDefault(STANDARD);
+
+ // Set up and store order with single ERC20 offer item
+ OfferItem[] memory offer = new OfferItem[](1);
+ offer[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(1);
+ OrderParameters memory parameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOffer(offer);
+ OrderLib.empty().withParameters(parameters).saveDefault(SINGLE_ERC20);
+
+ // Set up and store order with single ERC721 offer item
+ offer = new OfferItem[](1);
+ offer[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ parameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOffer(offer);
+ parameters.saveDefault(SINGLE_ERC721);
+ OrderLib.empty().withParameters(parameters).saveDefault(SINGLE_ERC721);
+
+ // Set up and store order with single ERC1155 offer item
+ offer = new OfferItem[](1);
+ offer[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(erc1155s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+ parameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOffer(offer);
+ OrderLib.empty().withParameters(parameters).saveDefault(SINGLE_ERC1155);
+
+ // Set up and store order with single native offer item
+ offer = new OfferItem[](1);
+ offer[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.NATIVE)
+ .withToken(address(0))
+ .withIdentifierOrCriteria(0)
+ .withAmount(1);
+ parameters = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .toOrderParameters()
+ .withOffer(offer);
+ OrderLib.empty().withParameters(parameters).saveDefault(SINGLE_NATIVE);
+
+ // Set up and store order with single ERC721 offer item
+ // and single native consideration item
+ ConsiderationItem[] memory _consideration = new ConsiderationItem[](1);
+ _consideration[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.NATIVE)
+ .withToken(address(0))
+ .withAmount(1);
+ parameters = OrderParametersLib
+ .fromDefault(SINGLE_ERC721)
+ .withConsideration(_consideration)
+ .withTotalOriginalConsiderationItems(1);
+ OrderLib.empty().withParameters(parameters).saveDefault(
+ SINGLE_ERC721_SINGLE_NATIVE
+ );
+
+ // Set up and store order with single ERC721 offer item
+ // and single ERC20 consideration item
+ _consideration[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withAmount(1);
+ parameters = OrderParametersLib
+ .fromDefault(SINGLE_ERC721)
+ .withConsideration(_consideration)
+ .withTotalOriginalConsiderationItems(1);
+ OrderLib.empty().withParameters(parameters).saveDefault(
+ SINGLE_ERC721_SINGLE_ERC20
+ );
+
+ // Set up and store order with single ERC721 offer item
+ // and single ERC721 consideration item
+ _consideration = new ConsiderationItem[](1);
+ _consideration[0] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1)
+ .withRecipient(offerer1.addr);
+ parameters = OrderParametersLib
+ .fromDefault(SINGLE_ERC721)
+ .withConsideration(_consideration)
+ .withTotalOriginalConsiderationItems(1);
+ OrderLib.empty().withParameters(parameters).saveDefault(
+ SINGLE_ERC721_SINGLE_ERC721
+ );
+ }
+
+ function test_empty_isValidOrder() public {
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ OrderLib.empty(),
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(TimeIssue.EndTimeBeforeStartTime)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(OfferIssue.ZeroItems)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_empty_isValidOrderReadOnly() public {
+ ErrorsAndWarnings memory actual = validator.isValidOrderReadOnly(
+ OrderLib.empty(),
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(TimeIssue.EndTimeBeforeStartTime)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(OfferIssue.ZeroItems)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_default_full_isValidOrder() public {
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ OrderLib.fromDefault(STANDARD),
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.ZeroItems)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc20_identifierNonZero() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC20);
+ order.parameters.offer[0].identifierOrCriteria = 1;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC20Issue.IdentifierNonZero)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc20_invalidToken() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC20);
+ order.parameters.offer[0].token = address(0);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC20Issue.InvalidToken)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc20_insufficientAllowance() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC20);
+ order.parameters.offerer = noTokens;
+ erc20s[0].mint(noTokens, 1);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC20Issue.InsufficientAllowance)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc20_insufficientBalance() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC20);
+ order.parameters.offerer = noTokens;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC20Issue.InsufficientAllowance)
+ .addError(ERC20Issue.InsufficientBalance)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc721_amountNotOne() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offer[0].startAmount = 3;
+ order.parameters.offer[0].endAmount = 3;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.AmountNotOne)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc721_invalidToken() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offer[0].token = address(0);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.InvalidToken)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc721_identifierDNE() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721_SINGLE_ERC721);
+ order.parameters.consideration[0].identifierOrCriteria = type(uint256)
+ .max;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(ERC721Issue.IdentifierDNE)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.OffererNotReceivingAtLeastOneItem);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc721_notOwner() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offerer = noTokens;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc721_notApproved() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offerer = noTokens;
+ erc721s[0].mint(noTokens, 1);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc721_criteriaNotPartialFill() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offer[0].itemType = ItemType.ERC721_WITH_CRITERIA;
+ order.parameters.offer[0].startAmount = 2;
+ order.parameters.offer[0].endAmount = 10;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(OfferIssue.AmountVelocityHigh)
+ .addError(ERC721Issue.CriteriaNotPartialFill)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.AmountStepLarge)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc1155_invalidToken() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC1155);
+ order.parameters.offer[0].token = address(0);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC1155Issue.InvalidToken)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc1155_notApproved() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC1155);
+ order.parameters.offerer = noTokens;
+ erc1155s[0].mint(noTokens, 1, 1);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC1155Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_erc1155_insufficientBalance() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC1155);
+ order.parameters.offerer = noTokens;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC1155Issue.NotApproved)
+ .addError(ERC1155Issue.InsufficientBalance)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_statusIssue_cancelled() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+
+ OrderComponents[] memory orderComponents = new OrderComponents[](1);
+ orderComponents[0] = order.parameters.toOrderComponents(
+ seaport.getCounter(order.parameters.offerer)
+ );
+ vm.prank(order.parameters.offerer);
+ seaport.cancel(orderComponents);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(StatusIssue.Cancelled)
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_statusIssue_contractOrder() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.orderType = OrderType.CONTRACT;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(StatusIssue.ContractOrder)
+ .addWarning(ConsiderationIssue.ZeroItems)
+ .addWarning(SignatureIssue.ContractOrder);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_timeIssue_endTimeBeforeStartTime() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.startTime = block.timestamp;
+ order.parameters.endTime = block.timestamp - 1;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(TimeIssue.EndTimeBeforeStartTime)
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_timeIssue_expired() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ vm.warp(block.timestamp + 2);
+ order.parameters.startTime = block.timestamp - 2;
+ order.parameters.endTime = block.timestamp - 1;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(TimeIssue.Expired)
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_timeIssue_distantExpiration() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.startTime = block.timestamp;
+ order.parameters.endTime = type(uint256).max;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.DistantExpiration)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_timeIssue_notActive_shortOrder() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.startTime = block.timestamp + 1;
+ order.parameters.endTime = block.timestamp + 2;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.NotActive)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_conduitIssue_keyInvalid() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.conduitKey = keccak256("invalid conduit key");
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ConduitIssue.KeyInvalid)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ // TODO: MissingSeaportChannel
+
+ function test_isValidOrder_signatureIssue_invalid() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(ERC721Issue.NotOwner)
+ .addError(ERC721Issue.NotApproved)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_zeroItems() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offer = new OfferItem[](0);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.ZeroItems)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_moreThanOneItem() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offerer = address(this);
+
+ erc721s[0].mint(address(this), 1);
+ erc721s[0].mint(address(this), 2);
+ erc721s[0].setApprovalForAll(address(seaport), true);
+
+ OfferItem[] memory offer = new OfferItem[](2);
+ offer[0] = order.parameters.offer[0];
+ offer[1] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(2)
+ .withAmount(1);
+
+ order.parameters.offer = offer;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.MoreThanOneItem)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_amountZero() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offer[0].startAmount = 0;
+ order.parameters.offer[0].endAmount = 0;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(OfferIssue.AmountZero)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_nativeItem() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_NATIVE);
+ order.parameters.offerer = address(this);
+
+ vm.deal(address(this), 1 ether);
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.NativeItem)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_duplicateItem() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC721);
+ order.parameters.offerer = address(this);
+
+ erc721s[0].mint(address(this), 1);
+ erc721s[0].setApprovalForAll(address(seaport), true);
+
+ OfferItem[] memory offer = new OfferItem[](2);
+ offer[0] = order.parameters.offer[0];
+ offer[1] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withAmount(1);
+
+ order.parameters.offer = offer;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(OfferIssue.DuplicateItem)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.MoreThanOneItem)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_amountVelocityHigh() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC20);
+
+ OfferItem[] memory offer = new OfferItem[](1);
+ offer[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withIdentifierOrCriteria(0)
+ .withStartAmount(1e16)
+ .withEndAmount(1e25);
+
+ order.parameters.offer = offer;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(OfferIssue.AmountVelocityHigh)
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_isValidOrder_offerIssue_amountStepLarge() public {
+ Order memory order = OrderLib.fromDefault(SINGLE_ERC20);
+ order.parameters.offerer = address(this);
+ order.parameters.startTime = block.timestamp;
+ order.parameters.endTime = block.timestamp + 60 * 60 * 24;
+
+ OfferItem[] memory offer = new OfferItem[](1);
+ offer[0] = OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withIdentifierOrCriteria(0)
+ .withStartAmount(1e10)
+ .withEndAmount(1e11);
+
+ order.parameters.offer = offer;
+
+ ErrorsAndWarnings memory actual = validator.isValidOrder(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(SignatureIssue.Invalid)
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(OfferIssue.AmountStepLarge)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function test_default_full_isValidOrderReadOnly() public {
+ Order memory order = OrderLib.empty().withParameters(
+ OrderComponentsLib.fromDefault(STANDARD).toOrderParameters()
+ );
+ ErrorsAndWarnings memory actual = validator.isValidOrderReadOnly(
+ order,
+ address(seaport)
+ );
+
+ ErrorsAndWarnings memory expected = ErrorsAndWarningsLib
+ .empty()
+ .addError(GenericIssue.InvalidOrderFormat)
+ .addWarning(TimeIssue.ShortOrder)
+ .addWarning(OfferIssue.ZeroItems)
+ .addWarning(ConsiderationIssue.ZeroItems);
+
+ assertEq(actual, expected);
+ }
+
+ function assertEq(
+ ErrorsAndWarnings memory left,
+ ErrorsAndWarnings memory right
+ ) internal {
+ assertEq(
+ left.errors.length,
+ right.errors.length,
+ "Unexpected number of errors"
+ );
+ assertEq(
+ left.warnings.length,
+ right.warnings.length,
+ "Unexpected number of warnings"
+ );
+ for (uint i = 0; i < left.errors.length; i++) {
+ assertEq(
+ left.errors[i].toIssueString(),
+ right.errors[i].toIssueString(),
+ "Unexpected error"
+ );
+ }
+ for (uint i = 0; i < left.warnings.length; i++) {
+ assertEq(
+ left.warnings[i].toIssueString(),
+ right.warnings[i].toIssueString(),
+ "Unexpected warning"
+ );
+ }
+ }
+}
diff --git a/test/foundry/new/SelfRestricted.t.sol b/test/foundry/new/SelfRestricted.t.sol
new file mode 100644
index 000000000..0a3613d08
--- /dev/null
+++ b/test/foundry/new/SelfRestricted.t.sol
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ CriteriaResolver,
+ FulfillmentComponentLib,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib,
+ SeaportArrays
+} from "seaport-sol/SeaportSol.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { Account, BaseOrderTest } from "./BaseOrderTest.sol";
+
+import { ValidationOffererZone } from "./zones/ValidationOffererZone.sol";
+
+contract SelfRestrictedTest is BaseOrderTest {
+ using AdvancedOrderLib for AdvancedOrder;
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using FulfillmentComponentLib for FulfillmentComponent[];
+ using FulfillmentLib for Fulfillment;
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ ValidationOffererZone zone;
+
+ struct ContextOverride {
+ SeaportInterface seaport;
+ bytes32 conduitKey;
+ bool exactAmount;
+ Account offerer;
+ }
+
+ function setUp() public virtual override {
+ super.setUp();
+ }
+
+ function test(
+ function(ContextOverride memory) external fn,
+ ContextOverride memory context
+ ) internal {
+ try fn(context) {
+ fail("Differential tests should revert with failure status");
+ } catch (bytes memory reason) {
+ assertPass(reason);
+ }
+ }
+
+ function testSelfFulfillRestrictedNoConduitExactAmount() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: bytes32(0),
+ exactAmount: true,
+ offerer: offerer1
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: bytes32(0),
+ exactAmount: true,
+ offerer: offerer1
+ })
+ );
+ }
+
+ function testSelfFulfillRestrictedWithConduitExactAmount() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: conduitKey,
+ exactAmount: true,
+ offerer: offerer1
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: conduitKey,
+ exactAmount: true,
+ offerer: offerer1
+ })
+ );
+ }
+
+ function testSelfFulfillRestrictedNoConduitNotExactAmount420() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: bytes32(0),
+ exactAmount: false,
+ offerer: offerer1
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: bytes32(0),
+ exactAmount: false,
+ offerer: offerer1
+ })
+ );
+ }
+
+ function testSelfFulfillRestrictedWithConduitNotExactAmount() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: conduitKey,
+ exactAmount: false,
+ offerer: offerer1
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: bytes32(0),
+ exactAmount: false,
+ offerer: offerer1
+ })
+ );
+ }
+
+ function testSuite() public {
+ for (uint256 i; i < 2; i++) {
+ SeaportInterface _seaport = i == 0 ? seaport : referenceSeaport;
+ for (uint256 j; j < 2; j++) {
+ bytes32 _conduitKey = j == 0 ? conduitKey : bytes32(0);
+ for (uint256 k; k < 2; k++) {
+ bool _exactAmount = k == 0 ? true : false;
+ for (uint256 m; m < 2; m++) {
+ Account memory _offerer = m == 0 ? offerer1 : offerer2;
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: _seaport,
+ conduitKey: _conduitKey,
+ exactAmount: _exactAmount,
+ offerer: _offerer
+ })
+ );
+ }
+ }
+ }
+ }
+ }
+
+ function testMultiOffer() public {
+ test(
+ this.execMultiOffer,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: bytes32(0),
+ exactAmount: false,
+ offerer: offerer2
+ })
+ );
+ }
+
+ function setUpSelfFulfillRestricted(
+ ContextOverride memory context
+ )
+ internal
+ returns (
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ Fulfillment[] memory fulfillments
+ )
+ {
+ erc721s[0].mint(offerer1.addr, 1);
+
+ AdvancedOrder memory advancedOrder;
+ AdvancedOrder memory advancedOrder2;
+
+ uint256 considerAmount = 10;
+ zone = new ValidationOffererZone(considerAmount + 1);
+
+ uint256 matchAmount = context.exactAmount
+ ? considerAmount
+ : considerAmount + 1;
+
+ advancedOrder = createOpenConsiderErc20(
+ context,
+ offerer1,
+ considerAmount
+ );
+ advancedOrder2 = createRestrictedOfferErc20(context, matchAmount);
+
+ fulfillments = SeaportArrays.Fulfillments(
+ FulfillmentLib.fromDefault(FF_SF),
+ FulfillmentLib.fromDefault(SF_FF)
+ );
+ orders = SeaportArrays.AdvancedOrders(advancedOrder, advancedOrder2);
+
+ return (orders, resolvers, fulfillments);
+ }
+
+ function createOpenConsiderErc20(
+ ContextOverride memory context,
+ Account memory account,
+ uint256 considerAmount
+ ) internal view returns (AdvancedOrder memory advancedOrder) {
+ // create the first order
+ // offer: 1 ERC721
+ // consider: 10 ERC20
+
+ OfferItem[] memory offer = SeaportArrays.OfferItems(
+ OfferItemLib
+ .fromDefault(SINGLE_ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ );
+ ConsiderationItem[] memory consideration = SeaportArrays
+ .ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withRecipient(account.addr)
+ .withStartAmount(considerAmount)
+ .withEndAmount(considerAmount)
+ );
+
+ OrderComponents memory components = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(account.addr)
+ .withOffer(offer)
+ .withConsideration(consideration)
+ .withCounter(context.seaport.getCounter(account.addr))
+ .withConduitKey(context.conduitKey);
+
+ bytes32 orderHash = seaport.getOrderHash(components);
+ bytes memory signature = signOrder(
+ context.seaport,
+ account.key,
+ orderHash
+ );
+ advancedOrder = AdvancedOrderLib
+ .fromDefault(FULL)
+ .withParameters(components.toOrderParameters())
+ .withSignature(signature);
+ }
+
+ function createRestrictedOfferErc20(
+ ContextOverride memory context,
+ uint256 amount
+ ) internal view returns (AdvancedOrder memory advancedOrder) {
+ OfferItem[] memory offer = SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(amount)
+ .withEndAmount(amount)
+ );
+ ConsiderationItem[] memory consideration = SeaportArrays
+ .ConsiderationItems(
+ ConsiderationItemLib
+ .fromDefault(SINGLE_ERC721)
+ .withToken(address(erc721s[0]))
+ .withRecipient(context.offerer.addr)
+ .withIdentifierOrCriteria(1)
+ );
+ Account memory _offerer = context.offerer;
+ OrderComponents memory components = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(_offerer.addr)
+ .withOffer(offer)
+ .withConsideration(consideration)
+ .withCounter(context.seaport.getCounter(_offerer.addr))
+ .withConduitKey(context.conduitKey)
+ .withOrderType(OrderType.FULL_RESTRICTED)
+ .withZone(address(zone));
+
+ bytes32 orderHash = seaport.getOrderHash(components);
+ bytes memory signature = signOrder(
+ context.seaport,
+ _offerer.key,
+ orderHash
+ );
+ advancedOrder = AdvancedOrderLib
+ .fromDefault(FULL)
+ .withParameters(components.toOrderParameters())
+ .withSignature(signature);
+ }
+
+ function setUpSelfFulfillRestrictedMultiOffer(
+ ContextOverride memory context
+ )
+ internal
+ returns (
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ Fulfillment[] memory fulfillments
+ )
+ {
+ erc721s[0].mint(offerer1.addr, 1);
+
+ AdvancedOrder memory advancedOrder;
+ AdvancedOrder memory advancedOrder2;
+
+ uint256 considerAmount = 10;
+ zone = new ValidationOffererZone(considerAmount + 1);
+
+ uint256 matchAmount = context.exactAmount
+ ? considerAmount
+ : considerAmount - 1;
+
+ advancedOrder = createOpenConsiderErc20(
+ context,
+ offerer1,
+ considerAmount
+ );
+ advancedOrder2 = createRestrictedOffersErc20(context, matchAmount);
+
+ fulfillments = SeaportArrays.Fulfillments(
+ FulfillmentLib.fromDefault(FF_SF),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponentLib
+ .empty()
+ .withOrderIndex(1)
+ .withItemIndex(0),
+ FulfillmentComponentLib
+ .empty()
+ .withOrderIndex(1)
+ .withItemIndex(1)
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponentLib.empty()
+ )
+ })
+ );
+ orders = SeaportArrays.AdvancedOrders(advancedOrder, advancedOrder2);
+
+ return (orders, resolvers, fulfillments);
+ }
+
+ function execMultiOffer(ContextOverride memory context) external stateless {
+ (
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ Fulfillment[] memory fulfillments
+ ) = setUpSelfFulfillRestrictedMultiOffer(context);
+ context.seaport.matchAdvancedOrders(
+ orders,
+ resolvers,
+ fulfillments,
+ address(0x1234)
+ );
+ }
+
+ function createRestrictedOffersErc20(
+ ContextOverride memory context,
+ uint256 amountLessOne
+ ) internal view returns (AdvancedOrder memory advancedOrder) {
+ OfferItem[] memory offer = SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(amountLessOne)
+ .withEndAmount(amountLessOne),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(amountLessOne)
+ .withEndAmount(amountLessOne)
+ );
+ ConsiderationItem[] memory consideration = SeaportArrays
+ .ConsiderationItems(
+ ConsiderationItemLib
+ .fromDefault(SINGLE_ERC721)
+ .withToken(address(erc721s[0]))
+ .withRecipient(context.offerer.addr)
+ .withIdentifierOrCriteria(1)
+ );
+ Account memory _offerer = context.offerer;
+ OrderComponents memory components = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(_offerer.addr)
+ .withOffer(offer)
+ .withConsideration(consideration)
+ .withCounter(context.seaport.getCounter(_offerer.addr))
+ .withConduitKey(context.conduitKey)
+ .withOrderType(OrderType.FULL_RESTRICTED)
+ .withZone(address(zone));
+
+ bytes32 orderHash = seaport.getOrderHash(components);
+ bytes memory signature = signOrder(
+ context.seaport,
+ _offerer.key,
+ orderHash
+ );
+ advancedOrder = AdvancedOrderLib
+ .fromDefault(FULL)
+ .withParameters(components.toOrderParameters())
+ .withSignature(signature);
+ }
+
+ function execSelfFulfillRestricted(
+ ContextOverride memory context
+ ) external stateless {
+ (
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ Fulfillment[] memory fulfillments
+ ) = setUpSelfFulfillRestricted(context);
+
+ context.seaport.matchAdvancedOrders(
+ orders,
+ resolvers,
+ fulfillments,
+ address(0x1234)
+ );
+ }
+}
diff --git a/test/foundry/new/SelfRestrictedContractOfferer.t.sol b/test/foundry/new/SelfRestrictedContractOfferer.t.sol
new file mode 100644
index 000000000..eba5e1590
--- /dev/null
+++ b/test/foundry/new/SelfRestrictedContractOfferer.t.sol
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ Fulfillment,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ CriteriaResolver,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib,
+ SeaportArrays
+} from "seaport-sol/SeaportSol.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { BaseOrderTest } from "./BaseOrderTest.sol";
+
+import { ValidationOffererZone } from "./zones/ValidationOffererZone.sol";
+
+import {
+ ERC20Interface,
+ ERC721Interface
+} from "seaport-core/interfaces/AbridgedTokenInterfaces.sol";
+
+contract SelfRestrictedContractOffererTest is BaseOrderTest {
+ using AdvancedOrderLib for AdvancedOrder;
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ ValidationOffererZone offerer;
+
+ struct ContextOverride {
+ SeaportInterface seaport;
+ bytes32 conduitKey;
+ bool exactAmount;
+ }
+
+ function setUp() public virtual override {
+ super.setUp();
+ }
+
+ function test(
+ function(ContextOverride memory) external fn,
+ ContextOverride memory context
+ ) internal {
+ try fn(context) {
+ fail("Differential tests should revert with failure status");
+ } catch (bytes memory reason) {
+ assertPass(reason);
+ }
+ }
+
+ function testSelfFulfillRestrictedNoConduitExactAmount69() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: bytes32(0),
+ exactAmount: true
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: bytes32(0),
+ exactAmount: true
+ })
+ );
+ }
+
+ function testSelfFulfillRestrictedWithConduitExactAmount() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: conduitKey,
+ exactAmount: true
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: conduitKey,
+ exactAmount: true
+ })
+ );
+ }
+
+ function testSelfFulfillRestrictedNoConduitNotExactAmount() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: bytes32(0),
+ exactAmount: false
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: bytes32(0),
+ exactAmount: false
+ })
+ );
+ }
+
+ function testSelfFulfillRestrictedWithConduitNotExactAmount() public {
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: seaport,
+ conduitKey: conduitKey,
+ exactAmount: false
+ })
+ );
+ test(
+ this.execSelfFulfillRestricted,
+ ContextOverride({
+ seaport: referenceSeaport,
+ conduitKey: bytes32(0),
+ exactAmount: false
+ })
+ );
+ }
+
+ function setUpSelfFulfillRestricted(
+ ContextOverride memory context
+ )
+ internal
+ returns (
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ Fulfillment[] memory fulfillments
+ )
+ {
+ erc721s[0].mint(offerer1.addr, 1);
+
+ AdvancedOrder memory advancedOrder;
+ OfferItem[] memory offer;
+ ConsiderationItem[] memory consideration;
+ OrderComponents memory components;
+ bytes32 orderHash;
+ bytes memory signature;
+ AdvancedOrder memory advancedOrder2;
+
+ uint256 considerAmount = 10;
+ offerer = new ValidationOffererZone(considerAmount + 1);
+
+ allocateTokensAndApprovals(address(offerer), type(uint128).max);
+
+ uint256 matchAmount = context.exactAmount
+ ? considerAmount
+ : considerAmount + 1;
+
+ // create the first order
+ // offer: 1 ERC721
+ // consider: 10 ERC20
+ {
+ consideration = SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .fromDefault(SINGLE_ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ .withRecipient(address(offerer))
+ );
+ offer = SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(matchAmount)
+ .withEndAmount(matchAmount)
+ );
+
+ components = OrderComponentsLib
+ .fromDefault(STANDARD)
+ .withOfferer(address(offerer))
+ .withOffer(offer)
+ .withConsideration(consideration)
+ .withOrderType(OrderType.CONTRACT);
+
+ // orderHash = seaport.getOrderHash(components);
+ // signature = signOrder(context.seaport, offerer1.key, orderHash);
+ advancedOrder = AdvancedOrderLib.fromDefault(FULL).withParameters(
+ components.toOrderParameters()
+ );
+ }
+
+ // create the second order
+ // offer: 100 ERC20
+ // consider: 1 ERC721
+ {
+ consideration = SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(erc20s[0]))
+ .withStartAmount(considerAmount)
+ .withEndAmount(considerAmount)
+ .withRecipient(address(offerer))
+ );
+ offer = SeaportArrays.OfferItems(
+ OfferItemLib
+ .fromDefault(SINGLE_ERC721)
+ .withToken(address(erc721s[0]))
+ .withIdentifierOrCriteria(1)
+ );
+ components = components
+ .copy()
+ .withOffer(offer)
+ .withConsideration(consideration)
+ .withOrderType(OrderType.FULL_OPEN)
+ .withCounter(context.seaport.getCounter(address(offerer))); //.withZone(address(zone))
+ // .withConduitKey(bytes32(0));
+
+ orderHash = seaport.getOrderHash(components);
+ Order memory order = Order({
+ parameters: components.toOrderParameters(),
+ signature: ""
+ });
+ vm.prank(address(offerer));
+ context.seaport.incrementCounter();
+ vm.prank(address(offerer));
+ context.seaport.validate(SeaportArrays.Orders(order));
+
+ advancedOrder2 = AdvancedOrderLib
+ .fromDefault(FULL)
+ .withParameters(components.toOrderParameters())
+ .withSignature(signature);
+ }
+
+ fulfillments = SeaportArrays.Fulfillments(
+ FulfillmentLib.fromDefault(FF_SF),
+ FulfillmentLib.fromDefault(SF_FF)
+ );
+ orders = SeaportArrays.AdvancedOrders(advancedOrder2, advancedOrder);
+
+ return (orders, resolvers, fulfillments);
+ }
+
+ function execSelfFulfillRestricted(
+ ContextOverride memory context
+ ) external stateless {
+ (
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers,
+ Fulfillment[] memory fulfillments
+ ) = setUpSelfFulfillRestricted(context);
+
+ context.seaport.matchAdvancedOrders(
+ orders,
+ resolvers,
+ fulfillments,
+ address(this)
+ );
+ }
+}
diff --git a/test/foundry/new/helpers/ArithmeticUtil.sol b/test/foundry/new/helpers/ArithmeticUtil.sol
new file mode 100644
index 000000000..919983b0a
--- /dev/null
+++ b/test/foundry/new/helpers/ArithmeticUtil.sol
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+library ArithmeticUtil {
+ /**
+ * @dev utility function to avoid overflows when multiplying fuzzed uints
+ * with widths <256
+ */
+ function mul(uint256 a, uint256 b) internal pure returns (uint256) {
+ return a * b;
+ }
+
+ /**
+ * @dev utility function to avoid overflows when adding fuzzed uints with
+ * with widths <256
+ */
+ function add(uint256 a, uint256 b) internal pure returns (uint256) {
+ return a + b;
+ }
+
+ /**
+ * @dev utility function to avoid overflows when subtracting fuzzed uints
+ * with widths <256
+ */
+ function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+ return a - b;
+ }
+
+ /**
+ * @dev utility function to avoid overflows when dividing fuzzed uints with
+ * widths <256
+ */
+ function div(uint256 a, uint256 b) internal pure returns (uint256) {
+ return a / b;
+ }
+}
diff --git a/test/foundry/new/helpers/BaseSeaportTest.sol b/test/foundry/new/helpers/BaseSeaportTest.sol
new file mode 100644
index 000000000..34ebcb95a
--- /dev/null
+++ b/test/foundry/new/helpers/BaseSeaportTest.sol
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { stdStorage, StdStorage } from "forge-std/Test.sol";
+
+import { DifferentialTest } from "./DifferentialTest.sol";
+
+import {
+ ConduitControllerInterface
+} from "seaport-sol/ConduitControllerInterface.sol";
+
+import {
+ ConduitController
+} from "../../../../contracts/conduit/ConduitController.sol";
+
+import {
+ ReferenceConduitController
+} from "../../../../reference/conduit/ReferenceConduitController.sol";
+
+import {
+ ConsiderationInterface
+} from "../../../../contracts/interfaces/ConsiderationInterface.sol";
+
+import { Consideration } from "../../../../contracts/lib/Consideration.sol";
+
+import {
+ ReferenceConsideration
+} from "../../../../reference/ReferenceConsideration.sol";
+
+import { Conduit } from "../../../../contracts/conduit/Conduit.sol";
+
+import { setLabel } from "./Labeler.sol";
+
+/// @dev Base test case that deploys Consideration and its dependencies.
+contract BaseSeaportTest is DifferentialTest {
+ using stdStorage for StdStorage;
+
+ bool coverage_or_debug;
+ bytes32 conduitKey;
+
+ Conduit conduit;
+ Conduit referenceConduit;
+ ConduitControllerInterface conduitController;
+ ConduitControllerInterface referenceConduitController;
+ ConsiderationInterface referenceSeaport;
+ ConsiderationInterface seaport;
+
+ function stringEq(
+ string memory a,
+ string memory b
+ ) internal pure returns (bool) {
+ return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
+ }
+
+ function debugEnabled() internal returns (bool) {
+ return vm.envOr("SEAPORT_COVERAGE", false) || debugProfileEnabled();
+ }
+
+ function debugProfileEnabled() internal returns (bool) {
+ string memory env = vm.envOr("FOUNDRY_PROFILE", string(""));
+ return stringEq(env, "debug") || stringEq(env, "moat_debug");
+ }
+
+ function setUp() public virtual {
+ // Conditionally deploy contracts normally or from precompiled source
+ // deploys normally when SEAPORT_COVERAGE is true for coverage analysis
+ // or when FOUNDRY_PROFILE is "debug" for debugging with source maps
+ // deploys from precompiled source when both are false.
+ coverage_or_debug = debugEnabled();
+
+ conduitKey = bytes32(uint256(uint160(address(this))) << 96);
+ _deployAndConfigurePrecompiledOptimizedConsideration();
+ _deployAndConfigurePrecompiledReferenceConsideration();
+
+ setLabel(address(conduitController), "conduitController");
+ setLabel(address(seaport), "seaport");
+ setLabel(address(conduit), "conduit");
+ setLabel(
+ address(referenceConduitController),
+ "referenceConduitController"
+ );
+ setLabel(address(referenceSeaport), "referenceSeaport");
+ setLabel(address(referenceConduit), "referenceConduit");
+ setLabel(address(this), "testContract");
+ }
+
+ /**
+ * @dev Get the configured preferred Seaport
+ */
+ function getSeaport() internal returns (ConsiderationInterface seaport_) {
+ string memory profile = vm.envOr("MOAT_PROFILE", string("optimized"));
+
+ if (stringEq(profile, "reference")) {
+ emit log("Using reference Seaport and ConduitController");
+ seaport_ = referenceSeaport;
+ } else {
+ seaport_ = seaport;
+ }
+ }
+
+ /**
+ * @dev Get the configured preferred ConduitController
+ */
+ function getConduitController()
+ internal
+ returns (ConduitControllerInterface conduitController_)
+ {
+ string memory profile = vm.envOr("MOAT_PROFILE", string("optimized"));
+
+ if (stringEq(profile, "reference")) {
+ conduitController_ = referenceConduitController;
+ } else {
+ conduitController_ = conduitController;
+ }
+ }
+
+ ///@dev deploy optimized consideration contracts from pre-compiled source
+ // (solc-0.8.17, IR pipeline enabled, unless running coverage or debug)
+ function _deployAndConfigurePrecompiledOptimizedConsideration() public {
+ if (!coverage_or_debug) {
+ conduitController = ConduitController(
+ deployCode(
+ "optimized-out/ConduitController.sol/ConduitController.json"
+ )
+ );
+ seaport = ConsiderationInterface(
+ deployCode(
+ "optimized-out/Consideration.sol/Consideration.json",
+ abi.encode(address(conduitController))
+ )
+ );
+ } else {
+ conduitController = new ConduitController();
+ seaport = new Consideration(address(conduitController));
+ }
+ //create conduit, update channel
+ conduit = Conduit(
+ conduitController.createConduit(conduitKey, address(this))
+ );
+ conduitController.updateChannel(
+ address(conduit),
+ address(seaport),
+ true
+ );
+ }
+
+ ///@dev deploy reference consideration contracts from pre-compiled source
+ /// (solc-0.8.13, IR pipeline disabled, unless running coverage or debug)
+ function _deployAndConfigurePrecompiledReferenceConsideration() public {
+ if (!coverage_or_debug) {
+ referenceConduitController = ConduitController(
+ deployCode(
+ "reference-out/ReferenceConduitController.sol/ReferenceConduitController.json"
+ )
+ );
+ referenceSeaport = ConsiderationInterface(
+ deployCode(
+ "reference-out/ReferenceConsideration.sol/ReferenceConsideration.json",
+ abi.encode(address(referenceConduitController))
+ )
+ );
+ } else {
+ referenceConduitController = new ReferenceConduitController();
+ // for debugging
+ referenceSeaport = new ReferenceConsideration(
+ address(referenceConduitController)
+ );
+ }
+
+ //create conduit, update channel
+ referenceConduit = Conduit(
+ referenceConduitController.createConduit(conduitKey, address(this))
+ );
+ referenceConduitController.updateChannel(
+ address(referenceConduit),
+ address(referenceSeaport),
+ true
+ );
+ }
+
+ function signOrder(
+ ConsiderationInterface _consideration,
+ uint256 _pkOfSigner,
+ bytes32 _orderHash
+ ) internal view returns (bytes memory) {
+ (bytes32 r, bytes32 s, uint8 v) = getSignatureComponents(
+ _consideration,
+ _pkOfSigner,
+ _orderHash
+ );
+ return abi.encodePacked(r, s, v);
+ }
+
+ function getSignatureComponents(
+ ConsiderationInterface _consideration,
+ uint256 _pkOfSigner,
+ bytes32 _orderHash
+ ) internal view returns (bytes32, bytes32, uint8) {
+ (, bytes32 domainSeparator, ) = _consideration.information();
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(
+ _pkOfSigner,
+ keccak256(
+ abi.encodePacked(bytes2(0x1901), domainSeparator, _orderHash)
+ )
+ );
+ return (r, s, v);
+ }
+}
diff --git a/test/foundry/new/helpers/CriteriaResolverHelper.sol b/test/foundry/new/helpers/CriteriaResolverHelper.sol
new file mode 100644
index 000000000..101bf3967
--- /dev/null
+++ b/test/foundry/new/helpers/CriteriaResolverHelper.sol
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import { LibSort } from "solady/src/utils/LibSort.sol";
+
+import { Merkle } from "murky/Merkle.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ OfferItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, Side } from "seaport-sol/SeaportEnums.sol";
+
+struct CriteriaMetadata {
+ uint256 resolvedIdentifier;
+ bytes32[] proof;
+}
+
+contract CriteriaResolverHelper {
+ using LibPRNG for LibPRNG.PRNG;
+
+ uint256 immutable MAX_LEAVES;
+ Merkle public immutable MERKLE;
+
+ struct WildcardIdentifier {
+ bool set;
+ uint256 identifier;
+ }
+
+ mapping(uint256 => CriteriaMetadata)
+ internal _resolvableIdentifierForGivenCriteria;
+
+ mapping(bytes32 => WildcardIdentifier)
+ internal _wildcardIdentifierForGivenItemHash;
+
+ constructor(uint256 maxLeaves) {
+ MAX_LEAVES = maxLeaves;
+ MERKLE = new Merkle();
+ }
+
+ function resolvableIdentifierForGivenCriteria(
+ uint256 criteria
+ ) public view returns (CriteriaMetadata memory) {
+ return _resolvableIdentifierForGivenCriteria[criteria];
+ }
+
+ function wildcardIdentifierForGivenItemHash(
+ bytes32 itemHash
+ ) public view returns (uint256) {
+ WildcardIdentifier memory id = _wildcardIdentifierForGivenItemHash[
+ itemHash
+ ];
+
+ if (!id.set) {
+ revert(
+ "CriteriaResolverHelper: no wildcard set for given item hash"
+ );
+ }
+
+ return id.identifier;
+ }
+
+ function deriveCriteriaResolvers(
+ AdvancedOrder[] memory orders
+ ) public view returns (CriteriaResolver[] memory criteriaResolvers) {
+ uint256 maxLength;
+
+ for (uint256 i; i < orders.length; i++) {
+ AdvancedOrder memory order = orders[i];
+ maxLength += (order.parameters.offer.length +
+ order.parameters.consideration.length);
+ }
+ criteriaResolvers = new CriteriaResolver[](maxLength);
+ uint256 index;
+
+ for (uint256 i; i < orders.length; i++) {
+ AdvancedOrder memory order = orders[i];
+
+ for (uint256 j; j < order.parameters.offer.length; j++) {
+ OfferItem memory offerItem = order.parameters.offer[j];
+ if (
+ offerItem.itemType == ItemType.ERC721_WITH_CRITERIA ||
+ offerItem.itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ if (offerItem.identifierOrCriteria == 0) {
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(i, j, Side.OFFER)
+ );
+
+ WildcardIdentifier
+ memory id = _wildcardIdentifierForGivenItemHash[
+ itemHash
+ ];
+ if (!id.set) {
+ revert(
+ "CriteriaResolverHelper: no wildcard identifier located for offer item"
+ );
+ }
+
+ criteriaResolvers[index] = CriteriaResolver({
+ orderIndex: i,
+ side: Side.OFFER,
+ index: j,
+ identifier: id.identifier,
+ criteriaProof: new bytes32[](0)
+ });
+ } else {
+ CriteriaMetadata
+ memory criteriaMetadata = _resolvableIdentifierForGivenCriteria[
+ offerItem.identifierOrCriteria
+ ];
+
+ // Store the criteria resolver in the mapping
+ criteriaResolvers[index] = CriteriaResolver({
+ orderIndex: i,
+ side: Side.OFFER,
+ index: j,
+ identifier: criteriaMetadata.resolvedIdentifier,
+ criteriaProof: criteriaMetadata.proof
+ });
+ }
+ index++;
+ }
+ }
+
+ for (uint256 j; j < order.parameters.consideration.length; j++) {
+ ConsiderationItem memory considerationItem = order
+ .parameters
+ .consideration[j];
+ if (
+ considerationItem.itemType ==
+ ItemType.ERC721_WITH_CRITERIA ||
+ considerationItem.itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ if (considerationItem.identifierOrCriteria == 0) {
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(i, j, Side.CONSIDERATION)
+ );
+
+ WildcardIdentifier
+ memory id = _wildcardIdentifierForGivenItemHash[
+ itemHash
+ ];
+ if (!id.set) {
+ revert(
+ "CriteriaResolverHelper: no wildcard identifier located for consideration item"
+ );
+ }
+
+ criteriaResolvers[index] = CriteriaResolver({
+ orderIndex: i,
+ side: Side.CONSIDERATION,
+ index: j,
+ identifier: id.identifier,
+ criteriaProof: new bytes32[](0)
+ });
+ } else {
+ CriteriaMetadata
+ memory criteriaMetadata = _resolvableIdentifierForGivenCriteria[
+ considerationItem.identifierOrCriteria
+ ];
+
+ // Store the criteria resolver in the mapping
+ criteriaResolvers[index] = CriteriaResolver({
+ orderIndex: i,
+ side: Side.CONSIDERATION,
+ index: j,
+ identifier: criteriaMetadata.resolvedIdentifier,
+ criteriaProof: criteriaMetadata.proof
+ });
+ }
+ index++;
+ }
+ }
+ }
+ // update actual length
+ assembly {
+ mstore(criteriaResolvers, index)
+ }
+ }
+
+ /**
+ * @notice Generates a random number of random token identifiers to use as
+ * leaves in a Merkle tree, then hashes them to leaves, and finally
+ * generates a Merkle root and proof for a randomly selected leaf
+ * @param prng PRNG to use to generate the criteria metadata
+ */
+ function generateCriteriaMetadata(
+ LibPRNG.PRNG memory prng,
+ uint256 desiredId
+ ) public returns (uint256 criteria) {
+ uint256[] memory identifiers = generateIdentifiers(prng);
+
+ uint256 selectedIdentifierIndex = prng.next() % identifiers.length;
+
+ if (desiredId != type(uint256).max) {
+ identifiers[selectedIdentifierIndex] = desiredId;
+ }
+
+ uint256 selectedIdentifier = identifiers[selectedIdentifierIndex];
+ bytes32[] memory leaves = hashIdentifiersToLeaves(identifiers);
+ // TODO: Base Murky impl is very memory-inefficient (O(n^2))
+ uint256 resolvedIdentifier = selectedIdentifier;
+ criteria = uint256(MERKLE.getRoot(leaves));
+ bytes32[] memory proof = MERKLE.getProof(
+ leaves,
+ selectedIdentifierIndex
+ );
+
+ _resolvableIdentifierForGivenCriteria[criteria] = CriteriaMetadata({
+ resolvedIdentifier: resolvedIdentifier,
+ proof: proof
+ });
+ }
+
+ function generateWildcard(
+ LibPRNG.PRNG memory prng,
+ uint256 desiredId,
+ uint256 orderIndex,
+ uint256 itemIndex,
+ Side side
+ ) public returns (uint256 criteria) {
+ criteria = (desiredId == type(uint256).max) ? prng.next() : desiredId;
+
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(orderIndex, itemIndex, side)
+ );
+
+ WildcardIdentifier storage id = (
+ _wildcardIdentifierForGivenItemHash[itemHash]
+ );
+
+ if (id.set) {
+ revert(
+ "CriteriaResolverHelper: wildcard already set for this item"
+ );
+ }
+
+ id.set = true;
+ id.identifier = criteria;
+ }
+
+ function shiftWildcards(
+ uint256 orderIndex,
+ Side side,
+ uint256 insertionIndex,
+ uint256 originalLength
+ ) public {
+ for (uint256 i = originalLength; i > insertionIndex; --i) {
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(orderIndex, i - 1, side)
+ );
+
+ WildcardIdentifier storage id = (
+ _wildcardIdentifierForGivenItemHash[itemHash]
+ );
+
+ if (id.set) {
+ uint256 identifier = id.identifier;
+ id.set = false;
+ id.identifier = 0;
+
+ bytes32 shiftedItemHash = keccak256(
+ abi.encodePacked(orderIndex, i, side)
+ );
+
+ WildcardIdentifier storage shiftedId = (
+ _wildcardIdentifierForGivenItemHash[shiftedItemHash]
+ );
+
+ if (shiftedId.set) {
+ revert("CriteriaResolverHelper: shifting into a set item");
+ }
+
+ shiftedId.set = true;
+ shiftedId.identifier = identifier;
+ }
+ }
+ }
+
+ /**
+ * @notice Generates a random number of random token identifiers to use as
+ * leaves in a Merkle tree
+ * @param prng PRNG to use to generate the identifiers
+ */
+ function generateIdentifiers(
+ LibPRNG.PRNG memory prng
+ ) public view returns (uint256[] memory identifiers) {
+ uint256 numIdentifiers = (prng.next() % (2 ** MAX_LEAVES));
+ if (numIdentifiers <= 1) {
+ numIdentifiers = 2;
+ }
+ identifiers = new uint256[](numIdentifiers);
+ for (uint256 i = 0; i < numIdentifiers; ) {
+ identifiers[i] = prng.next();
+ unchecked {
+ ++i;
+ }
+ }
+ bool shouldSort = prng.next() % 2 == 1;
+ if (shouldSort) {
+ LibSort.sort(identifiers);
+ }
+ }
+
+ /**
+ * @notice Hashes an array of identifiers in-place to use as leaves in a
+ * Merkle tree
+ * @param identifiers Identifiers to hash
+ */
+ function hashIdentifiersToLeaves(
+ uint256[] memory identifiers
+ ) public pure returns (bytes32[] memory leaves) {
+ assembly {
+ leaves := identifiers
+ }
+ for (uint256 i = 0; i < identifiers.length; ) {
+ bytes32 identifier = leaves[i];
+ assembly {
+ mstore(0x0, identifier)
+ identifier := keccak256(0x0, 0x20)
+ }
+ leaves[i] = identifier;
+ unchecked {
+ ++i;
+ }
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/DebugUtil.sol b/test/foundry/new/helpers/DebugUtil.sol
new file mode 100644
index 000000000..94a4ade46
--- /dev/null
+++ b/test/foundry/new/helpers/DebugUtil.sol
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.17;
+
+import { Searializer, Execution, ItemType, vm, Vm } from "./Searializer.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { ExpectedBalances } from "./ExpectedBalances.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import { console2 } from "forge-std/console2.sol";
+
+import { ArrayHelpers, MemoryPointer } from "seaport-sol/../ArrayHelpers.sol";
+
+import { OrderStatusEnum, UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import { ForgeEventsLib } from "./event-utils/ForgeEventsLib.sol";
+
+import { TransferEventsLib } from "./event-utils/TransferEventsLib.sol";
+
+struct ContextOutputSelection {
+ bool seaport;
+ bool conduitController;
+ bool caller;
+ bool callValue;
+ bool recipient;
+ bool fuzzParams;
+ bool orders;
+ bool orderHashes;
+ bool previewedOrders;
+ bool counter;
+ bool fulfillerConduitKey;
+ bool criteriaResolvers;
+ bool fulfillments;
+ bool remainingOfferComponents;
+ bool offerFulfillments;
+ bool considerationFulfillments;
+ bool maximumFulfilled;
+ bool basicOrderParameters;
+ bool testHelpers;
+ bool checks;
+ bool expectedZoneCalldataHash;
+ bool expectedContractOrderCalldataHashes;
+ bool expectedResults;
+ bool expectedImplicitExecutions;
+ bool expectedExplicitExecutions;
+ bool allExpectedExecutions;
+ bool expectedAvailableOrders;
+ ItemType executionsFilter;
+ bool expectedEventHashes;
+ bool actualEvents;
+ bool expectedEvents;
+ bool returnValues;
+ bool nativeExpectedBalances;
+ bool erc20ExpectedBalances;
+ bool erc721ExpectedBalances;
+ bool erc1155ExpectedBalances;
+ bool preExecOrderStatuses;
+ bool validationErrors;
+}
+
+using ForgeEventsLib for Vm.Log;
+using ForgeEventsLib for Vm.Log[];
+using TransferEventsLib for Execution[];
+using FuzzEngineLib for FuzzTestContext;
+using ExecutionFilterCast for Execution[];
+
+/**
+ * @dev Serialize and write a FuzzTestContext to a `fuzz_debug.json` file.
+ *
+ * @param context the FuzzTestContext to serialize.
+ * @param outputSelection a ContextOutputSelection struct containing flags
+ that define which FuzzTestContext fields to serialize.
+ */
+function dumpContext(
+ FuzzTestContext memory context,
+ ContextOutputSelection memory outputSelection
+) {
+ string memory jsonOut;
+ jsonOut = vm.serializeString("root", "_action", context.actionName());
+ if (outputSelection.seaport) {
+ jsonOut = Searializer.tojsonAddress(
+ "root",
+ "seaport",
+ address(context.seaport)
+ );
+ }
+ // if (outputSelection.conduitController) {
+ // jsonOut = Searializer.tojsonAddress(
+ // "root",
+ // "conduitController",
+ // address(context.conduitController)
+ // );
+ // }
+ if (outputSelection.caller) {
+ jsonOut = Searializer.tojsonAddress(
+ "root",
+ "caller",
+ context.executionState.caller
+ );
+ }
+ if (outputSelection.recipient) {
+ jsonOut = Searializer.tojsonAddress(
+ "root",
+ "recipient",
+ context.executionState.recipient
+ );
+ }
+ if (outputSelection.callValue) {
+ jsonOut = Searializer.tojsonUint256(
+ "root",
+ "callValue",
+ context.executionState.value
+ );
+ }
+ if (outputSelection.maximumFulfilled) {
+ jsonOut = Searializer.tojsonUint256(
+ "root",
+ "maximumFulfilled",
+ context.executionState.maximumFulfilled
+ );
+ }
+ // if (outputSelection.fuzzParams) {
+ // jsonOut = Searializer.tojsonFuzzParams("root", "fuzzParams", context.fuzzParams);
+ // }
+ if (outputSelection.orders) {
+ jsonOut = Searializer.tojsonDynArrayAdvancedOrder(
+ "root",
+ "orders",
+ context.executionState.orders
+ );
+ }
+ if (outputSelection.orderHashes) {
+ bytes32[] memory orderHashes = new bytes32[](
+ context.executionState.orderDetails.length
+ );
+
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ i++
+ ) {
+ orderHashes[i] = context.executionState.orderDetails[i].orderHash;
+ }
+
+ jsonOut = Searializer.tojsonDynArrayBytes32(
+ "root",
+ "orderHashes",
+ orderHashes
+ );
+ }
+ if (outputSelection.previewedOrders) {
+ jsonOut = Searializer.tojsonDynArrayAdvancedOrder(
+ "root",
+ "previewedOrders",
+ context.executionState.previewedOrders
+ );
+ }
+ // if (outputSelection.counter) {
+ // jsonOut = Searializer.tojsonUint256("root", "counter", context.executionState.counter);
+ // }
+ // if (outputSelection.fulfillerConduitKey) {
+ // jsonOut = Searializer.tojsonBytes32(
+ // "root",
+ // "fulfillerConduitKey",
+ // context.executionState.fulfillerConduitKey
+ // );
+ // }
+ // if (outputSelection.criteriaResolvers) {
+ // jsonOut = Searializer.tojsonDynArrayCriteriaResolver(
+ // "root",
+ // "criteriaResolvers",
+ // context.executionState.criteriaResolvers
+ // );
+ // }
+ // if (outputSelection.fulfillments) {
+ // jsonOut = Searializer.tojsonDynArrayFulfillment(
+ // "root",
+ // "fulfillments",
+ // context.executionState.fulfillments
+ // );
+ // }
+ // if (outputSelection.remainingOfferComponents) {
+ // jsonOut = Searializer.tojsonDynArrayFulfillmentComponent(
+ // "root",
+ // "remainingOfferComponents",
+ // context.executionState.remainingOfferComponents
+ // );
+ // }
+ // if (outputSelection.offerFulfillments) {
+ // jsonOut = Searializer.tojsonDynArrayDynArrayFulfillmentComponent(
+ // "root",
+ // "offerFulfillments",
+ // context.executionState.offerFulfillments
+ // );
+ // }
+ // if (outputSelection.considerationFulfillments) {
+ // jsonOut = Searializer.tojsonDynArrayDynArrayFulfillmentComponent(
+ // "root",
+ // "considerationFulfillments",
+ // context.executionState.considerationFulfillments
+ // );
+ // }
+ // if (outputSelection.maximumFulfilled) {
+ // jsonOut = Searializer.tojsonUint256(
+ // "root",
+ // "maximumFulfilled",
+ // context.executionState.maximumFulfilled
+ // );
+ // }
+ // if (outputSelection.basicOrderParameters) {
+ // jsonOut = Searializer.tojsonBasicOrderParameters(
+ // "root",
+ // "basicOrderParameters",
+ // context.executionState.basicOrderParameters
+ // );
+ // }
+ // if (outputSelection.testHelpers) {
+ // jsonOut = Searializer.tojsonAddress(
+ // "root",
+ // "testHelpers",
+ // address(context.testHelpers)
+ // );
+ // }
+ // if (outputSelection.checks) {
+ // jsonOut = Searializer.tojsonDynArrayBytes4("root", "checks", context.checks);
+ // }
+ if (outputSelection.preExecOrderStatuses) {
+ jsonOut = Searializer.tojsonDynArrayUint256(
+ "root",
+ "preExecOrderStatuses",
+ cast(context.executionState.preExecOrderStatuses)
+ );
+ }
+ // if (outputSelection.expectedZoneCalldataHash) {
+ // jsonOut = Searializer.tojsonDynArrayBytes32(
+ // "root",
+ // "expectedZoneCalldataHash",
+ // context.expectations.expectedZoneCalldataHash
+ // );
+ // }
+ // if (outputSelection.expectedContractOrderCalldataHashes) {
+ // jsonOut = Searializer.tojsonDynArrayArray2Bytes32(
+ // "root",
+ // "expectedContractOrderCalldataHashes",
+ // context.expectations.expectedContractOrderCalldataHashes
+ // );
+ // }
+ // if (outputSelection.expectedResults) {
+ // jsonOut = Searializer.tojsonDynArrayResult(
+ // "root",
+ // "expectedResults",
+ // context.expectedResults
+ // );
+ // }
+
+ // =====================================================================//
+ // Executions //
+ // =====================================================================//
+
+ if (outputSelection.expectedImplicitExecutions) {
+ jsonOut = Searializer.tojsonDynArrayExecution(
+ "root",
+ "expectedImplicitPreExecutions",
+ context.expectations.expectedImplicitPreExecutions.filter(
+ outputSelection.executionsFilter
+ )
+ );
+ jsonOut = Searializer.tojsonDynArrayExecution(
+ "root",
+ "expectedImplicitPostExecutions",
+ context.expectations.expectedImplicitPostExecutions.filter(
+ outputSelection.executionsFilter
+ )
+ );
+ }
+ if (outputSelection.expectedExplicitExecutions) {
+ jsonOut = Searializer.tojsonDynArrayExecution(
+ "root",
+ "expectedExplicitExecutions",
+ context.expectations.expectedExplicitExecutions.filter(
+ outputSelection.executionsFilter
+ )
+ );
+ }
+ if (outputSelection.allExpectedExecutions) {
+ jsonOut = Searializer.tojsonDynArrayExecution(
+ "root",
+ "allExpectedExecutions",
+ context.expectations.allExpectedExecutions.filter(
+ outputSelection.executionsFilter
+ )
+ );
+ }
+ if (outputSelection.expectedAvailableOrders) {
+ bool[] memory expectedAvailableOrders = new bool[](
+ context.executionState.orderDetails.length
+ );
+
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ i++
+ ) {
+ expectedAvailableOrders[i] =
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE;
+ }
+
+ jsonOut = Searializer.tojsonDynArrayBool(
+ "root",
+ "expectedAvailableOrders",
+ expectedAvailableOrders
+ );
+ }
+ // =====================================================================//
+ // Events //
+ // =====================================================================//
+ // if (outputSelection.expectedEventHashes) {
+ // jsonOut = Searializer.tojsonDynArrayBytes32(
+ // "root",
+ // "expectedEventHashes",
+ // context.expectedEventHashes
+ // );
+ // }
+ if (outputSelection.actualEvents) {
+ jsonOut = context.actualEvents.serializeTransferLogs(
+ "root",
+ "actualEvents"
+ );
+ }
+ if (outputSelection.expectedEvents) {
+ jsonOut = context
+ .expectations
+ .allExpectedExecutions
+ .serializeTransferLogs("root", "expectedEvents", context);
+ }
+ /*if (outputSelection.returnValues) {
+ jsonOut = Searializer.tojsonReturnValues(
+ "root",
+ "returnValues",
+ context.returnValues
+ );
+ } */
+
+ ExpectedBalances balanceChecker = context.testHelpers.balanceChecker();
+ if (outputSelection.nativeExpectedBalances) {
+ jsonOut = Searializer.tojsonDynArrayNativeAccountDump(
+ "root",
+ "nativeExpectedBalances",
+ balanceChecker.dumpNativeBalances()
+ );
+ }
+ if (outputSelection.erc20ExpectedBalances) {
+ jsonOut = Searializer.tojsonDynArrayERC20TokenDump(
+ "root",
+ "erc20ExpectedBalances",
+ balanceChecker.dumpERC20Balances()
+ );
+ }
+ // if (outputSelection.erc721ExpectedBalances) {
+ // jsonOut = Searializer.tojsonDynArrayERC721TokenDump(
+ // "root",
+ // "erc721ExpectedBalances",
+ // balanceChecker.dumpERC721Balances()
+ // );
+ // }
+ // if (outputSelection.erc1155ExpectedBalances) {
+ // jsonOut = Searializer.tojsonDynArrayERC1155TokenDump(
+ // "root",
+ // "erc1155ExpectedBalances",
+ // balanceChecker.dumpERC1155Balances()
+ // );
+ // }
+ if (outputSelection.validationErrors) {
+ jsonOut = Searializer.tojsonDynArrayValidationErrorsAndWarnings(
+ "root",
+ "validationErrors",
+ context.executionState.validationErrors
+ );
+ }
+ vm.writeJson(jsonOut, "./fuzz_debug.json");
+}
+
+/**
+ * @dev Helper to cast dumpContext to a pure function.
+ */
+function pureDumpContext()
+ pure
+ returns (
+ function(FuzzTestContext memory, ContextOutputSelection memory)
+ internal
+ pure pureFn
+ )
+{
+ function(FuzzTestContext memory, ContextOutputSelection memory)
+ internal viewFn = dumpContext;
+ assembly {
+ pureFn := viewFn
+ }
+}
+
+function cast(OrderStatusEnum[] memory a) pure returns (uint256[] memory b) {
+ assembly {
+ b := a
+ }
+}
+
+/**
+ * @dev Serialize and write transfer related fields from FuzzTestContext to a
+ * `fuzz_debug.json` file.
+ */
+function dumpTransfers(FuzzTestContext memory context) view {
+ ContextOutputSelection memory selection;
+ selection.allExpectedExecutions = true;
+ selection.expectedEvents = true;
+ selection.actualEvents = true;
+ pureDumpContext()(context, selection);
+ console2.log("Dumped transfer data to ./fuzz_debug.json");
+}
+
+/**
+ * @dev Serialize and write execution related fields from FuzzTestContext to a
+ * `fuzz_debug.json` file.
+ */
+function dumpExecutions(FuzzTestContext memory context) view {
+ ContextOutputSelection memory selection;
+ selection.orders = true;
+ selection.orderHashes = true;
+ selection.allExpectedExecutions = true;
+ selection.nativeExpectedBalances = true;
+ selection.expectedAvailableOrders = true;
+ selection.seaport = true;
+ selection.caller = true;
+ selection.callValue = true;
+ selection.maximumFulfilled = true;
+ selection.testHelpers = true;
+ selection.recipient = true;
+ selection.expectedExplicitExecutions = true;
+ selection.expectedImplicitExecutions = true;
+ selection.executionsFilter = ItemType.ERC1155_WITH_CRITERIA; // no filter
+ selection.orders = true;
+ selection.preExecOrderStatuses = true;
+ selection.validationErrors = true;
+ pureDumpContext()(context, selection);
+ console2.log("Dumped executions and balances to ./fuzz_debug.json");
+}
+
+library ExecutionFilterCast {
+ using ExecutionFilterCast for *;
+
+ function filter(
+ Execution[] memory executions,
+ ItemType itemType
+ ) internal pure returns (Execution[] memory) {
+ if (uint256(itemType) > 3) return executions;
+ return
+ ArrayHelpers.filterWithArg.asExecutionsFilterByItemType()(
+ executions,
+ ExecutionFilterCast.isItemType,
+ itemType
+ );
+ }
+
+ function isItemType(
+ Execution memory execution,
+ ItemType itemType
+ ) internal pure returns (bool) {
+ return execution.item.itemType == itemType;
+ }
+
+ function asExecutionsFilterByItemType(
+ function(
+ MemoryPointer,
+ function(MemoryPointer, MemoryPointer) internal pure returns (bool),
+ MemoryPointer
+ ) internal pure returns (MemoryPointer) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(
+ Execution[] memory,
+ function(Execution memory, ItemType)
+ internal
+ pure
+ returns (bool),
+ ItemType
+ ) internal pure returns (Execution[] memory) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/DifferentialTest.sol b/test/foundry/new/helpers/DifferentialTest.sol
new file mode 100644
index 000000000..38e561675
--- /dev/null
+++ b/test/foundry/new/helpers/DifferentialTest.sol
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+contract DifferentialTest is Test {
+ ///@dev error to supply
+ error RevertWithFailureStatus(bool status);
+ error DifferentialTestAssertionFailed();
+
+ // slot where HEVM stores a bool representing whether or not an assertion has failed
+ bytes32 HEVM_FAILED_SLOT = bytes32("failed");
+
+ // hash of the bytes surfaced by `revert RevertWithFailureStatus(false)`
+ bytes32 PASSING_HASH =
+ keccak256(
+ abi.encodeWithSelector(RevertWithFailureStatus.selector, false)
+ );
+
+ ///@dev reverts after function body with HEVM failure status, which clears all state changes
+ /// but still surfaces assertion failure status.
+ modifier stateless() {
+ _;
+ revert RevertWithFailureStatus(readHevmFailureSlot());
+ }
+
+ ///@dev revert if the supplied bytes do not match the expected "passing" revert bytes
+ function assertPass(bytes memory reason) internal view {
+ // hash the reason and compare to the hash of the passing revert bytes
+ if (keccak256(reason) != PASSING_HASH) {
+ revert DifferentialTestAssertionFailed();
+ }
+ }
+
+ ///@dev read the failure slot of the HEVM using the vm.load cheatcode
+ /// Returns true if there was an assertion failure. recorded.
+ function readHevmFailureSlot() internal view returns (bool) {
+ return vm.load(address(vm), HEVM_FAILED_SLOT) == bytes32(uint256(1));
+ }
+}
diff --git a/test/foundry/new/helpers/EIP1271Offerer.sol b/test/foundry/new/helpers/EIP1271Offerer.sol
new file mode 100644
index 000000000..9345db15e
--- /dev/null
+++ b/test/foundry/new/helpers/EIP1271Offerer.sol
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+import { ERC1155Recipient } from "../../utils/ERC1155Recipient.sol";
+
+contract EIP1271Offerer is ERC1155Recipient {
+ error EIP1271OffererInvalidSignature(bytes32 digest, bytes signature);
+ bytes4 private constant _EIP_1271_MAGIC_VALUE = 0x1626ba7e;
+
+ mapping(bytes32 => bytes32) public digestToSignatureHash;
+
+ bool private _returnEmpty = false;
+
+ function registerSignature(bytes32 digest, bytes memory signature) public {
+ digestToSignatureHash[digest] = keccak256(signature);
+ }
+
+ function isValidSignature(
+ bytes32 digest,
+ bytes memory signature
+ ) external view returns (bytes4) {
+ if (_returnEmpty) {
+ return bytes4(0x00000000);
+ }
+
+ bytes32 signatureHash = keccak256(signature);
+ if (digestToSignatureHash[digest] == signatureHash) {
+ return _EIP_1271_MAGIC_VALUE;
+ }
+
+ // TODO: test for bubbled up revert reasons as well
+ assembly {
+ revert(0, 0)
+ }
+ }
+
+ function returnEmpty() external {
+ _returnEmpty = true;
+ }
+
+ function is1271() external pure returns (bool) {
+ return true;
+ }
+
+ receive() external payable {}
+}
diff --git a/test/foundry/new/helpers/EIP712MerkleTree.sol b/test/foundry/new/helpers/EIP712MerkleTree.sol
new file mode 100644
index 000000000..f554303c8
--- /dev/null
+++ b/test/foundry/new/helpers/EIP712MerkleTree.sol
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { MurkyBase } from "murky/common/MurkyBase.sol";
+
+import { Math } from "openzeppelin-contracts/contracts/utils/math/Math.sol";
+
+import { Test } from "forge-std/Test.sol";
+
+import { OrderComponents } from "seaport-sol/SeaportStructs.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import {
+ TypehashDirectory
+} from "../../../../contracts/test/TypehashDirectory.sol";
+
+/**
+ * @dev Seaport doesn't sort leaves when hashing for bulk orders, but Murky
+ * does, so implement a custom hashLeafPairs function
+ */
+contract MerkleUnsorted is MurkyBase {
+ function hashLeafPairs(
+ bytes32 left,
+ bytes32 right
+ ) public pure override returns (bytes32 _hash) {
+ assembly {
+ mstore(0x0, left)
+ mstore(0x20, right)
+ _hash := keccak256(0x0, 0x40)
+ }
+ }
+}
+
+contract EIP712MerkleTree is Test {
+ // data contract to retrieve bulk order typehashes
+ TypehashDirectory internal immutable _typehashDirectory;
+ OrderComponents private emptyOrderComponents;
+ MerkleUnsorted private merkle;
+
+ constructor() {
+ _typehashDirectory = new TypehashDirectory();
+ merkle = new MerkleUnsorted();
+ }
+
+ /**
+ * @dev Creates a single bulk signature: a base signature + a three byte
+ * index + a series of 32 byte proofs. The height of the tree is determined
+ * by the length of the orderComponents array and only fills empty orders
+ * into the tree to make the length a power of 2.
+ */
+ function signBulkOrder(
+ SeaportInterface consideration,
+ uint256 privateKey,
+ OrderComponents[] memory orderComponents,
+ uint24 orderIndex,
+ bool useCompact2098
+ ) public view returns (bytes memory) {
+ // cache the hash of an empty order components struct to fill out any
+ // nodes required to make the length a power of 2
+ bytes32 emptyComponentsHash = consideration.getOrderHash(
+ emptyOrderComponents
+ );
+ // declare vars here to avoid stack too deep errors
+ bytes32[] memory leaves;
+ bytes32 bulkOrderTypehash;
+ // block scope to avoid stacc 2 dank
+ {
+ // height of merkle tree is log2(length), rounded up to next power
+ // of 2
+ uint256 height = Math.log2(orderComponents.length);
+ // Murky won't let you compute a merkle tree with only 1 leaf, so
+ // if height is 0 (length is 1), set height to 1
+ if (2 ** height != orderComponents.length || height == 0) {
+ height += 1;
+ }
+ // get the typehash for a bulk order of this height
+ bulkOrderTypehash = _lookupBulkOrderTypehash(height);
+ // allocate array for leaf hashes
+ leaves = new bytes32[](2 ** height);
+ // hash each original order component
+ for (uint256 i = 0; i < orderComponents.length; i++) {
+ leaves[i] = consideration.getOrderHash(orderComponents[i]);
+ }
+ // fill out empty node hashes
+ for (uint256 i = orderComponents.length; i < 2 ** height; i++) {
+ leaves[i] = emptyComponentsHash;
+ }
+ }
+
+ // get the proof for the order index
+ bytes32[] memory proof = merkle.getProof(leaves, orderIndex);
+ bytes32 root = merkle.getRoot(leaves);
+
+ return
+ _getSignature(
+ consideration,
+ privateKey,
+ bulkOrderTypehash,
+ root,
+ proof,
+ orderIndex,
+ useCompact2098
+ );
+ }
+
+ /**
+ * @dev Creates a single bulk signature: a base signature + a three byte
+ * index + a series of 32 byte proofs. The height of the tree is determined
+ * by the height parameter and this function will fill empty orders into the
+ * tree until the specified height is reached.
+ */
+ function signSparseBulkOrder(
+ SeaportInterface consideration,
+ uint256 privateKey,
+ OrderComponents memory orderComponents,
+ uint256 height,
+ uint24 orderIndex,
+ bool useCompact2098
+ ) public view returns (bytes memory) {
+ require(orderIndex < 2 ** height, "orderIndex out of bounds");
+ // get hash of actual order
+ bytes32 orderHash = consideration.getOrderHash(orderComponents);
+ // get initial empty order components hash
+ bytes32 emptyComponentsHash = consideration.getOrderHash(
+ emptyOrderComponents
+ );
+
+ // calculate intermediate hashes of a sparse order tree
+ // this will also serve as our proof
+ bytes32[] memory emptyHashes = new bytes32[]((height));
+ // first layer is empty order hash
+ emptyHashes[0] = emptyComponentsHash;
+ for (uint256 i = 1; i < height; i++) {
+ bytes32 nextHash;
+ bytes32 lastHash = emptyHashes[i - 1];
+ // subsequent layers are hash of emptyHeight+emptyHeight
+ assembly {
+ mstore(0, lastHash)
+ mstore(0x20, lastHash)
+ nextHash := keccak256(0, 0x40)
+ }
+ emptyHashes[i] = nextHash;
+ }
+ // begin calculating order tree root
+ bytes32 root = orderHash;
+ // hashIndex is the index within the layer of the non-sparse hash
+ uint24 hashIndex = orderIndex;
+
+ for (uint256 i = 0; i < height; i++) {
+ // get sparse hash at this height
+ bytes32 heightEmptyHash = emptyHashes[i];
+ assembly {
+ // if the hashIndex is odd, our "root" is second component
+ if and(hashIndex, 1) {
+ mstore(0, heightEmptyHash)
+ mstore(0x20, root)
+ }
+ // else it is even and our "root" is first component
+ // (this can def be done in a branchless way but who has the
+ // time??)
+ if iszero(and(hashIndex, 1)) {
+ mstore(0, root)
+ mstore(0x20, heightEmptyHash)
+ }
+ // compute new intermediate hash (or final root)
+ root := keccak256(0, 0x40)
+ }
+ // divide hashIndex by 2 to get index of next layer
+ // 0 -> 0
+ // 1 -> 0
+ // 2 -> 1
+ // 3 -> 1
+ // etc
+ hashIndex /= 2;
+ }
+
+ return
+ _getSignature(
+ consideration,
+ privateKey,
+ _lookupBulkOrderTypehash(height),
+ root,
+ emptyHashes,
+ orderIndex,
+ useCompact2098
+ );
+ }
+
+ /**
+ * @dev same lookup seaport optimized does
+ */
+ function _lookupBulkOrderTypehash(
+ uint256 treeHeight
+ ) internal view returns (bytes32 typeHash) {
+ TypehashDirectory directory = _typehashDirectory;
+ assembly {
+ let typeHashOffset := add(1, shl(0x5, sub(treeHeight, 1)))
+ extcodecopy(directory, 0, typeHashOffset, 0x20)
+ typeHash := mload(0)
+ }
+ }
+
+ function _getSignature(
+ SeaportInterface consideration,
+ uint256 privateKey,
+ bytes32 bulkOrderTypehash,
+ bytes32 root,
+ bytes32[] memory proof,
+ uint24 orderIndex,
+ bool useCompact2098
+ ) internal view returns (bytes memory) {
+ // bulkOrder hash is keccak256 of the specific bulk order typehash and
+ // the merkle root of the order hashes
+ bytes32 bulkOrderHash = keccak256(abi.encode(bulkOrderTypehash, root));
+
+ // get domain separator from the particular seaport instance
+ (, bytes32 domainSeparator, ) = consideration.information();
+
+ // declare out here to avoid stack too deep errors
+ bytes memory signature;
+ // avoid stacc 2 thicc
+ {
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(
+ privateKey,
+ keccak256(
+ abi.encodePacked(
+ bytes2(0x1901),
+ domainSeparator,
+ bulkOrderHash
+ )
+ )
+ );
+ // if useCompact2098 is true, encode yParity (v) into s
+ if (useCompact2098) {
+ uint256 yParity = (v == 27) ? 0 : 1;
+ bytes32 yAndS = bytes32(uint256(s) | (yParity << 255));
+ signature = abi.encodePacked(r, yAndS);
+ } else {
+ signature = abi.encodePacked(r, s, v);
+ }
+ }
+
+ // return the packed signature, order index, and proof
+ // encodePacked will pack everything tightly without lengths
+ // ie, long-style rsv signatures will have 1 byte for v
+ // orderIndex will be the next 3 bytes
+ // then proof will be each element one after another; its offset and
+ // length will not be encoded
+ return abi.encodePacked(signature, orderIndex, proof);
+ }
+}
diff --git a/test/foundry/new/helpers/ERC1155Recipient.sol b/test/foundry/new/helpers/ERC1155Recipient.sol
new file mode 100644
index 000000000..8a8313b0f
--- /dev/null
+++ b/test/foundry/new/helpers/ERC1155Recipient.sol
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.17;
+
+import {
+ ERC1155TokenReceiver
+} from "@rari-capital/solmate/src/tokens/ERC1155.sol";
+
+contract ERC1155Recipient is ERC1155TokenReceiver {
+ function onERC1155Received(
+ address,
+ address,
+ uint256,
+ uint256,
+ bytes calldata
+ ) public virtual override returns (bytes4) {
+ return ERC1155TokenReceiver.onERC1155Received.selector;
+ }
+
+ function onERC1155BatchReceived(
+ address,
+ address,
+ uint256[] calldata,
+ uint256[] calldata,
+ bytes calldata
+ ) external virtual override returns (bytes4) {
+ return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
+ }
+}
diff --git a/test/foundry/new/helpers/ERC721Recipient.sol b/test/foundry/new/helpers/ERC721Recipient.sol
new file mode 100644
index 000000000..255775af7
--- /dev/null
+++ b/test/foundry/new/helpers/ERC721Recipient.sol
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.17;
+
+import {
+ ERC721TokenReceiver
+} from "@rari-capital/solmate/src/tokens/ERC721.sol";
+
+contract ERC721Recipient is ERC721TokenReceiver {
+ function onERC721Received(
+ address,
+ address,
+ uint256,
+ bytes calldata
+ ) public virtual override returns (bytes4) {
+ return ERC721TokenReceiver.onERC721Received.selector;
+ }
+}
diff --git a/test/foundry/new/helpers/ExpectedBalances.sol b/test/foundry/new/helpers/ExpectedBalances.sol
new file mode 100644
index 000000000..72fb487a7
--- /dev/null
+++ b/test/foundry/new/helpers/ExpectedBalances.sol
@@ -0,0 +1,897 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.17;
+
+import {
+ EnumerableSet
+} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
+import {
+ EnumerableMap
+} from "openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sol";
+
+import { IERC20 } from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
+
+import {
+ IERC721
+} from "openzeppelin-contracts/contracts/interfaces/IERC721.sol";
+
+import {
+ IERC1155
+} from "openzeppelin-contracts/contracts/interfaces/IERC1155.sol";
+
+import { LibString } from "solady/src/utils/LibString.sol";
+
+import { withLabel } from "./Labeler.sol";
+
+import { Execution, ReceivedItem } from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+struct NativeAccountDump {
+ address account;
+ uint256 balance;
+}
+
+/* struct ERC20AccountDump {
+ address account;
+ uint256 balance;
+} */
+
+struct ERC20TokenDump {
+ address token;
+ // ERC20AccountDump[] accounts;
+ address[] accounts;
+ uint256[] balances;
+}
+
+// struct ERC721AccountDump {
+// address account;
+// uint256[] identifiers;
+// }
+
+struct ERC721TokenDump {
+ address token;
+ address[] accounts;
+ uint256[][] accountIdentifiers;
+}
+
+/* struct ERC1155IdentifierDump {
+ uint256 identifier;
+ uint256 balance;
+}
+ */
+struct ERC1155AccountDump {
+ address account;
+ uint256[] identifiers;
+ uint256[] balances;
+ // ERC1155IdentifierDump[] identifiers;
+}
+
+struct ERC1155TokenDump {
+ address token;
+ ERC1155AccountDump[] accounts;
+ // address[] accounts;
+ // uint256[][] accountIdentifiers;
+ // uint256[][] accountBalances;
+ // ERC1155AccountDump[] accounts;
+}
+
+struct ExpectedBalancesDump {
+ ERC20TokenDump[] erc20;
+ ERC721TokenDump[] erc721;
+ ERC1155TokenDump[] erc1155;
+}
+
+/**
+ * @dev Helper library for generating balance related error messages.
+ */
+library BalanceErrorMessages {
+ function unexpectedAmountErrorMessage(
+ string memory errorSummary,
+ address token,
+ address account,
+ uint256 expected,
+ uint256 actual
+ ) internal pure returns (string memory) {
+ return
+ string.concat(
+ errorSummary,
+ "\n token: ",
+ withLabel(token),
+ "\n account: ",
+ withLabel(account),
+ "\n expected: ",
+ LibString.toString(expected),
+ "\n actual: ",
+ LibString.toString(actual),
+ "\n"
+ );
+ }
+
+ function unexpectedAmountErrorMessage(
+ string memory errorSummary,
+ address token,
+ uint256 identifier,
+ address account,
+ uint256 expected,
+ uint256 actual
+ ) internal pure returns (string memory) {
+ return
+ string.concat(
+ errorSummary,
+ "\n token: ",
+ withLabel(token),
+ "\n identifier: ",
+ LibString.toString(identifier),
+ "\n account: ",
+ withLabel(account),
+ "\n expected: ",
+ LibString.toString(expected),
+ "\n actual: ",
+ LibString.toString(actual),
+ "\n"
+ );
+ }
+
+ function nativeUnexpectedBalance(
+ address account,
+ uint256 expectedBalance,
+ uint256 actualBalance
+ ) internal pure returns (string memory) {
+ return
+ unexpectedAmountErrorMessage(
+ "ExpectedBalances: Unexpected native balance",
+ address(0),
+ account,
+ expectedBalance,
+ actualBalance
+ );
+ }
+
+ function erc20UnexpectedBalance(
+ address token,
+ address account,
+ uint256 expectedBalance,
+ uint256 actualBalance
+ ) internal pure returns (string memory) {
+ return
+ unexpectedAmountErrorMessage(
+ "ExpectedBalances: Unexpected ERC20 balance",
+ token,
+ account,
+ expectedBalance,
+ actualBalance
+ );
+ }
+
+ function erc721UnexpectedBalance(
+ address token,
+ address account,
+ uint256 expectedBalance,
+ uint256 actualBalance
+ ) internal pure returns (string memory) {
+ return
+ unexpectedAmountErrorMessage(
+ "ExpectedBalances: Unexpected ERC721 balance",
+ token,
+ account,
+ expectedBalance,
+ actualBalance
+ );
+ }
+
+ function erc1155UnexpectedBalance(
+ address token,
+ address account,
+ uint256 identifier,
+ uint256 expectedBalance,
+ uint256 actualBalance
+ ) internal pure returns (string memory) {
+ return
+ unexpectedAmountErrorMessage(
+ "ExpectedBalances: Unexpected ERC1155 balance for ID",
+ token,
+ identifier,
+ account,
+ expectedBalance,
+ actualBalance
+ );
+ }
+
+ function insufficientBalance(
+ string memory prefix,
+ address account,
+ address recipient,
+ uint256 balance,
+ uint256 amount,
+ bool derived
+ ) internal pure returns (string memory) {
+ return
+ string.concat(
+ prefix,
+ "\n from: ",
+ withLabel(account),
+ derived ? "\n balance (derived): " : "\n balance (actual): ",
+ LibString.toString(balance),
+ "\n transfer amount: ",
+ LibString.toString(amount),
+ "\n to: ",
+ withLabel(recipient),
+ "\n"
+ );
+ }
+
+ function insufficientNativeBalance(
+ address account,
+ address recipient,
+ uint256 balance,
+ uint256 amount,
+ bool derived
+ ) internal pure returns (string memory) {
+ return
+ insufficientBalance(
+ "ExpectedBalances: Insufficient native balance for transfer",
+ account,
+ recipient,
+ balance,
+ amount,
+ derived
+ );
+ }
+
+ function insufficientERC20Balance(
+ address token,
+ address account,
+ address recipient,
+ uint256 balance,
+ uint256 amount,
+ bool derived
+ ) internal pure returns (string memory) {
+ return
+ insufficientBalance(
+ string.concat(
+ "ExpectedBalances: Insufficient ERC20 balance for transfer\n token: ",
+ withLabel(token)
+ ),
+ account,
+ recipient,
+ balance,
+ amount,
+ derived
+ );
+ }
+
+ function insufficientERC1155Balance(
+ address token,
+ uint256 identifier,
+ address account,
+ address recipient,
+ uint256 balance,
+ uint256 amount,
+ bool derived
+ ) internal pure returns (string memory) {
+ return
+ insufficientBalance(
+ string.concat(
+ "ExpectedBalances: Insufficient ERC1155 balance for transfer\n token: ",
+ withLabel(token),
+ "\n identifier: ",
+ LibString.toString(identifier)
+ ),
+ account,
+ recipient,
+ balance,
+ amount,
+ derived
+ );
+ }
+}
+
+contract Subtractor {
+ string internal tokenKind;
+
+ constructor(string memory _tokenKind) {
+ tokenKind = _tokenKind;
+ }
+}
+
+/**
+ * @dev Helper contract for tracking, checking, and debugging native balances.
+ */
+contract NativeBalances {
+ using EnumerableMap for EnumerableMap.AddressToUintMap;
+
+ EnumerableMap.AddressToUintMap private accountsMap;
+
+ function sub(
+ address account,
+ address recipient,
+ uint256 balance,
+ uint256 amount,
+ bool derived
+ ) private pure returns (uint256) {
+ if (balance < amount) {
+ revert(
+ BalanceErrorMessages.insufficientNativeBalance(
+ account,
+ recipient,
+ balance,
+ amount,
+ derived
+ )
+ );
+ }
+ return balance - amount;
+ }
+
+ function addNativeTransfer(
+ address from,
+ address to,
+ uint256 amount
+ ) public {
+ (bool fromExists, uint256 fromBalance) = accountsMap.tryGet(from);
+ if (!fromExists) {
+ fromBalance = from.balance;
+ }
+ accountsMap.set(from, sub(from, to, fromBalance, amount, fromExists));
+
+ (bool toExists, uint256 toBalance) = accountsMap.tryGet(to);
+ if (!toExists) {
+ toBalance = to.balance;
+ }
+ accountsMap.set(to, toBalance + amount);
+ }
+
+ function checkNativeBalances() internal view {
+ address[] memory accounts = accountsMap.keys();
+ uint256 accountsLength = accounts.length;
+ for (uint256 j; j < accountsLength; j++) {
+ address account = accounts[j];
+ uint256 expectedBalance = accountsMap.get(account);
+ uint256 actualBalance = account.balance;
+ if (expectedBalance != actualBalance) {
+ revert(
+ BalanceErrorMessages.nativeUnexpectedBalance(
+ account,
+ expectedBalance,
+ actualBalance
+ )
+ );
+ }
+ }
+ }
+
+ function dumpNativeBalances()
+ public
+ view
+ returns (NativeAccountDump[] memory accountBalances)
+ {
+ address[] memory accounts = accountsMap.keys();
+ accountBalances = new NativeAccountDump[](accounts.length);
+ for (uint256 i; i < accounts.length; i++) {
+ address account = accounts[i];
+ accountBalances[i] = NativeAccountDump(
+ account,
+ accountsMap.get(account)
+ );
+ }
+ }
+}
+
+/**
+ * @dev Helper contract for tracking, checking, and debugging ERC20 balances.
+ */
+contract ERC20Balances {
+ using EnumerableMap for EnumerableMap.AddressToUintMap;
+ using EnumerableSet for EnumerableSet.AddressSet;
+
+ EnumerableSet.AddressSet private tokens;
+ mapping(address => EnumerableMap.AddressToUintMap) private tokenAccounts;
+
+ function sub(
+ address token,
+ address account,
+ address recipient,
+ uint256 balance,
+ uint256 amount,
+ bool derived
+ ) private pure returns (uint256) {
+ if (balance < amount) {
+ revert(
+ BalanceErrorMessages.insufficientERC20Balance(
+ token,
+ account,
+ recipient,
+ balance,
+ amount,
+ derived
+ )
+ );
+ }
+ return balance - amount;
+ }
+
+ function addERC20Transfer(
+ address token,
+ address from,
+ address to,
+ uint256 amount
+ ) public {
+ tokens.add(token);
+ EnumerableMap.AddressToUintMap storage accounts = tokenAccounts[token];
+
+ (bool fromExists, uint256 fromBalance) = accounts.tryGet(from);
+ if (!fromExists) {
+ fromBalance = IERC20(token).balanceOf(from);
+ }
+ accounts.set(
+ from,
+ sub(token, from, to, fromBalance, amount, fromExists)
+ );
+
+ (bool toExists, uint256 toBalance) = accounts.tryGet(to);
+ if (!toExists) {
+ toBalance = IERC20(token).balanceOf(to);
+ }
+ accounts.set(to, toBalance + amount);
+ }
+
+ function checkERC20Balances() internal view {
+ uint256 length = tokens.length();
+ for (uint256 i; i < length; i++) {
+ address token = tokens.at(i);
+ EnumerableMap.AddressToUintMap storage accountsMap = tokenAccounts[
+ token
+ ];
+ address[] memory accounts = accountsMap.keys();
+ uint256 accountsLength = accounts.length;
+ for (uint256 j; j < accountsLength; j++) {
+ address account = accounts[j];
+ uint256 expectedBalance = accountsMap.get(account);
+ uint256 actualBalance = IERC20(token).balanceOf(account);
+ if (expectedBalance != actualBalance) {
+ revert(
+ BalanceErrorMessages.erc20UnexpectedBalance(
+ token,
+ account,
+ expectedBalance,
+ actualBalance
+ )
+ );
+ }
+ }
+ }
+ }
+
+ function dumpERC20Balances()
+ public
+ view
+ returns (ERC20TokenDump[] memory tokenDumps)
+ {
+ uint256 length = tokens.length();
+ tokenDumps = new ERC20TokenDump[](length);
+ for (uint256 i; i < length; i++) {
+ address token = tokens.at(i);
+ EnumerableMap.AddressToUintMap storage accountsMap = tokenAccounts[
+ token
+ ];
+ address[] memory accounts = accountsMap.keys();
+ ERC20TokenDump memory tokenDump = ERC20TokenDump({
+ token: token,
+ accounts: accounts,
+ balances: new uint256[](accounts.length)
+ });
+ tokenDumps[i] = tokenDump;
+ for (uint256 j; j < accounts.length; j++) {
+ address account = accounts[j];
+ tokenDump.balances[j] = accountsMap.get(account);
+ }
+ }
+ }
+}
+
+/**
+ * @dev Helper contract for tracking, checking, and debugging ERC721 balances.
+ */
+contract ERC721Balances {
+ using EnumerableMap for EnumerableMap.AddressToUintMap;
+ using EnumerableSet for EnumerableSet.AddressSet;
+ using EnumerableSet for EnumerableSet.UintSet;
+
+ struct TokenData721 {
+ mapping(address => EnumerableSet.UintSet) accountIdentifiers;
+ EnumerableSet.UintSet touchedIdentifiers;
+ EnumerableMap.AddressToUintMap accountBalances;
+ }
+ EnumerableSet.AddressSet private tokens;
+ mapping(address => TokenData721) private tokenDatas;
+
+ function addERC721Transfer(
+ address token,
+ address from,
+ address to,
+ uint256 identifier
+ ) public {
+ tokens.add(token);
+ TokenData721 storage tokenData = tokenDatas[token];
+
+ (bool fromExists, uint256 fromBalance) = tokenData
+ .accountBalances
+ .tryGet(from);
+ if (!fromExists) {
+ fromBalance = IERC721(token).balanceOf(from);
+ }
+
+ if (fromBalance == 0) {
+ revert("ERC721Balances: sender does not have a balance");
+ }
+
+ tokenData.accountBalances.set(from, fromBalance - 1);
+
+ (bool toExists, uint256 toBalance) = tokenData.accountBalances.tryGet(
+ to
+ );
+ if (!toExists) {
+ toBalance = IERC721(token).balanceOf(to);
+ }
+ tokenData.accountBalances.set(to, toBalance + 1);
+
+ // If we have not seen the identifier before, assert that the sender owns it
+ if (tokenData.touchedIdentifiers.add(identifier)) {
+ require(
+ IERC721(token).ownerOf(identifier) == from,
+ "ExpectedBalances: sender does not own token"
+ );
+ } else {
+ require(
+ tokenData.accountIdentifiers[from].remove(identifier),
+ "ExpectedBalances: sender does not own token"
+ );
+ }
+
+ require(
+ tokenData.accountIdentifiers[to].add(identifier),
+ "ExpectedBalances: receiver already owns token"
+ );
+ }
+
+ function checkERC721Balances() internal view {
+ address[] memory tokensArray = tokens.values();
+
+ uint256 length = tokensArray.length;
+
+ for (uint256 i; i < length; i++) {
+ address token = tokensArray[i];
+
+ TokenData721 storage tokenData = tokenDatas[token];
+
+ address[] memory accounts = tokenData.accountBalances.keys();
+
+ uint256 accountsLength = accounts.length;
+
+ for (uint256 j; j < accountsLength; j++) {
+ address account = accounts[j];
+
+ {
+ uint256 expectedBalance = tokenData.accountBalances.get(
+ account
+ );
+ uint256 actualBalance = IERC721(token).balanceOf(account);
+ if (actualBalance != expectedBalance) {
+ revert(
+ BalanceErrorMessages.erc721UnexpectedBalance(
+ token,
+ account,
+ expectedBalance,
+ actualBalance
+ )
+ );
+ }
+ }
+
+ uint256[] memory identifiers = tokenData
+ .accountIdentifiers[account]
+ .values();
+
+ uint256 identifiersLength = identifiers.length;
+
+ for (uint256 k; k < identifiersLength; k++) {
+ require(
+ IERC721(token).ownerOf(identifiers[k]) == account,
+ "ExpectedBalances: account does not own expected token"
+ );
+ }
+ }
+ }
+ }
+
+ function dumpERC721Balances()
+ public
+ view
+ returns (ERC721TokenDump[] memory tokenDumps)
+ {
+ address[] memory tokensArray;
+
+ tokenDumps = new ERC721TokenDump[](tokensArray.length);
+
+ for (uint256 i; i < tokensArray.length; i++) {
+ tokenDumps[i] = dumpERC721Token(tokensArray[i]);
+ }
+ }
+
+ function dumpERC721Token(
+ address token
+ ) internal view returns (ERC721TokenDump memory dump) {
+ TokenData721 storage tokenData = tokenDatas[token];
+
+ dump.accounts = tokenData.accountBalances.keys();
+ uint256 accountsLength = dump.accounts.length;
+
+ dump.token = token;
+ for (uint256 i; i < accountsLength; i++) {
+ address account = dump.accounts[i];
+
+ dump.accountIdentifiers[i] = tokenData
+ .accountIdentifiers[account]
+ .values();
+ }
+ }
+}
+
+struct ERC1155TransferDetails {
+ address token;
+ address from;
+ address to;
+ uint256 identifier;
+ uint256 amount;
+}
+
+/**
+ * @dev Helper contract for tracking, checking, and debugging ERC721 balances.
+ */
+contract ERC1155Balances {
+ using EnumerableMap for EnumerableMap.AddressToUintMap;
+ using EnumerableSet for EnumerableSet.AddressSet;
+ using EnumerableMap for EnumerableMap.UintToUintMap;
+
+ struct TokenData1155 {
+ EnumerableSet.AddressSet accounts;
+ mapping(address => EnumerableMap.UintToUintMap) accountIdentifiers;
+ }
+
+ EnumerableSet.AddressSet private tokens;
+ mapping(address => TokenData1155) private tokenDatas;
+
+ function sub(
+ ERC1155TransferDetails memory details,
+ uint256 balance,
+ bool derived
+ ) private pure returns (uint256) {
+ if (balance < details.amount) {
+ revert(
+ BalanceErrorMessages.insufficientERC1155Balance(
+ details.token,
+ details.identifier,
+ details.from,
+ details.to,
+ balance,
+ details.amount,
+ derived
+ )
+ );
+ }
+ return balance - details.amount;
+ }
+
+ function addERC1155Transfer(ERC1155TransferDetails memory details) public {
+ tokens.add(details.token);
+
+ TokenData1155 storage tokenData = tokenDatas[details.token];
+
+ tokenData.accounts.add(details.from);
+ tokenData.accounts.add(details.to);
+
+ {
+ EnumerableMap.UintToUintMap storage fromIdentifiers = tokenData
+ .accountIdentifiers[details.from];
+ (bool fromExists, uint256 fromBalance) = fromIdentifiers.tryGet(
+ details.identifier
+ );
+ if (!fromExists) {
+ fromBalance = IERC1155(details.token).balanceOf(
+ details.from,
+ details.identifier
+ );
+ }
+ fromIdentifiers.set(
+ details.identifier,
+ sub(details, fromBalance, fromExists)
+ );
+ }
+
+ {
+ EnumerableMap.UintToUintMap storage toIdentifiers = tokenData
+ .accountIdentifiers[details.to];
+ (bool toExists, uint256 toBalance) = toIdentifiers.tryGet(
+ details.identifier
+ );
+ if (!toExists) {
+ toBalance = IERC1155(details.token).balanceOf(
+ details.to,
+ details.identifier
+ );
+ }
+ toIdentifiers.set(details.identifier, toBalance + details.amount);
+ }
+ }
+
+ function checkERC1155Balances() internal view {
+ address[] memory tokensArray = tokens.values();
+
+ uint256 length = tokensArray.length;
+
+ // For each token...
+ for (uint256 i; i < length; i++) {
+ address token = tokensArray[i];
+
+ TokenData1155 storage tokenData = tokenDatas[token];
+
+ address[] memory accounts = tokenData.accounts.values();
+
+ uint256 accountsLength = accounts.length;
+
+ // For each account that has interacted with the token...
+ for (uint256 j; j < accountsLength; j++) {
+ address account = accounts[j];
+
+ EnumerableMap.UintToUintMap
+ storage accountIdentifiers = tokenData.accountIdentifiers[
+ account
+ ];
+
+ uint256[] memory identifiers = accountIdentifiers.keys();
+
+ uint256 identifiersLength = identifiers.length;
+
+ // For each identifier the account has interacted with,
+ // assert their balance matches the expected balance.
+ for (uint256 k; k < identifiersLength; k++) {
+ uint256 identifier = identifiers[k];
+ uint256 expectedBalance = accountIdentifiers.get(
+ identifier
+ );
+ uint256 actualBalance = IERC1155(token).balanceOf(
+ account,
+ identifier
+ );
+ if (expectedBalance != actualBalance) {
+ revert(
+ BalanceErrorMessages.erc1155UnexpectedBalance(
+ token,
+ account,
+ identifier,
+ expectedBalance,
+ actualBalance
+ )
+ );
+ }
+ }
+ }
+ }
+ }
+
+ function dumpERC1155Balances()
+ public
+ view
+ returns (ERC1155TokenDump[] memory tokenDumps)
+ {
+ address[] memory tokensArray = tokens.values();
+ uint256 length = tokensArray.length;
+ tokenDumps = new ERC1155TokenDump[](length);
+
+ // For each token...
+ for (uint256 i; i < length; i++) {
+ address token = tokensArray[i];
+ TokenData1155 storage tokenData = tokenDatas[token];
+ uint256 accountsLength = tokenData.accounts.length();
+
+ ERC1155TokenDump memory tokenDump = ERC1155TokenDump({
+ token: token,
+ accounts: new ERC1155AccountDump[](accountsLength)
+ });
+ tokenDumps[i] = tokenDump;
+
+ for (uint256 j; j < accountsLength; j++) {
+ address account = tokenData.accounts.at(j);
+
+ EnumerableMap.UintToUintMap
+ storage accountIdentifiers = tokenData.accountIdentifiers[
+ account
+ ];
+
+ uint256[] memory identifiers = accountIdentifiers.keys();
+
+ uint256 identifiersLength = identifiers.length;
+
+ ERC1155AccountDump memory accountDump = ERC1155AccountDump({
+ account: account,
+ identifiers: new uint256[](identifiersLength),
+ balances: new uint256[](identifiersLength)
+ });
+ tokenDump.accounts[j] = accountDump;
+
+ for (uint256 k; k < identifiersLength; k++) {
+ uint256 identifier = identifiers[k];
+ accountDump.identifiers[k] = identifier;
+ accountDump.balances[k] = accountIdentifiers.get(
+ identifier
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * @dev Combined helper contract for tracking and checking token balances.
+ */
+contract ExpectedBalances is
+ NativeBalances,
+ ERC20Balances,
+ ERC721Balances,
+ ERC1155Balances
+{
+ function addTransfer(Execution calldata execution) public {
+ ReceivedItem memory item = execution.item;
+ if (item.itemType == ItemType.NATIVE) {
+ return
+ addNativeTransfer(
+ execution.offerer,
+ item.recipient,
+ item.amount
+ );
+ }
+ if (item.itemType == ItemType.ERC20) {
+ return
+ addERC20Transfer(
+ item.token,
+ execution.offerer,
+ item.recipient,
+ item.amount
+ );
+ }
+ if (item.itemType == ItemType.ERC721) {
+ return
+ addERC721Transfer(
+ item.token,
+ execution.offerer,
+ item.recipient,
+ item.identifier
+ );
+ }
+ if (item.itemType == ItemType.ERC1155) {
+ return
+ addERC1155Transfer(
+ ERC1155TransferDetails(
+ item.token,
+ execution.offerer,
+ item.recipient,
+ item.identifier,
+ item.amount
+ )
+ );
+ }
+ }
+
+ function addTransfers(Execution[] calldata executions) external {
+ for (uint256 i; i < executions.length; i++) {
+ addTransfer(executions[i]);
+ }
+ }
+
+ function checkBalances() external view {
+ checkNativeBalances();
+ checkERC20Balances();
+ checkERC721Balances();
+ checkERC1155Balances();
+ }
+}
diff --git a/test/foundry/new/helpers/FractionUtil.sol b/test/foundry/new/helpers/FractionUtil.sol
new file mode 100644
index 000000000..c635b84a7
--- /dev/null
+++ b/test/foundry/new/helpers/FractionUtil.sol
@@ -0,0 +1,135 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+enum FractionStatus {
+ INVALID,
+ WHOLE_FILL,
+ WHOLE_FILL_GCD,
+ PARTIAL_FILL,
+ PARTIAL_FILL_GCD
+}
+
+struct FractionResults {
+ uint120 realizedNumerator;
+ uint120 realizedDenominator;
+ uint120 finalFilledNumerator;
+ uint120 finalFilledDenominator;
+ uint120 originalStatusNumerator;
+ uint120 originalStatusDenominator;
+ uint120 requestedNumerator;
+ uint120 requestedDenominator;
+ FractionStatus status;
+}
+
+/**
+ * @dev Helper utilities for calculating partial fill fractions.
+ */
+library FractionUtil {
+ function _gcd(uint256 a, uint256 b) internal pure returns (uint256) {
+ uint256 temp;
+ while (b != 0) {
+ temp = b;
+ b = a % b;
+ a = temp;
+ }
+ return a;
+ }
+
+ function getPartialFillResults(
+ uint120 currentStatusNumerator,
+ uint120 currentStatusDenominator,
+ uint120 numeratorToFill,
+ uint120 denominatorToFill
+ ) internal pure returns (FractionResults memory) {
+ uint256 filledNumerator = uint256(currentStatusNumerator);
+ uint256 filledDenominator = uint256(currentStatusDenominator);
+ uint256 numerator = uint256(numeratorToFill);
+ uint256 denominator = uint256(denominatorToFill);
+ bool partialFill;
+ bool applyGcd;
+
+ // If denominator of 1 supplied, fill all remaining amount on order.
+ if (denominator == 1) {
+ // Scale numerator & denominator to match current denominator.
+ numerator = filledDenominator;
+ denominator = filledDenominator;
+ }
+ // Otherwise, if supplied denominator differs from current one...
+ else if (filledDenominator != denominator) {
+ // scale current numerator by the supplied denominator, then...
+ filledNumerator *= denominator;
+
+ // the supplied numerator & denominator by current denominator.
+ numerator *= filledDenominator;
+ denominator *= filledDenominator;
+ }
+
+ // Once adjusted, if current+supplied numerator exceeds denominator:
+ if (filledNumerator + numerator > denominator) {
+ // Reduce current numerator so it + supplied = denominator.
+ numerator = denominator - filledNumerator;
+
+ partialFill = true;
+ }
+
+ // Increment the filled numerator by the new numerator.
+ filledNumerator += numerator;
+
+ // Ensure fractional amounts are below max uint120.
+ if (
+ filledNumerator > type(uint120).max ||
+ denominator > type(uint120).max
+ ) {
+ applyGcd = true;
+
+ // Derive greatest common divisor using euclidean algorithm.
+ uint256 scaleDown = _gcd(
+ numerator,
+ _gcd(filledNumerator, denominator)
+ );
+
+ // Scale all fractional values down by gcd.
+ numerator = numerator / scaleDown;
+ filledNumerator = filledNumerator / scaleDown;
+ denominator = denominator / scaleDown;
+ }
+
+ if (denominator > type(uint120).max) {
+ return
+ FractionResults({
+ realizedNumerator: 0,
+ realizedDenominator: 0,
+ finalFilledNumerator: 0,
+ finalFilledDenominator: 0,
+ originalStatusNumerator: currentStatusNumerator,
+ originalStatusDenominator: currentStatusDenominator,
+ requestedNumerator: numeratorToFill,
+ requestedDenominator: denominatorToFill,
+ status: FractionStatus.INVALID
+ });
+ }
+ FractionStatus status;
+ if (partialFill && applyGcd) {
+ status = FractionStatus.PARTIAL_FILL_GCD;
+ } else if (partialFill) {
+ status = FractionStatus.PARTIAL_FILL;
+ } else if (applyGcd) {
+ status = FractionStatus.WHOLE_FILL_GCD;
+ } else {
+ status = FractionStatus.WHOLE_FILL;
+ }
+
+ return
+ FractionResults({
+ realizedNumerator: uint120(numerator),
+ realizedDenominator: uint120(denominator),
+ finalFilledNumerator: uint120(filledNumerator),
+ finalFilledDenominator: uint120(denominator),
+ originalStatusNumerator: currentStatusNumerator,
+ originalStatusDenominator: currentStatusDenominator,
+ requestedNumerator: numeratorToFill,
+ requestedDenominator: denominatorToFill,
+ status: status
+ });
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzAmendments.sol b/test/foundry/new/helpers/FuzzAmendments.sol
new file mode 100644
index 000000000..6cf12fa28
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzAmendments.sol
@@ -0,0 +1,553 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import { AdvancedOrderLib } from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ OfferItem,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType, Side } from "seaport-sol/SeaportEnums.sol";
+
+import { FuzzChecks } from "./FuzzChecks.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import { FuzzInscribers } from "./FuzzInscribers.sol";
+
+import { FuzzHelpers } from "./FuzzHelpers.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { CheckHelpers } from "./FuzzSetup.sol";
+
+import {
+ OrderStatusEnum,
+ ContractOrderRebate
+} from "seaport-sol/SpaceEnums.sol";
+
+import {
+ HashCalldataContractOfferer
+} from "../../../../contracts/test/HashCalldataContractOfferer.sol";
+
+import {
+ OffererZoneFailureReason
+} from "../../../../contracts/test/OffererZoneFailureReason.sol";
+
+import { FuzzGeneratorContext } from "./FuzzGeneratorContextLib.sol";
+import { PRNGHelpers } from "./FuzzGenerators.sol";
+
+import {
+ FractionResults,
+ FractionStatus,
+ FractionUtil
+} from "./FractionUtil.sol";
+
+/**
+ * @dev "Amendments" are changes to Seaport state that are required to execute
+ * a given order configuration. Amendments do not modify the orders, test
+ * context, or test environment, but rather set up Seaport state like
+ * order statuses, counters, and contract offerer nonces. Amendments run
+ * after order generation, but before derivers.
+ */
+abstract contract FuzzAmendments is Test {
+ using AdvancedOrderLib for AdvancedOrder[];
+ using AdvancedOrderLib for AdvancedOrder;
+
+ using CheckHelpers for FuzzTestContext;
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzInscribers for AdvancedOrder;
+ using FuzzInscribers for address;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for OrderParameters;
+
+ using PRNGHelpers for FuzzGeneratorContext;
+
+ /**
+ * @dev Configure the contract offerer to provide rebates if required.
+ */
+ function prepareRebates(FuzzTestContext memory context) public {
+ // TODO: make it so it adds / removes / modifies more than a single thing
+ // and create arbitrary new items.
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ OrderParameters memory orderParams = (
+ context.executionState.orders[i].parameters
+ );
+
+ if (orderParams.orderType == OrderType.CONTRACT) {
+ uint256 contractNonce;
+ HashCalldataContractOfferer offerer;
+ {
+ ContractOrderRebate rebate = (
+ context.advancedOrdersSpace.orders[i].rebate
+ );
+
+ if (rebate == ContractOrderRebate.NONE) {
+ continue;
+ }
+
+ offerer = (
+ HashCalldataContractOfferer(
+ payable(orderParams.offerer)
+ )
+ );
+
+ bytes32 orderHash = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+
+ if (rebate == ContractOrderRebate.MORE_OFFER_ITEMS) {
+ offerer.addExtraItemMutation(
+ Side.OFFER,
+ ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: 1,
+ recipient: payable(orderParams.offerer)
+ }),
+ orderHash
+ );
+ } else if (
+ rebate == ContractOrderRebate.MORE_OFFER_ITEM_AMOUNTS
+ ) {
+ uint256 itemIdx = _findFirstNon721Index(
+ orderParams.offer
+ );
+ offerer.addItemAmountMutation(
+ Side.OFFER,
+ itemIdx,
+ orderParams.offer[itemIdx].startAmount + 1,
+ orderHash
+ );
+ } else if (
+ rebate == ContractOrderRebate.LESS_CONSIDERATION_ITEMS
+ ) {
+ offerer.addDropItemMutation(
+ Side.CONSIDERATION,
+ orderParams.consideration.length - 1,
+ orderHash
+ );
+ } else if (
+ rebate ==
+ ContractOrderRebate.LESS_CONSIDERATION_ITEM_AMOUNTS
+ ) {
+ uint256 itemIdx = _findFirstNon721Index(
+ orderParams.consideration
+ );
+ offerer.addItemAmountMutation(
+ Side.CONSIDERATION,
+ itemIdx,
+ orderParams.consideration[itemIdx].startAmount - 1,
+ orderHash
+ );
+ } else {
+ revert("FuzzAmendments: unknown rebate type");
+ }
+
+ uint256 shiftedOfferer = (uint256(
+ uint160(orderParams.offerer)
+ ) << 96);
+ contractNonce = uint256(orderHash) ^ shiftedOfferer;
+ }
+
+ uint256 originalContractNonce = (
+ context.seaport.getContractOffererNonce(orderParams.offerer)
+ );
+
+ // Temporarily adjust the contract nonce and reset it after.
+ orderParams.offerer.inscribeContractOffererNonce(
+ contractNonce,
+ context.seaport
+ );
+
+ (
+ SpentItem[] memory offer,
+ ReceivedItem[] memory consideration
+ ) = offerer.previewOrder(
+ address(context.seaport),
+ context.executionState.caller,
+ _toSpent(orderParams.offer),
+ _toSpent(orderParams.consideration),
+ context.executionState.orders[i].extraData
+ );
+
+ orderParams.offerer.inscribeContractOffererNonce(
+ originalContractNonce,
+ context.seaport
+ );
+
+ context
+ .executionState
+ .previewedOrders[i]
+ .parameters
+ .offer = _toOffer(offer);
+ context
+ .executionState
+ .previewedOrders[i]
+ .parameters
+ .consideration = _toConsideration(consideration);
+ }
+ }
+ }
+
+ /**
+ * @dev Validate orders that should be in "Validated" state.
+ *
+ * @param context The test context.
+ */
+ function validateOrdersAndRegisterCheck(
+ FuzzTestContext memory context
+ ) public {
+ for (
+ uint256 i = 0;
+ i < context.executionState.preExecOrderStatuses.length;
+ ++i
+ ) {
+ if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.VALIDATED
+ ) {
+ bool validated = context
+ .executionState
+ .orders[i]
+ .validateTipNeutralizedOrder(context);
+
+ require(validated, "Failed to validate orders.");
+ }
+ }
+
+ context.registerCheck(FuzzChecks.check_ordersValidated.selector);
+ }
+
+ /**
+ * @dev Set up partial fill fractions for orders.
+ *
+ * @param context The test context.
+ */
+ function setPartialFills(FuzzTestContext memory context) public {
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ if (
+ context.executionState.preExecOrderStatuses[i] !=
+ OrderStatusEnum.PARTIAL
+ ) {
+ continue;
+ }
+
+ AdvancedOrder memory order = context.executionState.orders[i];
+
+ if (
+ order.parameters.orderType != OrderType.PARTIAL_OPEN &&
+ order.parameters.orderType != OrderType.PARTIAL_RESTRICTED
+ ) {
+ revert(
+ "FuzzAmendments: invalid order type for partial fill state"
+ );
+ }
+
+ (uint256 denominator, bool canScaleUp) = order
+ .parameters
+ .getSmallestDenominator();
+
+ // If the denominator is 0 or 1, the order cannot have a partial
+ // fill fraction applied.
+ if (denominator > 1) {
+ // All partially-filled orders are de-facto valid.
+ order.inscribeOrderStatusValidated(true, context.seaport);
+
+ uint256 numerator = context.generatorContext.randRange(
+ 1,
+ canScaleUp ? (denominator - 1) : 1
+ );
+
+ uint256 maxScaleFactor = type(uint120).max / denominator;
+
+ uint256 scaleFactor = context.generatorContext.randRange(
+ 1,
+ maxScaleFactor
+ );
+
+ numerator *= scaleFactor;
+ denominator *= scaleFactor;
+
+ if (
+ numerator == 0 ||
+ denominator < 2 ||
+ numerator >= denominator ||
+ numerator > type(uint120).max ||
+ denominator > type(uint120).max
+ ) {
+ revert("FuzzAmendments: partial fill sanity check failed");
+ }
+
+ order.inscribeOrderStatusNumeratorAndDenominator(
+ uint120(numerator),
+ uint120(denominator),
+ context.seaport
+ );
+
+ // Derive the realized and final fill fractions and status.
+ FractionResults memory fractionResults = (
+ FractionUtil.getPartialFillResults(
+ uint120(numerator),
+ uint120(denominator),
+ order.numerator,
+ order.denominator
+ )
+ );
+
+ // Register the realized and final fill fractions and status.
+ context.expectations.expectedFillFractions[i] = fractionResults;
+
+ // Update "previewed" orders with the realized numerator and
+ // denominator so orderDetails derivation is based on realized.
+ context.executionState.previewedOrders[i].numerator = (
+ fractionResults.realizedNumerator
+ );
+ context.executionState.previewedOrders[i].denominator = (
+ fractionResults.realizedDenominator
+ );
+ } else {
+ // TODO: log these occurrences?
+ context.executionState.preExecOrderStatuses[i] = (
+ OrderStatusEnum.AVAILABLE
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Ensure each order's on chain status matches its generated status.
+ *
+ * @param context The test context.
+ */
+ function conformOnChainStatusToExpected(
+ FuzzTestContext memory context
+ ) public {
+ for (
+ uint256 i = 0;
+ i < context.executionState.preExecOrderStatuses.length;
+ ++i
+ ) {
+ if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.VALIDATED
+ ) {
+ validateOrdersAndRegisterCheck(context);
+ } else if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.CANCELLED_EXPLICIT
+ ) {
+ context.executionState.orders[i].inscribeOrderStatusCancelled(
+ true,
+ context.seaport
+ );
+ } else if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.FULFILLED
+ ) {
+ context
+ .executionState
+ .orders[i]
+ .inscribeOrderStatusNumeratorAndDenominator(
+ 1,
+ 1,
+ context.seaport
+ );
+ } else if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.AVAILABLE
+ ) {
+ context
+ .executionState
+ .orders[i]
+ .inscribeOrderStatusNumeratorAndDenominator(
+ 0,
+ 0,
+ context.seaport
+ );
+ context.executionState.orders[i].inscribeOrderStatusCancelled(
+ false,
+ context.seaport
+ );
+ } else if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.REVERT
+ ) {
+ OrderParameters memory orderParams = context
+ .executionState
+ .orders[i]
+ .parameters;
+ bytes32 orderHash = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+ if (orderParams.orderType != OrderType.CONTRACT) {
+ revert("FuzzAmendments: bad pre-exec order status");
+ }
+
+ HashCalldataContractOfferer(payable(orderParams.offerer))
+ .setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_generateReverts
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Set up offerer's counter value.
+ *
+ * @param context The test context.
+ */
+ function setCounter(FuzzTestContext memory context) public {
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ OrderParameters memory order = (
+ context.executionState.orders[i].parameters
+ );
+
+ if (order.orderType == OrderType.CONTRACT) {
+ continue;
+ }
+
+ uint256 offererSpecificCounter = context.executionState.counter +
+ uint256(uint160(order.offerer));
+
+ order.offerer.inscribeCounter(
+ offererSpecificCounter,
+ context.seaport
+ );
+ }
+ }
+
+ /**
+ * @dev Set up contract offerer's nonce value.
+ *
+ * @param context The test context.
+ */
+ function setContractOffererNonce(FuzzTestContext memory context) public {
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ OrderParameters memory order = (
+ context.executionState.orders[i].parameters
+ );
+
+ if (order.orderType != OrderType.CONTRACT) {
+ continue;
+ }
+
+ uint256 contractOffererSpecificContractNonce = context
+ .executionState
+ .contractOffererNonce + uint256(uint160(order.offerer));
+
+ order.offerer.inscribeContractOffererNonce(
+ contractOffererSpecificContractNonce,
+ context.seaport
+ );
+ }
+ }
+
+ function _findFirstNon721Index(
+ OfferItem[] memory items
+ ) internal pure returns (uint256) {
+ for (uint256 i = 0; i < items.length; ++i) {
+ ItemType itemType = items[i].itemType;
+ if (
+ itemType != ItemType.ERC721 &&
+ itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return i;
+ }
+ }
+
+ revert("FuzzAmendments: could not locate non-721 offer item index");
+ }
+
+ function _findFirstNon721Index(
+ ConsiderationItem[] memory items
+ ) internal pure returns (uint256) {
+ for (uint256 i = 0; i < items.length; ++i) {
+ ItemType itemType = items[i].itemType;
+ if (
+ itemType != ItemType.ERC721 &&
+ itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return i;
+ }
+ }
+
+ revert(
+ "FuzzAmendments: could not locate non-721 consideration item index"
+ );
+ }
+
+ function _toSpent(
+ OfferItem[] memory offer
+ ) internal pure returns (SpentItem[] memory spent) {
+ spent = new SpentItem[](offer.length);
+ for (uint256 i = 0; i < offer.length; ++i) {
+ OfferItem memory item = offer[i];
+ spent[i] = SpentItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifierOrCriteria,
+ amount: item.startAmount
+ });
+ }
+ }
+
+ function _toSpent(
+ ConsiderationItem[] memory consideration
+ ) internal pure returns (SpentItem[] memory spent) {
+ spent = new SpentItem[](consideration.length);
+ for (uint256 i = 0; i < consideration.length; ++i) {
+ ConsiderationItem memory item = consideration[i];
+ spent[i] = SpentItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifier: item.identifierOrCriteria,
+ amount: item.startAmount
+ });
+ }
+ }
+
+ function _toOffer(
+ SpentItem[] memory spent
+ ) internal pure returns (OfferItem[] memory offer) {
+ offer = new OfferItem[](spent.length);
+ for (uint256 i = 0; i < spent.length; ++i) {
+ SpentItem memory item = spent[i];
+ offer[i] = OfferItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifierOrCriteria: item.identifier,
+ startAmount: item.amount,
+ endAmount: item.amount
+ });
+ }
+ }
+
+ function _toConsideration(
+ ReceivedItem[] memory received
+ ) internal pure returns (ConsiderationItem[] memory consideration) {
+ consideration = new ConsiderationItem[](received.length);
+ for (uint256 i = 0; i < received.length; ++i) {
+ ReceivedItem memory item = received[i];
+ consideration[i] = ConsiderationItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifierOrCriteria: item.identifier,
+ startAmount: item.amount,
+ endAmount: item.amount,
+ recipient: item.recipient
+ });
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzChecks.sol b/test/foundry/new/helpers/FuzzChecks.sol
new file mode 100644
index 000000000..4ae9a481a
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzChecks.sol
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import { ExpectedEventsUtil } from "./event-utils/ExpectedEventsUtil.sol";
+
+import { OrderParametersLib } from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ Execution,
+ OrderParameters,
+ OrderType
+} from "seaport-sol/SeaportStructs.sol";
+
+import { OrderStatusEnum, UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import { FuzzHelpers } from "./FuzzHelpers.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import {
+ TestCalldataHashContractOfferer
+} from "../../../../contracts/test/TestCalldataHashContractOfferer.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../../contracts/test/HashValidationZoneOfferer.sol";
+
+/**
+ * @dev Check functions are the post-execution assertions we want to validate.
+ * Checks should be public functions that accept a FuzzTestContext as their
+ * only argument. Checks have access to the post-execution FuzzTestContext
+ * and can use it to make test assertions. The check phase happens last,
+ * immediately after execution.
+ */
+abstract contract FuzzChecks is Test {
+ using OrderParametersLib for OrderParameters;
+
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for AdvancedOrder[];
+
+ address payable testZone;
+ address payable contractOfferer;
+
+ /**
+ * @dev Check that the returned `fulfilled` values were `true`.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_orderFulfilled(FuzzTestContext memory context) public {
+ assertEq(
+ context.returnValues.fulfilled,
+ true,
+ "check_orderFulfilled: not all orders were fulfilled"
+ );
+ }
+
+ /**
+ * @dev Check that the returned `validated` values were `true`.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_orderValidated(FuzzTestContext memory context) public {
+ assertEq(
+ context.returnValues.validated,
+ true,
+ "check_orderValidated: not all orders were validated"
+ );
+ }
+
+ /**
+ * @dev Check that the returned `cancelled` values were `true`.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_orderCancelled(FuzzTestContext memory context) public {
+ assertEq(
+ context.returnValues.cancelled,
+ true,
+ "check_orderCancelled: not all orders were cancelled"
+ );
+ }
+
+ /**
+ * @dev Check that the returned `availableOrders` array length was the
+ * expected length and matches the expected array.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_allOrdersFilled(FuzzTestContext memory context) public {
+ assertEq(
+ context.returnValues.availableOrders.length,
+ context.executionState.orders.length,
+ "check_allOrdersFilled: returnValues.availableOrders.length != orders.length"
+ );
+
+ for (uint256 i; i < context.executionState.orderDetails.length; i++) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ assertEq(
+ context.returnValues.availableOrders[i],
+ true,
+ "check_allOrdersFilled: returnValues.availableOrders[i] false for an available order"
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Check that the zone is getting the right calldata.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_validateOrderExpectedDataHash(
+ FuzzTestContext memory context
+ ) public {
+ // Iterate over the orders.
+ for (uint256 i; i < context.executionState.orders.length; i++) {
+ OrderParameters memory order = context
+ .executionState
+ .orders[i]
+ .parameters;
+
+ // If the order is restricted, check the calldata.
+ if (
+ order.orderType == OrderType.FULL_RESTRICTED ||
+ order.orderType == OrderType.PARTIAL_RESTRICTED
+ ) {
+ testZone = payable(order.zone);
+
+ // Each order has a calldata hash, indexed to orders, that is
+ // expected to be returned by the zone.
+ bytes32 expectedCalldataHash = context
+ .expectations
+ .expectedZoneCalldataHash[i];
+
+ bytes32 orderHash = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+
+ // Use order hash to get the expected calldata hash from zone.
+ // TODO: fix this in cases where contract orders are part of
+ // orderHashes (the hash calculation is most likely incorrect).
+ bytes32 actualCalldataHash = HashValidationZoneOfferer(testZone)
+ .orderHashToValidateOrderDataHash(orderHash);
+
+ // Check that the expected calldata hash matches the actual
+ // calldata hash.
+ assertEq(
+ actualCalldataHash,
+ expectedCalldataHash,
+ "check_validateOrderExpectedDataHash: actualCalldataHash != expectedCalldataHash"
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Check that contract orders were generated and ratified with expected
+ * calldata hashes.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_contractOrderExpectedDataHashes(
+ FuzzTestContext memory context
+ ) public {
+ bytes32[2][] memory expectedCalldataHashes = context
+ .expectations
+ .expectedContractOrderCalldataHashes;
+
+ for (uint256 i; i < context.executionState.orders.length; i++) {
+ AdvancedOrder memory order = context.executionState.orders[i];
+
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ bytes32 orderHash = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+
+ bytes32 expectedGenerateOrderCalldataHash = expectedCalldataHashes[
+ i
+ ][0];
+
+ bytes32 expectedRatifyOrderCalldataHash = expectedCalldataHashes[
+ i
+ ][1];
+
+ contractOfferer = payable(order.parameters.offerer);
+
+ bytes32 actualGenerateOrderCalldataHash = TestCalldataHashContractOfferer(
+ contractOfferer
+ ).orderHashToGenerateOrderDataHash(orderHash);
+
+ bytes32 actualRatifyOrderCalldataHash = TestCalldataHashContractOfferer(
+ contractOfferer
+ ).orderHashToRatifyOrderDataHash(orderHash);
+
+ assertEq(
+ expectedGenerateOrderCalldataHash,
+ actualGenerateOrderCalldataHash,
+ "check_contractOrderExpectedDataHashes: actualGenerateOrderCalldataHash != expectedGenerateOrderCalldataHash"
+ );
+ assertEq(
+ expectedRatifyOrderCalldataHash,
+ actualRatifyOrderCalldataHash,
+ "check_contractOrderExpectedDataHashes: actualRatifyOrderCalldataHash != expectedRatifyOrderCalldataHash"
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Check that the returned `executions` array length is non-zero.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_executionsPresent(FuzzTestContext memory context) public {
+ assertTrue(
+ context.returnValues.executions.length > 0,
+ "check_executionsPresent: returnValues.executions.length == 0"
+ );
+ }
+
+ /**
+ * @dev Check that returned executions match the expected executions.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_executions(FuzzTestContext memory context) public {
+ assertEq(
+ context.returnValues.executions.length,
+ context.expectations.expectedExplicitExecutions.length,
+ "check_executions: expectedExplicitExecutions.length != returnValues.executions.length"
+ );
+
+ for (
+ uint256 i;
+ (i < context.expectations.expectedExplicitExecutions.length &&
+ i < context.returnValues.executions.length);
+ i++
+ ) {
+ Execution memory actual = context.returnValues.executions[i];
+ Execution memory expected = context
+ .expectations
+ .expectedExplicitExecutions[i];
+ assertEq(
+ uint256(actual.item.itemType),
+ uint256(expected.item.itemType),
+ "check_executions: itemType"
+ );
+ assertEq(
+ actual.item.token,
+ expected.item.token,
+ "check_executions: token"
+ );
+ assertEq(
+ actual.item.identifier,
+ expected.item.identifier,
+ "check_executions: identifier"
+ );
+ assertEq(
+ actual.item.amount,
+ expected.item.amount,
+ "check_executions: amount"
+ );
+ assertEq(
+ address(actual.item.recipient),
+ address(expected.item.recipient),
+ "check_executions: recipient"
+ );
+ assertEq(
+ actual.conduitKey,
+ expected.conduitKey,
+ "check_executions: conduitKey"
+ );
+ assertEq(
+ actual.offerer,
+ expected.offerer,
+ "check_executions: offerer"
+ );
+ }
+ }
+
+ /**
+ * @dev Check that expected token transfer events were correctly emitted.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_expectedTransferEventsEmitted(
+ FuzzTestContext memory context
+ ) public {
+ ExpectedEventsUtil.checkExpectedTransferEvents(context);
+ }
+
+ /**
+ * @dev Check that expected Seaport events were correctly emitted.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_expectedSeaportEventsEmitted(
+ FuzzTestContext memory context
+ ) public {
+ ExpectedEventsUtil.checkExpectedSeaportEvents(context);
+ }
+
+ /**
+ * @dev Check that account balance changes (native and tokens) are correct.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_expectedBalances(
+ FuzzTestContext memory context
+ ) public view {
+ context.testHelpers.balanceChecker().checkBalances();
+ }
+
+ /**
+ * @dev Check that the order status is in expected state.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_orderStatusFullyFilled(
+ FuzzTestContext memory context
+ ) public {
+ for (uint256 i; i < context.executionState.orders.length; i++) {
+ AdvancedOrder memory order = context.executionState.orders[i];
+
+ bytes32 orderHash = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+
+ (, , uint256 totalFilled, uint256 totalSize) = context
+ .seaport
+ .getOrderStatus(orderHash);
+
+ if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.FULFILLED
+ ) {
+ assertEq(
+ totalFilled,
+ 1,
+ "check_orderStatusFullyFilled: totalFilled != 1"
+ );
+ assertEq(
+ totalSize,
+ 1,
+ "check_orderStatusFullyFilled: totalSize != 1"
+ );
+ } else if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.PARTIAL
+ ) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ assertEq(
+ totalFilled,
+ context
+ .expectations
+ .expectedFillFractions[i]
+ .finalFilledNumerator,
+ "check_orderStatusFullyFilled: totalFilled != expected partial"
+ );
+ assertEq(
+ totalSize,
+ context
+ .expectations
+ .expectedFillFractions[i]
+ .finalFilledDenominator,
+ "check_orderStatusFullyFilled: totalSize != expected partial"
+ );
+ } else {
+ assertEq(
+ totalFilled,
+ context
+ .expectations
+ .expectedFillFractions[i]
+ .originalStatusNumerator,
+ "check_orderStatusFullyFilled: totalFilled != expected partial (skipped)"
+ );
+ assertEq(
+ totalSize,
+ context
+ .expectations
+ .expectedFillFractions[i]
+ .originalStatusDenominator,
+ "check_orderStatusFullyFilled: totalSize != expected partial (skipped)"
+ );
+ }
+ } else if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ // TODO: This just checks the nonce has been incremented
+ // at least once. It should be incremented once for each
+ // call to `generateOrder`. So, this check should sum the
+ // expected number of calls to `generateOrder` and check
+ // that the nonce has been incremented that many times.
+ uint256 currentNonce = context
+ .seaport
+ .getContractOffererNonce(order.parameters.offerer);
+
+ uint256 contractOffererSpecificContractNonce = context
+ .executionState
+ .contractOffererNonce +
+ uint256(uint160(order.parameters.offerer));
+
+ assertTrue(
+ currentNonce - contractOffererSpecificContractNonce > 0,
+ "FuzzChecks: contract offerer nonce not incremented"
+ );
+ } else {
+ assertEq(
+ totalFilled,
+ order.numerator,
+ "FuzzChecks: totalFilled != numerator"
+ );
+ assertEq(
+ totalSize,
+ order.denominator,
+ "FuzzChecks: totalSize != denominator"
+ );
+ assertTrue(totalSize != 0, "FuzzChecks: totalSize != 0");
+ assertTrue(
+ totalFilled != 0,
+ "FuzzChecks: totalFilled != 0"
+ );
+ }
+ } else {
+ assertTrue(
+ totalFilled == 0,
+ "check_orderStatusFullyFilled: totalFilled != 0"
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Check that validated order status is updated.
+ *
+ * @param context A Fuzz test context.
+ */
+ function check_ordersValidated(FuzzTestContext memory context) public {
+ // Iterate over all orders and if the order was validated pre-execution,
+ // check that calling `getOrderStatus` on the order hash returns `true`
+ // for `isValid`. Note that contract orders cannot be validated.
+ for (
+ uint256 i;
+ i < context.executionState.preExecOrderStatuses.length;
+ i++
+ ) {
+ // Only check orders that were validated pre-execution.
+ if (
+ context.executionState.preExecOrderStatuses[i] ==
+ OrderStatusEnum.VALIDATED
+ ) {
+ bytes32 orderHash = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+ (bool isValid, , , ) = context.seaport.getOrderStatus(
+ orderHash
+ );
+ assertTrue(isValid, "check_ordersValidated: !isValid");
+ }
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzDerivers.sol b/test/foundry/new/helpers/FuzzDerivers.sol
new file mode 100644
index 000000000..8c10b79cb
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzDerivers.sol
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import { assume } from "./VmUtils.sol";
+
+import {
+ AdvancedOrderLib,
+ MatchComponent,
+ MatchComponentType
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import { OrderStatusEnum, UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import { ExecutionHelper } from "seaport-sol/executions/ExecutionHelper.sol";
+
+import {
+ FulfillmentDetails,
+ OrderDetails
+} from "seaport-sol/fulfillments/lib/Structs.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { FuzzHelpers } from "./FuzzHelpers.sol";
+
+import { CriteriaResolverHelper } from "./CriteriaResolverHelper.sol";
+
+import {
+ FulfillmentGeneratorLib
+} from "seaport-sol/fulfillments/lib/FulfillmentLib.sol";
+
+/**
+ * @dev "Derivers" examine generated orders and calculate additional
+ * information based on the order state, like fulfillments and expected
+ * executions. Derivers run after generators and amendments, but before
+ * setup. Deriver functions should take a `FuzzTestContext` as input and
+ * modify it, adding any additional information that might be necessary
+ * for later steps. Derivers should not modify the order state itself,
+ * only the `FuzzTestContext`.
+ */
+library FuzzDerivers {
+ using FuzzEngineLib for FuzzTestContext;
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using FuzzHelpers for AdvancedOrder;
+ using MatchComponentType for MatchComponent[];
+ using ExecutionHelper for FulfillmentDetails;
+ using ExecutionHelper for OrderDetails;
+ using FulfillmentDetailsHelper for FuzzTestContext;
+ using FulfillmentGeneratorLib for OrderDetails[];
+
+ /**
+ * @dev Calculate msg.value from native token amounts in the generated
+ * orders.
+ *
+ * @param context A Fuzz test context.
+ */
+ function withDerivedCallValue(
+ FuzzTestContext memory context
+ ) internal returns (FuzzTestContext memory) {
+ (uint256 value, uint256 minimum) = context.getNativeTokensToSupply();
+
+ context.executionState.value = value;
+ context.expectations.minimumValue = minimum;
+
+ return context;
+ }
+
+ /**
+ * @dev Calculate criteria resolvers for the generated orders.
+ *
+ * @param context A Fuzz test context.
+ */
+ function withDerivedCriteriaResolvers(
+ FuzzTestContext memory context
+ ) internal view returns (FuzzTestContext memory) {
+ CriteriaResolverHelper criteriaResolverHelper = context
+ .testHelpers
+ .criteriaResolverHelper();
+
+ CriteriaResolver[] memory criteriaResolvers = criteriaResolverHelper
+ .deriveCriteriaResolvers(context.executionState.orders);
+
+ context.executionState.criteriaResolvers = criteriaResolvers;
+
+ return context;
+ }
+
+ /**
+ * @dev Calculate OrderDetails for the generated orders.
+ *
+ * @param context A Fuzz test context.
+ */
+ function withDerivedOrderDetails(
+ FuzzTestContext memory context
+ ) internal view returns (FuzzTestContext memory) {
+ UnavailableReason[] memory unavailableReasons = new UnavailableReason[](
+ context.advancedOrdersSpace.orders.length
+ );
+
+ for (uint256 i; i < context.advancedOrdersSpace.orders.length; ++i) {
+ unavailableReasons[i] = context
+ .advancedOrdersSpace
+ .orders[i]
+ .unavailableReason;
+ }
+
+ bytes32[] memory orderHashes = context
+ .executionState
+ .orders
+ .getOrderHashes(address(context.seaport));
+
+ OrderDetails[] memory orderDetails = context
+ .executionState
+ .previewedOrders
+ .getOrderDetails(
+ context.executionState.criteriaResolvers,
+ orderHashes,
+ unavailableReasons
+ );
+
+ context.executionState.orderDetails = orderDetails;
+
+ uint256 totalAvailable;
+
+ // If it's not actually available, but that fact isn't reflected in the
+ // unavailable reason in orderDetails, update orderDetails. This could
+ // probably be removed at some point.
+ for (uint256 i; i < context.executionState.orders.length; ++i) {
+ OrderParameters memory order = context
+ .executionState
+ .orders[i]
+ .parameters;
+ OrderStatusEnum status = context
+ .executionState
+ .preExecOrderStatuses[i];
+
+ // The only one of these that should get hit is the max fulfilled
+ // branch. The rest are just for safety for now and should be
+ // removed at some point.
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ if (!(block.timestamp < order.endTime)) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason.EXPIRED;
+ } else if (!(block.timestamp >= order.startTime)) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason.STARTS_IN_FUTURE;
+ } else if (
+ status == OrderStatusEnum.CANCELLED_EXPLICIT ||
+ status == OrderStatusEnum.CANCELLED_COUNTER
+ ) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason.CANCELLED;
+ } else if (status == OrderStatusEnum.FULFILLED) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason
+ .ALREADY_FULFILLED;
+ } else if (status == OrderStatusEnum.REVERT) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason
+ .GENERATE_ORDER_FAILURE;
+ } else if (
+ !(totalAvailable < context.executionState.maximumFulfilled)
+ ) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason
+ .MAX_FULFILLED_SATISFIED;
+ } else {
+ totalAvailable += 1;
+ }
+ }
+ }
+
+ return context;
+ }
+
+ /**
+ * @dev Derive the `offerFulfillments` and `considerationFulfillments`
+ * arrays or the `fulfillments` array from the `orders` array.
+ *
+ * @param context A Fuzz test context.
+ * @param orderDetails The orders after applying criteria resolvers, amounts
+ * and contract rebates.
+ */
+ function getDerivedFulfillments(
+ FuzzTestContext memory context,
+ OrderDetails[] memory orderDetails
+ )
+ internal
+ pure
+ returns (
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents
+ )
+ {
+ // Note: items do not need corresponding fulfillments for unavailable
+ // orders, but generally will be provided as availability is usually
+ // unknown at submission time. Consider adding a fuzz condition to
+ // supply all or only necessary consideration fulfillment components.
+ (
+ ,
+ offerFulfillments,
+ considerationFulfillments,
+ fulfillments,
+ remainingOfferComponents,
+
+ ) = orderDetails.getFulfillments(
+ context.advancedOrdersSpace.strategy,
+ context.executionState.caller,
+ context.executionState.recipient,
+ context.fuzzParams.seed
+ );
+ }
+
+ /**
+ * @dev Derive the `offerFulfillments` and `considerationFulfillments`
+ * arrays or the `fulfillments` array from the `orders` array and set
+ * the values in context.
+ *
+ * @param context A Fuzz test context.
+ */
+ function withDerivedFulfillments(
+ FuzzTestContext memory context
+ ) internal pure returns (FuzzTestContext memory) {
+ // Derive the required fulfillment arrays.
+ (
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents
+ ) = getDerivedFulfillments(
+ context,
+ context.executionState.orderDetails
+ );
+
+ // For the fulfillAvailable functions, set the offerFullfillments
+ // and considerationFulfillments arrays.
+ context.executionState.offerFulfillments = offerFulfillments;
+ context
+ .executionState
+ .considerationFulfillments = considerationFulfillments;
+
+ // For match, set fulfillment and remaining offer component arrays.
+ context.executionState.fulfillments = fulfillments;
+ context
+ .executionState
+ .remainingOfferComponents = remainingOfferComponents
+ .toFulfillmentComponents();
+
+ return context;
+ }
+
+ /**
+ * @dev Derive implicit and explicit executions for the given orders.
+ *
+ * @param context A Fuzz test context.
+ * @param nativeTokensSupplied quantity of native tokens supplied.
+ */
+ function getDerivedExecutions(
+ FuzzTestContext memory context,
+ uint256 nativeTokensSupplied
+ )
+ internal
+ returns (
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost,
+ uint256 nativeTokensReturned
+ )
+ {
+ // Get the action.
+ bytes4 action = context.action();
+
+ if (
+ action == context.seaport.fulfillOrder.selector ||
+ action == context.seaport.fulfillAdvancedOrder.selector
+ ) {
+ // For the fulfill functions, derive the expected implicit
+ // (standard) executions. There are no explicit executions here
+ // because the caller doesn't pass in fulfillments for these
+ // functions.
+ (implicitExecutionsPost, nativeTokensReturned) = context
+ .toFulfillmentDetails(nativeTokensSupplied)
+ .getStandardExecutions();
+ } else if (
+ action == context.seaport.fulfillBasicOrder.selector ||
+ action ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ // For the fulfillBasic functions, derive the expected implicit
+ // (basic) executions. There are no explicit executions here
+ // because the caller doesn't pass in fulfillments for these
+ // functions.
+ (implicitExecutionsPost, nativeTokensReturned) = context
+ .toFulfillmentDetails(nativeTokensSupplied)
+ .getBasicExecutions();
+ } else if (
+ action == context.seaport.fulfillAvailableOrders.selector ||
+ action == context.seaport.fulfillAvailableAdvancedOrders.selector
+ ) {
+ // For the fulfillAvailable functions, derive the expected implicit
+ // and explicit executions.
+ (
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost,
+ nativeTokensReturned
+ ) = context
+ .toFulfillmentDetails(nativeTokensSupplied)
+ .getFulfillAvailableExecutions(
+ context.executionState.offerFulfillments,
+ context.executionState.considerationFulfillments,
+ context.executionState.orderDetails
+ );
+
+ // TEMP (TODO: handle upstream)
+ assume(
+ explicitExecutions.length > 0,
+ "no_explicit_executions_fulfillAvailable"
+ );
+
+ if (explicitExecutions.length == 0) {
+ revert(
+ "FuzzDerivers: no explicit execs derived - fulfillAvailable"
+ );
+ }
+ } else if (
+ action == context.seaport.matchOrders.selector ||
+ action == context.seaport.matchAdvancedOrders.selector
+ ) {
+ // For the match functions, derive the expected implicit and
+ // explicit executions.
+ (
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost,
+ nativeTokensReturned
+ ) = context
+ .toFulfillmentDetails(nativeTokensSupplied)
+ .getMatchExecutions(context.executionState.fulfillments);
+ }
+ }
+
+ function getDerivedExecutionsFromDirectInputs(
+ FuzzTestContext memory context,
+ FulfillmentDetails memory details,
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments
+ )
+ internal
+ returns (
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost,
+ uint256 nativeTokensReturned
+ )
+ {
+ if (
+ context.action() == context.seaport.fulfillOrder.selector ||
+ context.action() == context.seaport.fulfillAdvancedOrder.selector
+ ) {
+ // For the fulfill functions, derive the expected implicit
+ // (standard) executions. There are no explicit executions here
+ // because the caller doesn't pass in fulfillments for these
+ // functions.
+ (implicitExecutionsPost, nativeTokensReturned) = details
+ .getStandardExecutions();
+ } else if (
+ context.action() == context.seaport.fulfillBasicOrder.selector ||
+ context.action() ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ // For the fulfillBasic functions, derive the expected implicit
+ // (basic) executions. There are no explicit executions here
+ // because the caller doesn't pass in fulfillments for these
+ // functions.
+ (implicitExecutionsPost, nativeTokensReturned) = details
+ .getBasicExecutions();
+ } else if (
+ context.action() ==
+ context.seaport.fulfillAvailableOrders.selector ||
+ context.action() ==
+ context.seaport.fulfillAvailableAdvancedOrders.selector
+ ) {
+ // For the fulfillAvailable functions, derive the expected implicit
+ // and explicit executions.
+ (
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost,
+ nativeTokensReturned
+ ) = details.getFulfillAvailableExecutions(
+ offerFulfillments,
+ considerationFulfillments,
+ context.executionState.orderDetails
+ );
+
+ // TEMP (TODO: handle upstream)
+ assume(
+ explicitExecutions.length > 0,
+ "no_explicit_executions_fulfillAvailable_direct_in"
+ );
+
+ if (explicitExecutions.length == 0) {
+ revert(
+ "FuzzDerivers: no explicit execs (direct) - fulfillAvailable"
+ );
+ }
+ } else if (
+ context.action() == context.seaport.matchOrders.selector ||
+ context.action() == context.seaport.matchAdvancedOrders.selector
+ ) {
+ // For the match functions, derive the expected implicit and
+ // explicit executions.
+ (
+ explicitExecutions,
+ implicitExecutionsPre,
+ implicitExecutionsPost,
+ nativeTokensReturned
+ ) = details.getMatchExecutions(fulfillments);
+
+ // TEMP (TODO: handle upstream)
+ assume(
+ explicitExecutions.length > 0,
+ "no_explicit_executions_match_direct_in"
+ );
+
+ if (explicitExecutions.length == 0) {
+ revert("FuzzDerivers: no explicit executions (direct) - match");
+ }
+ }
+ }
+
+ function getExecutionsFromRegeneratedFulfillments(
+ FuzzTestContext memory context,
+ FulfillmentDetails memory details
+ )
+ internal
+ returns (
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost,
+ uint256 nativeTokensReturned
+ )
+ {
+ // Derive the required fulfillment arrays.
+ (
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
+ Fulfillment[] memory fulfillments,
+
+ ) = getDerivedFulfillments(context, details.orders);
+
+ return
+ getDerivedExecutionsFromDirectInputs(
+ context,
+ details,
+ offerFulfillments,
+ considerationFulfillments,
+ fulfillments
+ );
+ }
+
+ /**
+ * @dev Derive the `expectedImplicitExecutions` and
+ * `expectedExplicitExecutions` arrays from the `orders` array.
+ *
+ * @param context A Fuzz test context.
+ */
+ function withDerivedExecutions(
+ FuzzTestContext memory context
+ ) internal returns (FuzzTestContext memory) {
+ (
+ Execution[] memory explicitExecutions,
+ Execution[] memory implicitExecutionsPre,
+ Execution[] memory implicitExecutionsPost,
+ uint256 nativeTokensReturned
+ ) = getDerivedExecutions(context, context.executionState.value);
+ context
+ .expectations
+ .expectedImplicitPreExecutions = implicitExecutionsPre;
+ context
+ .expectations
+ .expectedImplicitPostExecutions = implicitExecutionsPost;
+ context.expectations.expectedExplicitExecutions = explicitExecutions;
+ context
+ .expectations
+ .expectedNativeTokensReturned = nativeTokensReturned;
+
+ bytes4 action = context.action();
+ if (
+ action == context.seaport.fulfillAvailableOrders.selector ||
+ action == context.seaport.fulfillAvailableAdvancedOrders.selector ||
+ action == context.seaport.matchOrders.selector ||
+ action == context.seaport.matchAdvancedOrders.selector
+ ) {
+ uint256 expectedImpliedNativeExecutions = 0;
+
+ for (uint256 i = 0; i < implicitExecutionsPost.length; ++i) {
+ ReceivedItem memory item = implicitExecutionsPost[i].item;
+ if (item.itemType == ItemType.NATIVE) {
+ expectedImpliedNativeExecutions += item.amount;
+ }
+ }
+
+ if (expectedImpliedNativeExecutions < nativeTokensReturned) {
+ revert("FuzzDeriver: invalid expected implied native value");
+ }
+
+ context.expectations.expectedImpliedNativeExecutions =
+ expectedImpliedNativeExecutions -
+ nativeTokensReturned;
+ }
+
+ return context;
+ }
+}
+
+library FulfillmentDetailsHelper {
+ using AdvancedOrderLib for AdvancedOrder[];
+
+ function toFulfillmentDetails(
+ FuzzTestContext memory context,
+ uint256 nativeTokensSupplied
+ ) internal view returns (FulfillmentDetails memory fulfillmentDetails) {
+ address caller = context.executionState.caller == address(0)
+ ? address(this)
+ : context.executionState.caller;
+ address recipient = context.executionState.recipient == address(0)
+ ? caller
+ : context.executionState.recipient;
+
+ return
+ FulfillmentDetails({
+ orders: context.executionState.orderDetails,
+ recipient: payable(recipient),
+ fulfiller: payable(caller),
+ nativeTokensSupplied: nativeTokensSupplied,
+ fulfillerConduitKey: context.executionState.fulfillerConduitKey,
+ seaport: address(context.seaport)
+ });
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzEngine.sol b/test/foundry/new/helpers/FuzzEngine.sol
new file mode 100644
index 000000000..96ce46cb2
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzEngine.sol
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Vm } from "forge-std/Vm.sol";
+
+import { dumpExecutions } from "./DebugUtil.sol";
+
+import {
+ AdvancedOrderLib,
+ FulfillAvailableHelper,
+ MatchFulfillmentHelper,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ BasicOrderParameters,
+ Execution,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import {
+ ConduitControllerInterface
+} from "seaport-sol/ConduitControllerInterface.sol";
+
+import {
+ ConduitControllerInterface
+} from "seaport-sol/ConduitControllerInterface.sol";
+
+import { BaseOrderTest } from "../BaseOrderTest.sol";
+
+import {
+ FuzzGeneratorContext,
+ FuzzGeneratorContextLib
+} from "./FuzzGeneratorContextLib.sol";
+
+import {
+ FuzzTestContext,
+ FuzzTestContextLib,
+ FuzzParams,
+ MutationState
+} from "./FuzzTestContextLib.sol";
+
+import {
+ AdvancedOrdersSpace,
+ AdvancedOrdersSpaceGenerator,
+ TestStateGenerator
+} from "./FuzzGenerators.sol";
+
+import { FuzzAmendments } from "./FuzzAmendments.sol";
+
+import { FuzzChecks } from "./FuzzChecks.sol";
+
+import { FuzzDerivers } from "./FuzzDerivers.sol";
+
+import { FuzzExecutor } from "./FuzzExecutor.sol";
+
+import { FuzzMutations } from "./FuzzMutations.sol";
+
+import { FuzzMutationSelectorLib } from "./FuzzMutationSelectorLib.sol";
+
+import { MutationEligibilityLib } from "./FuzzMutationHelpers.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import { FuzzHelpers, Structure } from "./FuzzHelpers.sol";
+
+import { CheckHelpers, FuzzSetup } from "./FuzzSetup.sol";
+
+import { ExpectedEventsUtil } from "./event-utils/ExpectedEventsUtil.sol";
+
+import { logMutation } from "./Metrics.sol";
+
+import {
+ ErrorsAndWarnings,
+ ValidationConfiguration
+} from "../../../../contracts/helpers/order-validator/SeaportValidator.sol";
+
+import {
+ IssueStringHelpers
+} from "../../../../contracts/helpers/order-validator/lib/SeaportValidatorTypes.sol";
+
+/**
+ * @notice Base test contract for FuzzEngine. Fuzz tests should inherit this.
+ * Includes the setup and helper functions from BaseOrderTest.
+ *
+ * The BaseOrderTest used in this fuzz engine is not the same as the
+ * BaseOrderTest contract used in the legacy tests. The relative path
+ * for the relevant version is `test/foundry/new/BaseOrderTest.sol`.
+ *
+ * Running test_fuzz_generateOrders in FuzzMain triggers the following
+ * lifecycle:
+ *
+ * 1. Generation - `generate`
+ * First, the engine generates a pseudorandom `FuzzTestContext` from
+ * the randomized `FuzzParams`. See `FuzzGenerators.sol` for the helper
+ * libraries used to construct orders from the Seaport state space.
+ *
+ * The `generate` function in this file calls out to the `generate`
+ * functions in `TestStateGenerator` (responsible for setting up the
+ * order components) and `AdvancedOrdersSpaceGenerator` (responsible for
+ * setting up the orders and actions). The generation phase relies on a
+ * `FuzzGeneratorContext` internally, but it returns a `FuzzTestContext`
+ * struct, which is used throughout the rest of the lifecycle.
+ *
+ * 2. Amendment - `amendOrderState`
+ * Next, the engine runs "amendments," which mutate the state of the
+ * orders. See `FuzzAmendments.sol` for the amendment helper library.
+ *
+ * The `amendOrderState` function in this file serves as a central
+ * location to slot in calls to functions that amend the state of the
+ * orders. For example, calling `validate` on an order.
+ *
+ * 3. Derivation - `runDerivers`
+ * Next up are "derivers," functions that calculate additional values
+ * like fulfillments and executions from the generated orders. See
+ * `FuzzDerivers.sol` for the deriver helper library. Derivers don't
+ * mutate order state, but do add new values to the `FuzzTestContext`.
+ *
+ * The `runDerivers` function in this file serves as a central location
+ * to slot in calls to functions that deterministically derive values
+ * from the state that was created in the generation phase.
+ *
+ * 4. Setup - `runSetup`
+ * This phase sets up any necessary conditions for a test to pass,
+ * including minting test tokens and setting up the required approvals.
+ * The setup phase also detects and registers relevant expectations
+ * to verify after executing the selected Seaport action.
+ *
+ * The `runSetup` function should hold everything that mutates test
+ * environment state, such as minting and approving tokens. It also
+ * contains the logic for setting up the expectations for the
+ * post-execution state of the test. Logic for handling unavailable
+ * orders and balance checking should also live here. Setup phase
+ * helpers are in `FuzzSetup.sol`.
+ *
+ * 5. Check Registration - `runCheckRegistration`
+ * The `runCheckRegistration` function should hold everything that
+ * registers checks but does not belong naturally elsewhere. Checks
+ * can be registered throughout the lifecycle, but unless there's a
+ * natural reason to place them inline elsewhere in the lifecycle, they
+ * should go in a helper in `runCheckRegistration`.
+ *
+ * 6. Execution - `execFailure` and `execSuccess`
+ * The execution phase runs the selected Seaport action and saves the
+ * returned values to the `FuzzTestContext`. See `FuzzExecutor.sol` for
+ * the executor helper contract.
+ *
+ * Execution has two phases: `execFailure` and `execSuccess`. For each
+ * generated order, we test both a failure and success case. To test the
+ * failure case, the engine selects and applies a random mutation to the
+ * order and verifies that it reverts with an expected error. We then
+ * proceed to the success case, where we execute a successful call to
+ * Seaport and save return values to the test context.
+ *
+ * 7. Checks - `checkAll`
+ * Finally, the checks phase runs all registered checks to ensure that
+ * the post-execution state matches all expectations registered during
+ * the setup phase.
+ *
+ * The `checkAll` function runs all of the checks that were registered
+ * throughout the test lifecycle. This is where the engine makes actual
+ * assertions about the effects of a specific test, based on the post
+ * execution state. The engine registers different checks during test
+ * setup depending on the order state, like verifying token transfers,
+ * and account balances, expected events, and expected calldata for
+ * contract orders. To add a new check, add a function to `FuzzChecks`
+ * and then register it with `registerCheck`.
+ *
+ *
+ */
+contract FuzzEngine is
+ BaseOrderTest,
+ FuzzAmendments,
+ FuzzSetup,
+ FuzzChecks,
+ FuzzExecutor,
+ FulfillAvailableHelper,
+ MatchFulfillmentHelper
+{
+ // Use the various builder libraries. These allow for creating structs in a
+ // more readable way.
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ using CheckHelpers for FuzzTestContext;
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for AdvancedOrder[];
+ using FuzzTestContextLib for FuzzTestContext;
+ using FuzzDerivers for FuzzTestContext;
+ using FuzzMutationSelectorLib for FuzzTestContext;
+
+ using IssueStringHelpers for uint16;
+ using IssueStringHelpers for uint16[];
+
+ Vm.Log[] internal _logs;
+ FuzzMutations internal mutations;
+
+ function setLogs(Vm.Log[] memory logs) external {
+ delete _logs;
+ for (uint256 i = 0; i < logs.length; ++i) {
+ _logs.push(logs[i]);
+ }
+ }
+
+ function getLogs() external view returns (Vm.Log[] memory logs) {
+ logs = new Vm.Log[](_logs.length);
+ for (uint256 i = 0; i < _logs.length; ++i) {
+ logs[i] = _logs[i];
+ }
+ }
+
+ function setUp() public virtual override {
+ super.setUp();
+ mutations = new FuzzMutations();
+ }
+
+ /**
+ * @dev Generate a randomized `FuzzTestContext` from fuzz parameters and run
+ * a `FuzzEngine` test.
+ *
+ * @param fuzzParams A FuzzParams struct containing fuzzed values.
+ */
+ function run(FuzzParams memory fuzzParams) internal {
+ FuzzTestContext memory context = generate(fuzzParams);
+ run(context);
+ }
+
+ /**
+ * @dev Run a `FuzzEngine` test with the provided FuzzTestContext. Calls the
+ * following test lifecycle functions in order:
+ *
+ * 1. amendOrderState: Amend the order state.
+ * 2. runDerivers: Run deriver functions for the test.
+ * 3. runSetup: Run setup functions for the test.
+ * 4. runCheckRegistration: Register checks for the test.
+ * 5. execFailure: Mutate orders and call a function expecting failure.
+ * 6. execSuccess: Call a Seaport function expecting success.
+ * 7. checkAll: Call all registered checks.
+ *
+ * @param context A Fuzz test context.
+ */
+ function run(FuzzTestContext memory context) internal {
+ amendOrderState(context);
+ runDerivers(context);
+ runSetup(context);
+ runCheckRegistration(context);
+ validate(context);
+ execFailure(context);
+ execSuccess(context);
+ checkAll(context);
+ }
+
+ /**
+ * @dev Generate a randomized `FuzzTestContext` from fuzz parameters.
+ *
+ * @param fuzzParams A FuzzParams struct containing fuzzed values.
+ */
+ function generate(
+ FuzzParams memory fuzzParams
+ ) internal returns (FuzzTestContext memory) {
+ // JAN_1_2023_UTC
+ vm.warp(1672531200);
+
+ // Get the conduit controller, which allows dpeloying and managing
+ // conduits. Conduits are used to transfer tokens between accounts.
+ ConduitControllerInterface conduitController_ = getConduitController();
+
+ // Set up a default FuzzGeneratorContext. Note that this is only used
+ // for the generation pphase. The `FuzzTestContext` is used throughout
+ // the rest of the lifecycle.
+ FuzzGeneratorContext memory generatorContext = FuzzGeneratorContextLib
+ .from({
+ vm: vm,
+ seaport: getSeaport(),
+ conduitController: conduitController_,
+ erc20s: erc20s,
+ erc721s: erc721s,
+ erc1155s: erc1155s
+ });
+
+ // Generate a pseudorandom order space. The `AdvancedOrdersSpace` is
+ // made up of an `OrderComponentsSpace` array and an `isMatchable` bool.
+ // Each `OrderComponentsSpace` is a struct with fields that are enums
+ // (or arrays of enums) from `SpaceEnums.sol`. In other words, the
+ // `AdvancedOrdersSpace` is a container for a set of constrained
+ // possibilities.
+ AdvancedOrdersSpace memory space = TestStateGenerator.generate(
+ fuzzParams.totalOrders,
+ fuzzParams.maxOfferItems,
+ fuzzParams.maxConsiderationItems,
+ fuzzParams.seedInput,
+ generatorContext
+ );
+
+ generatorContext.caller = AdvancedOrdersSpaceGenerator.generateCaller(
+ space,
+ generatorContext
+ );
+
+ // Generate orders from the space. These are the actual orders that will
+ // be used in the test.
+ AdvancedOrder[] memory orders = AdvancedOrdersSpaceGenerator.generate(
+ space,
+ generatorContext
+ );
+
+ FuzzTestContext memory context = FuzzTestContextLib
+ .from({ orders: orders, seaport: getSeaport() })
+ .withConduitController(conduitController_)
+ .withSeaportValidator(validator)
+ .withFuzzParams(fuzzParams)
+ .withMaximumFulfilled(space.maximumFulfilled)
+ .withPreExecOrderStatuses(space)
+ .withCounter(generatorContext.counter);
+
+ // This is on a separate line to avoid stack too deep.
+ context = context
+ .withContractOffererNonce(generatorContext.contractOffererNonce)
+ .withCaller(generatorContext.caller)
+ .withFulfillerConduitKey(
+ AdvancedOrdersSpaceGenerator.generateFulfillerConduitKey(
+ space,
+ generatorContext
+ )
+ )
+ .withGeneratorContext(generatorContext)
+ .withSpace(space);
+
+ // If it's an advanced order, generate and add a top-level recipient.
+ if (
+ orders.getStructure(address(context.seaport)) == Structure.ADVANCED
+ ) {
+ context = context.withRecipient(
+ AdvancedOrdersSpaceGenerator.generateRecipient(
+ space,
+ generatorContext
+ )
+ );
+ }
+
+ return context;
+ }
+
+ /**
+ * @dev Amend the order state.
+ *
+ * @param context A Fuzz test context.
+ */
+ function amendOrderState(FuzzTestContext memory context) internal {
+ setPartialFills(context);
+ conformOnChainStatusToExpected(context);
+ // Redundant for now, because the counter and nonce are set in the
+ // generation phase.
+ setCounter(context);
+ setContractOffererNonce(context);
+ prepareRebates(context);
+ }
+
+ /**
+ * @dev Perform any "deriver" steps necessary before calling `runSetup`.
+ * Each `withDerived` function calculates a value from the generated
+ * orders and adds it to the test context.
+ *
+ * 1. withDerivedCriteriaResolvers: calculate criteria resolvers
+ * 2. withDerivedOrderDetails: calculate order details
+ * 3. withDetectedRemainders: detect and calculate remainders
+ * 4. withDerivedFulfillments: calculate expected fulfillments
+ * 5. withDerivedCallValue: calculate expected call value
+ * 6. withDerivedExecutions: expected implicit/explicit executions
+ * 7. withDerivedOrderDetails: calculate order details
+ *
+ * @param context A Fuzz test context.
+ */
+ function runDerivers(FuzzTestContext memory context) internal {
+ context = context
+ .withDerivedCriteriaResolvers()
+ .withDerivedOrderDetails()
+ .withDetectedRemainders()
+ .withDerivedFulfillments()
+ .withDerivedCallValue()
+ .withDerivedExecutions()
+ .withDerivedOrderDetails();
+ }
+
+ /**
+ * @dev Perform any setup steps necessary before execution
+ *
+ * 1. setUpZoneParameters: calculate expected zone hashes and set up
+ * zone related checks for restricted orders.
+ * 2. setUpContractOfferers: configure test contract offerers.
+ * 3. setUpOfferItems: Create and approve offer items for each order.
+ * 4. setUpConsiderationItems: Create and approve consideration items
+ * for each order.
+ *
+ * @param context A Fuzz test context.
+ */
+ function runSetup(FuzzTestContext memory context) internal {
+ setUpZoneParameters(context);
+ setUpContractOfferers(context);
+ setUpOfferItems(context);
+ setUpConsiderationItems(context);
+ }
+
+ /**
+ * @dev Register additional checks for the test.
+ *
+ * 1. registerExpectedEventsAndBalances: checks for expected token
+ * transfer events and account balance changes.
+ * 2. registerCommonChecks: register common checks applied to all test
+ * cases: expected Seaport events and post-exec order status.
+ * 4. registerFunctionSpecificChecks: register additional function
+ * specific checks based on the selected action.
+ *
+ * @param context A Fuzz test context.
+ */
+ function runCheckRegistration(FuzzTestContext memory context) internal {
+ registerExpectedEventsAndBalances(context);
+ registerCommonChecks(context);
+ registerFunctionSpecificChecks(context);
+ }
+
+ /**
+ * @dev Mutate an order, call Seaport, and verify the failure.
+ * `execFailure` is a generative test of its own: the engine selects
+ * a mutation, applies it to the order, calls Seaport, and verifies
+ * that the call reverts as expected.
+ *
+ * Mutations, helpers, and expected errors are defined in
+ * `FuzzMutations.sol`.
+ *
+ * @param context A Fuzz test context.
+ */
+ function execFailure(FuzzTestContext memory context) internal {
+ (
+ string memory name,
+ bytes4 mutationSelector,
+ bytes memory expectedRevertReason,
+ MutationState memory mutationState
+ ) = context.selectMutation();
+
+ logMutation(name);
+
+ bytes memory callData = abi.encodeWithSelector(
+ mutationSelector,
+ context,
+ mutationState
+ );
+ (bool success, bytes memory data) = address(mutations).call(callData);
+
+ assertFalse(
+ success,
+ string.concat("Mutation ", name, " did not revert")
+ );
+
+ if (
+ data.length == 4 &&
+ abi.decode(abi.encodePacked(data, uint224(0)), (bytes4)) ==
+ MutationEligibilityLib.NoEligibleIndexFound.selector
+ ) {
+ assertTrue(
+ false,
+ string.concat(
+ "No eligible element index found to apply failure '",
+ name,
+ "'"
+ )
+ );
+ }
+
+ // NOTE: some reverts in the reference contracts do not revert with
+ // the same revert reason as the optimized. Consider a more granular
+ // approach than this one.
+ string memory profile = vm.envOr("MOAT_PROFILE", string("optimized"));
+ if (!stringEq(profile, "reference")) {
+ assertEq(
+ data,
+ expectedRevertReason,
+ string.concat(
+ "Mutation ",
+ name,
+ " did not revert with the expected reason"
+ )
+ );
+ }
+ }
+
+ /**
+ * @dev Validate the generated orders using SeaportValidator and save the
+ * validation errors to the test context.
+ *
+ * @param context A Fuzz test context.
+ */
+ function validate(FuzzTestContext memory context) internal {
+ for (uint256 i; i < context.executionState.orders.length; ++i) {
+ Order memory order = context.executionState.orders[i].toOrder();
+ context.executionState.validationErrors[i] = context
+ .seaportValidator
+ .isValidOrderWithConfiguration(
+ ValidationConfiguration({
+ seaport: address(context.seaport),
+ primaryFeeRecipient: address(0),
+ primaryFeeBips: 0,
+ checkCreatorFee: false,
+ skipStrictValidation: true,
+ shortOrderDuration: 30 minutes,
+ distantOrderExpiration: 26 weeks
+ }),
+ order
+ );
+ }
+ }
+
+ /**
+ * @dev Call a Seaport function with the generated order, expecting success.
+ *
+ * @param context A Fuzz test context.
+ */
+ function execSuccess(FuzzTestContext memory context) internal {
+ ExpectedEventsUtil.startRecordingLogs();
+ exec(context, true);
+ }
+
+ /**
+ * @dev Perform a "check," i.e. a post-execution assertion we want to
+ * validate. Checks should be public functions that accept a
+ * FuzzTestContext as their only argument. Checks have access to the
+ * post-execution FuzzTestContext and can use it to make test
+ * assertions.
+ *
+ * Since we delegatecall ourself, checks must be public functions on
+ * this contract. It's a good idea to prefix them with "check_" as a
+ * naming convention, although it doesn't actually matter.
+ *
+ * Shared check functions are defined in `FuzzChecks.sol`.
+ *
+ * @param context A Fuzz test context.
+ * @param selector bytes4 selector of the check function to call.
+ */
+ function check(FuzzTestContext memory context, bytes4 selector) internal {
+ (bool success, bytes memory result) = address(this).delegatecall(
+ abi.encodeWithSelector(selector, context)
+ );
+
+ if (!success) {
+ dumpExecutions(context);
+ if (result.length == 0) revert();
+ assembly {
+ revert(add(0x20, result), mload(result))
+ }
+ }
+ }
+
+ /**
+ * @dev Perform all checks registered in the context.checks array.
+ *
+ * @param context A Fuzz test context.
+ */
+ function checkAll(FuzzTestContext memory context) internal {
+ for (uint256 i; i < context.checks.length; ++i) {
+ bytes4 selector = context.checks[i];
+ check(context, selector);
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzEngineLib.sol b/test/foundry/new/helpers/FuzzEngineLib.sol
new file mode 100644
index 000000000..cbfcba325
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzEngineLib.sol
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrderLib,
+ MatchComponent,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ Execution,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ SpentItem,
+ ReceivedItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { OrderDetails } from "seaport-sol/fulfillments/lib/Structs.sol";
+
+import { ItemType, Side, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import {
+ _locateCurrentAmount,
+ Family,
+ FuzzHelpers,
+ Structure
+} from "./FuzzHelpers.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { FuzzDerivers } from "./FuzzDerivers.sol";
+
+import {
+ FulfillmentGeneratorLib
+} from "seaport-sol/fulfillments/lib/FulfillmentLib.sol";
+
+/**
+ * @notice Stateless helpers for FuzzEngine. The FuzzEngine uses functions in
+ * this library to select which Seaport action it should call.
+ */
+library FuzzEngineLib {
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+ using FulfillmentGeneratorLib for OrderDetails[];
+
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for AdvancedOrder[];
+ using FuzzDerivers for FuzzTestContext;
+
+ /**
+ * @dev Select an available "action," i.e. "which Seaport function to call,"
+ * based on the orders in a given FuzzTestContext. Selects a random
+ * action using the context's fuzzParams.seed when multiple actions are
+ * available for the given order config.
+ *
+ * @param context A Fuzz test context.
+ * @return bytes4 selector of a SeaportInterface function.
+ */
+ function action(
+ FuzzTestContext memory context
+ ) internal view returns (bytes4) {
+ if (context.actionSelected) return context._action;
+ bytes4[] memory _actions = actions(context);
+ context.actionSelected = true;
+ return (context._action = _actions[
+ context.fuzzParams.seed % _actions.length
+ ]);
+ }
+
+ /**
+ * @dev Get the human-readable name of the selected action.
+ *
+ * @param context A Fuzz test context.
+ * @return string name of the selected action.
+ */
+ function actionName(
+ FuzzTestContext memory context
+ ) internal view returns (string memory) {
+ bytes4 selector = action(context);
+ if (selector == 0xe7acab24) return "fulfillAdvancedOrder";
+ if (selector == 0x87201b41) return "fulfillAvailableAdvancedOrders";
+ if (selector == 0xed98a574) return "fulfillAvailableOrders";
+ if (selector == 0xfb0f3ee1) return "fulfillBasicOrder";
+ if (selector == 0x00000000) return "fulfillBasicOrder_efficient_6GL6yc";
+ if (selector == 0xb3a34c4c) return "fulfillOrder";
+ if (selector == 0xf2d12b12) return "matchAdvancedOrders";
+ if (selector == 0xa8174404) return "matchOrders";
+
+ revert("Unknown selector");
+ }
+
+ /**
+ * @dev Get an array of all possible "actions," i.e. "which Seaport
+ * functions can we call," based on the generated orders in a given
+ * `FuzzTestContext`.
+ *
+ * @param context A Fuzz test context.
+ * @return bytes4[] of SeaportInterface function selectors.
+ */
+ function actions(
+ FuzzTestContext memory context
+ ) internal view returns (bytes4[] memory) {
+ Family family = context.executionState.orders.getFamily();
+
+ bool invalidOfferItemsLocated = mustUseMatch(context);
+
+ Structure structure = context.executionState.orders.getStructure(
+ address(context.seaport)
+ );
+
+ bool hasUnavailable = context.executionState.maximumFulfilled <
+ context.executionState.orders.length;
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason !=
+ UnavailableReason.AVAILABLE
+ ) {
+ hasUnavailable = true;
+ break;
+ }
+ }
+
+ if (hasUnavailable) {
+ if (invalidOfferItemsLocated) {
+ revert(
+ "FuzzEngineLib: invalid native token + unavailable combination"
+ );
+ }
+
+ if (structure == Structure.ADVANCED) {
+ bytes4[] memory selectors = new bytes4[](1);
+ selectors[0] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ return selectors;
+ } else {
+ bytes4[] memory selectors = new bytes4[](2);
+ selectors[0] = context.seaport.fulfillAvailableOrders.selector;
+ selectors[1] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ return selectors;
+ }
+ }
+
+ if (family == Family.SINGLE && !invalidOfferItemsLocated) {
+ if (structure == Structure.BASIC) {
+ bytes4[] memory selectors = new bytes4[](6);
+ selectors[0] = context.seaport.fulfillOrder.selector;
+ selectors[1] = context.seaport.fulfillAdvancedOrder.selector;
+ selectors[2] = context.seaport.fulfillBasicOrder.selector;
+ selectors[3] = context
+ .seaport
+ .fulfillBasicOrder_efficient_6GL6yc
+ .selector;
+ selectors[4] = context.seaport.fulfillAvailableOrders.selector;
+ selectors[5] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ return selectors;
+ }
+
+ if (structure == Structure.STANDARD) {
+ bytes4[] memory selectors = new bytes4[](4);
+ selectors[0] = context.seaport.fulfillOrder.selector;
+ selectors[1] = context.seaport.fulfillAdvancedOrder.selector;
+ selectors[2] = context.seaport.fulfillAvailableOrders.selector;
+ selectors[3] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ return selectors;
+ }
+
+ if (structure == Structure.ADVANCED) {
+ bytes4[] memory selectors = new bytes4[](2);
+ selectors[0] = context.seaport.fulfillAdvancedOrder.selector;
+ selectors[1] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ return selectors;
+ }
+ }
+
+ bool cannotMatch = (context.executionState.hasRemainders ||
+ hasUnavailable);
+
+ if (cannotMatch && invalidOfferItemsLocated) {
+ revert("FuzzEngineLib: cannot fulfill provided combined order");
+ }
+
+ if (cannotMatch) {
+ if (structure == Structure.ADVANCED) {
+ bytes4[] memory selectors = new bytes4[](1);
+ selectors[0] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ return selectors;
+ } else {
+ bytes4[] memory selectors = new bytes4[](2);
+ selectors[0] = context.seaport.fulfillAvailableOrders.selector;
+ selectors[1] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ //selectors[2] = context.seaport.cancel.selector;
+ //selectors[3] = context.seaport.validate.selector;
+ return selectors;
+ }
+ } else if (invalidOfferItemsLocated) {
+ if (structure == Structure.ADVANCED) {
+ bytes4[] memory selectors = new bytes4[](1);
+ selectors[0] = context.seaport.matchAdvancedOrders.selector;
+ return selectors;
+ } else {
+ bytes4[] memory selectors = new bytes4[](2);
+ selectors[0] = context.seaport.matchOrders.selector;
+ selectors[1] = context.seaport.matchAdvancedOrders.selector;
+ return selectors;
+ }
+ } else {
+ if (structure == Structure.ADVANCED) {
+ bytes4[] memory selectors = new bytes4[](2);
+ selectors[0] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ selectors[1] = context.seaport.matchAdvancedOrders.selector;
+ return selectors;
+ } else {
+ bytes4[] memory selectors = new bytes4[](4);
+ selectors[0] = context.seaport.fulfillAvailableOrders.selector;
+ selectors[1] = context
+ .seaport
+ .fulfillAvailableAdvancedOrders
+ .selector;
+ selectors[2] = context.seaport.matchOrders.selector;
+ selectors[3] = context.seaport.matchAdvancedOrders.selector;
+ //selectors[4] = context.seaport.cancel.selector;
+ //selectors[5] = context.seaport.validate.selector;
+ return selectors;
+ }
+ }
+ }
+
+ /**
+ * @dev Determine whether a matching function (either `matchOrders` or
+ * `matchAdvancedOrders`) will be selected, based on the given order
+ * configuration.
+ *
+ * @param context A Fuzz test context.
+ * @return bool whether a matching function will be called.
+ */
+ function mustUseMatch(
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ OrderDetails[] memory orders = context.executionState.orderDetails;
+
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderDetails memory order = orders[i];
+
+ if (order.isContract) {
+ continue;
+ }
+
+ for (uint256 j = 0; j < order.offer.length; ++j) {
+ if (order.offer[j].itemType == ItemType.NATIVE) {
+ return true;
+ }
+ }
+ }
+
+ if (context.executionState.caller == context.executionState.recipient) {
+ return false;
+ }
+
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderDetails memory order = orders[i];
+
+ for (uint256 j = 0; j < order.offer.length; ++j) {
+ SpentItem memory item = order.offer[j];
+
+ if (item.itemType != ItemType.ERC721) {
+ continue;
+ }
+
+ for (uint256 k = 0; k < orders.length; ++k) {
+ OrderDetails memory comparisonOrder = orders[k];
+ for (
+ uint256 l = 0;
+ l < comparisonOrder.consideration.length;
+ ++l
+ ) {
+ ReceivedItem memory considerationItem = comparisonOrder
+ .consideration[l];
+
+ if (
+ considerationItem.itemType == ItemType.ERC721 &&
+ considerationItem.identifier == item.identifier &&
+ considerationItem.token == item.token
+ ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @dev Determine the amount of native tokens the caller must supply.
+ *
+ * @param context A Fuzz test context.
+ * @return value The amount of native tokens to supply.
+ * @return minimum The minimum amount of native tokens to supply.
+ */
+ function getNativeTokensToSupply(
+ FuzzTestContext memory context
+ ) internal returns (uint256 value, uint256 minimum) {
+ bool isMatch = action(context) ==
+ context.seaport.matchAdvancedOrders.selector ||
+ action(context) == context.seaport.matchOrders.selector;
+
+ uint256 valueToCreditBack = 0;
+
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ OrderDetails memory order = context.executionState.orderDetails[i];
+ OrderParameters memory orderParams = context
+ .executionState
+ .previewedOrders[i]
+ .parameters;
+
+ if (isMatch) {
+ for (uint256 j = 0; j < order.offer.length; ++j) {
+ SpentItem memory item = order.offer[j];
+
+ if (
+ item.itemType == ItemType.NATIVE &&
+ orderParams.orderType != OrderType.CONTRACT
+ ) {
+ value += item.amount;
+ }
+ }
+ } else {
+ for (uint256 j = 0; j < order.offer.length; ++j) {
+ SpentItem memory item = order.offer[j];
+
+ if (item.itemType == ItemType.NATIVE) {
+ if (orderParams.orderType == OrderType.CONTRACT) {
+ valueToCreditBack += item.amount;
+ }
+ value += item.amount;
+ }
+ }
+
+ for (uint256 j = 0; j < order.consideration.length; ++j) {
+ ReceivedItem memory item = order.consideration[j];
+
+ if (item.itemType == ItemType.NATIVE) {
+ value += item.amount;
+ }
+ }
+ }
+ }
+
+ if (valueToCreditBack >= value) {
+ value = 0;
+ } else {
+ value = value - valueToCreditBack;
+ }
+
+ minimum = getMinimumNativeTokensToSupply(context);
+
+ if (minimum > value) {
+ value = minimum;
+ }
+ }
+
+ function getMinimumNativeTokensToSupply(
+ FuzzTestContext memory context
+ ) internal returns (uint256) {
+ bytes4 _action = action(context);
+ if (
+ _action == context.seaport.fulfillBasicOrder.selector ||
+ _action ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ // TODO: handle OOR orders or items just in case
+ if (
+ context.executionState.orderDetails[0].offer[0].itemType ==
+ ItemType.ERC20
+ ) {
+ // Basic order bids cannot supply any native tokens
+ return 0;
+ }
+ }
+
+ uint256 hugeCallValue = uint256(type(uint128).max);
+ (, , , uint256 nativeTokensReturned) = context.getDerivedExecutions(
+ hugeCallValue
+ );
+
+ if (nativeTokensReturned > hugeCallValue) {
+ return 0;
+ }
+
+ return hugeCallValue - nativeTokensReturned;
+ }
+
+ /**
+ * @dev Determine whether or not an order configuration has remainders.
+ */
+ function withDetectedRemainders(
+ FuzzTestContext memory context
+ ) internal pure returns (FuzzTestContext memory) {
+ (, , MatchComponent[] memory remainders) = context
+ .executionState
+ .orderDetails
+ .getMatchedFulfillments();
+
+ context.executionState.hasRemainders = remainders.length != 0;
+
+ return context;
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzExecutor.sol b/test/foundry/new/helpers/FuzzExecutor.sol
new file mode 100644
index 000000000..ac4956246
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzExecutor.sol
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import {
+ AdvancedOrderLib,
+ FulfillAvailableHelper,
+ MatchFulfillmentHelper,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ BasicOrderParameters,
+ Execution,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import {
+ FuzzTestContext,
+ FuzzTestContextLib,
+ FuzzParams
+} from "./FuzzTestContextLib.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+import { FuzzHelpers } from "./FuzzHelpers.sol";
+
+import { logCall } from "./Metrics.sol";
+import { dumpExecutions } from "./DebugUtil.sol";
+
+/**
+ * @notice Abstract FuzzEngine helper contract responsible for executing the
+ * selected Seaport action.
+ */
+abstract contract FuzzExecutor is Test {
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzHelpers for AdvancedOrder[];
+ using FuzzTestContextLib for FuzzTestContext;
+
+ /**
+ * @dev Call an available Seaport function based on the orders in the given
+ * FuzzTestContext. FuzzEngine will deduce which actions are available
+ * for the given orders and call a Seaport function at random using the
+ * context's `fuzzParams.seed`.
+ *
+ * 1. Log the call to a call metrics file.
+ * 2. If a caller address is set in the context, prank the address.
+ * 3. Call the selected Seaport function, passing any additional data
+ * necessary from the test context.
+ * 4. Store the return value of the call in the context.
+ *
+ * @param context A Fuzz test context.
+ */
+ function exec(FuzzTestContext memory context, bool logCalls) public {
+ // // Activate this to help with debugging
+ // dumpExecutions(context);
+
+ // Get the action to execute. The action is derived from the fuzz seed,
+ // so it will be the same for each run of the test throughout the entire
+ // lifecycle of the test.
+ bytes4 _action = context.action();
+
+ // Execute the action.
+ if (_action == context.seaport.fulfillOrder.selector) {
+ logCall("fulfillOrder", logCalls);
+ AdvancedOrder memory order = context.executionState.orders[0];
+
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ context.returnValues.fulfilled = context.seaport.fulfillOrder{
+ value: context.executionState.value
+ }(order.toOrder(), context.executionState.fulfillerConduitKey);
+ } else if (_action == context.seaport.fulfillAdvancedOrder.selector) {
+ logCall("fulfillAdvancedOrder", logCalls);
+ AdvancedOrder memory order = context.executionState.orders[0];
+
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ context.returnValues.fulfilled = context
+ .seaport
+ .fulfillAdvancedOrder{ value: context.executionState.value }(
+ order,
+ context.executionState.criteriaResolvers,
+ context.executionState.fulfillerConduitKey,
+ context.executionState.recipient
+ );
+ } else if (_action == context.seaport.fulfillBasicOrder.selector) {
+ logCall("fulfillBasicOrder", logCalls);
+
+ BasicOrderParameters memory basicOrderParameters = context
+ .executionState
+ .orders[0]
+ .toBasicOrderParameters(
+ context.executionState.orders[0].getBasicOrderType()
+ );
+
+ basicOrderParameters.fulfillerConduitKey = context
+ .executionState
+ .fulfillerConduitKey;
+
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ context.returnValues.fulfilled = context.seaport.fulfillBasicOrder{
+ value: context.executionState.value
+ }(basicOrderParameters);
+ } else if (
+ _action ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ logCall("fulfillBasicOrder_efficient", logCalls);
+
+ BasicOrderParameters memory basicOrderParameters = context
+ .executionState
+ .orders[0]
+ .toBasicOrderParameters(
+ context.executionState.orders[0].getBasicOrderType()
+ );
+
+ basicOrderParameters.fulfillerConduitKey = context
+ .executionState
+ .fulfillerConduitKey;
+
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ context.returnValues.fulfilled = context
+ .seaport
+ .fulfillBasicOrder_efficient_6GL6yc{
+ value: context.executionState.value
+ }(basicOrderParameters);
+ } else if (_action == context.seaport.fulfillAvailableOrders.selector) {
+ logCall("fulfillAvailableOrders", logCalls);
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ (
+ bool[] memory availableOrders,
+ Execution[] memory executions
+ ) = context.seaport.fulfillAvailableOrders{
+ value: context.executionState.value
+ }(
+ context.executionState.orders.toOrders(),
+ context.executionState.offerFulfillments,
+ context.executionState.considerationFulfillments,
+ context.executionState.fulfillerConduitKey,
+ context.executionState.maximumFulfilled
+ );
+
+ context.returnValues.availableOrders = availableOrders;
+ context.returnValues.executions = executions;
+ } else if (
+ _action == context.seaport.fulfillAvailableAdvancedOrders.selector
+ ) {
+ logCall("fulfillAvailableAdvancedOrders", logCalls);
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ (
+ bool[] memory availableOrders,
+ Execution[] memory executions
+ ) = context.seaport.fulfillAvailableAdvancedOrders{
+ value: context.executionState.value
+ }(
+ context.executionState.orders,
+ context.executionState.criteriaResolvers,
+ context.executionState.offerFulfillments,
+ context.executionState.considerationFulfillments,
+ context.executionState.fulfillerConduitKey,
+ context.executionState.recipient,
+ context.executionState.maximumFulfilled
+ );
+
+ context.returnValues.availableOrders = availableOrders;
+ context.returnValues.executions = executions;
+ } else if (_action == context.seaport.matchOrders.selector) {
+ logCall("matchOrders", logCalls);
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ Execution[] memory executions = context.seaport.matchOrders{
+ value: context.executionState.value
+ }(
+ context.executionState.orders.toOrders(),
+ context.executionState.fulfillments
+ );
+
+ context.returnValues.executions = executions;
+ } else if (_action == context.seaport.matchAdvancedOrders.selector) {
+ logCall("matchAdvancedOrders", logCalls);
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ Execution[] memory executions = context.seaport.matchAdvancedOrders{
+ value: context.executionState.value
+ }(
+ context.executionState.orders,
+ context.executionState.criteriaResolvers,
+ context.executionState.fulfillments,
+ context.executionState.recipient
+ );
+
+ context.returnValues.executions = executions;
+ } else if (_action == context.seaport.cancel.selector) {
+ logCall("cancel", logCalls);
+ AdvancedOrder[] memory orders = context.executionState.orders;
+ OrderComponents[] memory orderComponents = new OrderComponents[](
+ orders.length
+ );
+
+ for (uint256 i; i < orders.length; ++i) {
+ AdvancedOrder memory order = orders[i];
+ orderComponents[i] = order
+ .toOrder()
+ .parameters
+ .toOrderComponents(context.executionState.counter);
+ }
+
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ context.returnValues.cancelled = context.seaport.cancel(
+ orderComponents
+ );
+ } else if (_action == context.seaport.validate.selector) {
+ logCall("validate", logCalls);
+ if (context.executionState.caller != address(0))
+ vm.prank(context.executionState.caller);
+ context.returnValues.validated = context.seaport.validate(
+ context.executionState.orders.toOrders()
+ );
+ } else {
+ revert("FuzzEngine: Action not implemented");
+ }
+ }
+
+ function exec(FuzzTestContext memory context) public {
+ exec(context, false);
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzGeneratorContextLib.sol b/test/foundry/new/helpers/FuzzGeneratorContextLib.sol
new file mode 100644
index 000000000..4d37c732c
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzGeneratorContextLib.sol
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Vm } from "forge-std/Vm.sol";
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import { MatchComponent } from "seaport-sol/SeaportSol.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ Amount,
+ BasicOrderCategory,
+ Criteria,
+ TokenIndex
+} from "seaport-sol/SpaceEnums.sol";
+
+import { OfferItemSpace } from "seaport-sol/StructSpace.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { EIP1271Offerer } from "./EIP1271Offerer.sol";
+
+import {
+ ConduitControllerInterface
+} from "seaport-sol/ConduitControllerInterface.sol";
+
+import { Account } from "../BaseOrderTest.sol";
+
+import { TestHelpers } from "./FuzzTestContextLib.sol";
+
+import { TestERC20 } from "../../../../contracts/test/TestERC20.sol";
+
+import { TestERC721 } from "../../../../contracts/test/TestERC721.sol";
+
+import { TestERC1155 } from "../../../../contracts/test/TestERC1155.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../../contracts/test/HashValidationZoneOfferer.sol";
+
+import {
+ HashCalldataContractOfferer
+} from "../../../../contracts/test/HashCalldataContractOfferer.sol";
+
+import { Conduit } from "../../../../contracts/conduit/Conduit.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../../contracts/test/HashValidationZoneOfferer.sol";
+
+import { setLabel } from "./Labeler.sol";
+
+struct TestConduit {
+ address addr;
+ bytes32 key;
+}
+
+struct FuzzGeneratorContext {
+ Vm vm;
+ TestHelpers testHelpers;
+ LibPRNG.PRNG prng;
+ uint256 timestamp;
+ SeaportInterface seaport;
+ ConduitControllerInterface conduitController;
+ HashValidationZoneOfferer validatorZone;
+ HashCalldataContractOfferer contractOfferer;
+ EIP1271Offerer eip1271Offerer;
+ TestERC20[] erc20s;
+ TestERC721[] erc721s;
+ TestERC1155[] erc1155s;
+ address self;
+ address caller;
+ Account alice;
+ Account bob;
+ Account carol;
+ Account dillon;
+ Account eve;
+ Account frank;
+ TestConduit[] conduits;
+ uint256 starting721offerIndex;
+ uint256 starting721considerationIndex;
+ uint256[] potential1155TokenIds;
+ BasicOrderCategory basicOrderCategory;
+ OfferItemSpace basicOfferSpace;
+ uint256 counter;
+ uint256 contractOffererNonce;
+}
+
+library FuzzGeneratorContextLib {
+ /**
+ * @dev Create a new, empty FuzzGeneratorContext. This function is used
+ * mostly in tests. To create a usable context, use `from` instead.
+ */
+ function empty() internal returns (FuzzGeneratorContext memory) {
+ LibPRNG.PRNG memory prng = LibPRNG.PRNG({ state: 0 });
+
+ uint256[] memory potential1155TokenIds = new uint256[](3);
+ potential1155TokenIds[0] = 1;
+ potential1155TokenIds[1] = 2;
+ potential1155TokenIds[2] = 3;
+
+ TestHelpers testHelpers = TestHelpers(address(this));
+
+ return
+ FuzzGeneratorContext({
+ vm: Vm(address(0)),
+ seaport: SeaportInterface(address(0)),
+ conduitController: ConduitControllerInterface(address(0)),
+ erc20s: new TestERC20[](0),
+ erc721s: new TestERC721[](0),
+ erc1155s: new TestERC1155[](0),
+ prng: prng,
+ testHelpers: testHelpers,
+ timestamp: block.timestamp,
+ validatorZone: new HashValidationZoneOfferer(address(0)),
+ contractOfferer: new HashCalldataContractOfferer(address(0)),
+ eip1271Offerer: new EIP1271Offerer(),
+ self: address(this),
+ caller: address(this),
+ alice: testHelpers.makeAccount("alice"),
+ bob: testHelpers.makeAccount("bob"),
+ carol: testHelpers.makeAccount("carol"),
+ dillon: testHelpers.makeAccount("dillon"),
+ eve: testHelpers.makeAccount("eve"),
+ frank: testHelpers.makeAccount("frank"),
+ conduits: new TestConduit[](2),
+ starting721offerIndex: 0,
+ starting721considerationIndex: 0,
+ potential1155TokenIds: potential1155TokenIds,
+ basicOrderCategory: BasicOrderCategory.NONE,
+ basicOfferSpace: OfferItemSpace(
+ ItemType.NATIVE,
+ TokenIndex.ONE,
+ Criteria.MERKLE,
+ Amount.FIXED
+ ),
+ counter: 0,
+ contractOffererNonce: 0
+ });
+ }
+
+ /**
+ * @dev Create a new FuzzGeneratorContext from the given parameters.
+ */
+ function from(
+ Vm vm,
+ SeaportInterface seaport,
+ ConduitControllerInterface conduitController,
+ TestERC20[] memory erc20s,
+ TestERC721[] memory erc721s,
+ TestERC1155[] memory erc1155s
+ ) internal returns (FuzzGeneratorContext memory) {
+ // Get a new PRNG lib.Account
+ LibPRNG.PRNG memory prng = LibPRNG.PRNG({ state: 0 });
+
+ // Create a list of potential 1155 token IDs.
+ uint256[] memory potential1155TokenIds = new uint256[](3);
+ potential1155TokenIds[0] = 1;
+ potential1155TokenIds[1] = 2;
+ potential1155TokenIds[2] = 3;
+
+ // Create a new TestHelpers instance. The helpers get passed around the
+ // test suite through the context.
+ TestHelpers testHelpers = TestHelpers(address(this));
+
+ // Set up the conduits.
+ TestConduit[] memory conduits = new TestConduit[](2);
+ conduits[0] = _createConduit(conduitController, seaport, uint96(1));
+ conduits[1] = _createConduit(conduitController, seaport, uint96(2));
+
+ HashValidationZoneOfferer validatorZone = new HashValidationZoneOfferer(
+ address(0)
+ );
+ HashCalldataContractOfferer contractOfferer = new HashCalldataContractOfferer(
+ address(seaport)
+ );
+ EIP1271Offerer eip1271Offerer = new EIP1271Offerer();
+
+ setLabel(address(validatorZone), "validatorZone");
+ setLabel(address(contractOfferer), "contractOfferer");
+ setLabel(address(eip1271Offerer), "eip1271Offerer");
+
+ return
+ FuzzGeneratorContext({
+ vm: vm,
+ seaport: seaport,
+ conduitController: conduitController,
+ erc20s: erc20s,
+ erc721s: erc721s,
+ erc1155s: erc1155s,
+ prng: prng,
+ testHelpers: testHelpers,
+ timestamp: block.timestamp,
+ validatorZone: validatorZone,
+ contractOfferer: contractOfferer,
+ eip1271Offerer: eip1271Offerer,
+ self: address(this),
+ caller: address(this),
+ alice: testHelpers.makeAccount("alice"),
+ bob: testHelpers.makeAccount("bob"),
+ carol: testHelpers.makeAccount("carol"),
+ dillon: testHelpers.makeAccount("dillon"),
+ eve: testHelpers.makeAccount("eve"),
+ frank: testHelpers.makeAccount("frank"),
+ conduits: conduits,
+ starting721offerIndex: 0,
+ starting721considerationIndex: 0,
+ potential1155TokenIds: potential1155TokenIds,
+ basicOrderCategory: BasicOrderCategory.NONE,
+ basicOfferSpace: OfferItemSpace(
+ ItemType.NATIVE,
+ TokenIndex.ONE,
+ Criteria.MERKLE,
+ Amount.FIXED
+ ),
+ counter: 0,
+ contractOffererNonce: 0
+ });
+ }
+
+ /**
+ * @dev Internal helper used to create a new conduit based on the salt.
+ */
+ function _createConduit(
+ ConduitControllerInterface conduitController,
+ SeaportInterface seaport,
+ uint96 conduitSalt
+ ) internal returns (TestConduit memory) {
+ bytes32 conduitKey = abi.decode(
+ abi.encodePacked(address(this), conduitSalt),
+ (bytes32)
+ );
+ Conduit conduit = Conduit(
+ conduitController.createConduit(conduitKey, address(this))
+ );
+ conduitController.updateChannel(
+ address(conduit),
+ address(seaport),
+ true
+ );
+ return TestConduit({ addr: address(conduit), key: conduitKey });
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzGenerators.sol b/test/foundry/new/helpers/FuzzGenerators.sol
new file mode 100644
index 000000000..66ad58d75
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzGenerators.sol
@@ -0,0 +1,2441 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ MatchComponent,
+ MatchComponentType,
+ OfferItemLib,
+ OrderLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ Fulfillment,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType, Side } from "seaport-sol/SeaportEnums.sol";
+
+import { OrderDetails } from "seaport-sol/fulfillments/lib/Structs.sol";
+import { Solarray } from "solarray/Solarray.sol";
+import {
+ Amount,
+ BasicOrderCategory,
+ BroadOrderType,
+ Caller,
+ ConduitChoice,
+ ContractOrderRebate,
+ Criteria,
+ EOASignature,
+ ExtraData,
+ FulfillmentRecipient,
+ Offerer,
+ Recipient,
+ SignatureMethod,
+ Time,
+ Tips,
+ TokenIndex,
+ UnavailableReason,
+ Zone,
+ ZoneHash
+} from "seaport-sol/SpaceEnums.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import {
+ ConduitControllerInterface
+} from "seaport-sol/ConduitControllerInterface.sol";
+
+import {
+ AdvancedOrdersSpace,
+ ConsiderationItemSpace,
+ OfferItemSpace,
+ OrderComponentsSpace
+} from "seaport-sol/StructSpace.sol";
+
+import { EIP712MerkleTree } from "../../utils/EIP712MerkleTree.sol";
+
+import {
+ FuzzGeneratorContext,
+ TestConduit
+} from "./FuzzGeneratorContextLib.sol";
+
+import {
+ _locateCurrentAmount,
+ FuzzHelpers,
+ Structure
+} from "./FuzzHelpers.sol";
+
+import { FuzzInscribers } from "./FuzzInscribers.sol";
+
+import { EIP1271Offerer } from "./EIP1271Offerer.sol";
+
+import {
+ AggregationStrategy,
+ FulfillAvailableStrategy,
+ MatchStrategy,
+ FulfillmentGeneratorLib,
+ FulfillmentStrategy
+} from "seaport-sol/fulfillments/lib/FulfillmentLib.sol";
+
+/**
+ * @dev Generators are responsible for creating guided, random order data for
+ * FuzzEngine tests. Generation happens in two phases: first, we create an
+ * AdvancedOrdersSpace, a nested struct of state enums that represent the
+ * test state itself. Then we walk this generated state struct and build
+ * up an actual array of AdvancedOrders that we can give to Seaport. Each
+ * state enum has its own "generator" library, responsible either for
+ * returning a value or modifying an order according to the selected
+ * state. Generators have access to a PRNG in their context, which they
+ * can use to generate random values.
+ *
+ * The TestStateGenerator library is responsible for step one: generating
+ * an AdvancedOrdersSpace struct representing a fuzzed order permutation.
+ */
+library TestStateGenerator {
+ using PRNGHelpers for FuzzGeneratorContext;
+ using MatchComponentType for MatchComponent;
+
+ function generate(
+ uint256 totalOrders,
+ uint256 maxOfferItemsPerOrder,
+ uint256 maxConsiderationItemsPerOrder,
+ bytes memory seedInput,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (AdvancedOrdersSpace memory) {
+ context.prng.state = uint256(keccak256(seedInput));
+
+ {
+ uint256 basicToggle = context.randRange(0, 10);
+ if (basicToggle == 0) {
+ context.basicOrderCategory = BasicOrderCategory.LISTING;
+ } else if (basicToggle == 1) {
+ context.basicOrderCategory = BasicOrderCategory.BID;
+ } else {
+ context.basicOrderCategory = BasicOrderCategory.NONE;
+ }
+ }
+
+ bool isMatchable = false;
+
+ uint256 maximumFulfilled = totalOrders;
+
+ if (context.basicOrderCategory != BasicOrderCategory.NONE) {
+ totalOrders = 1;
+ maxOfferItemsPerOrder = 1;
+ if (maxConsiderationItemsPerOrder == 0) {
+ maxConsiderationItemsPerOrder = 1;
+ }
+ maximumFulfilled = 1;
+ } else {
+ isMatchable = context.randRange(0, 4) == 0 ? true : false;
+ if (!isMatchable) {
+ maximumFulfilled = context.randRange(1, totalOrders);
+ }
+ }
+
+ if (maxOfferItemsPerOrder == 0 && maxConsiderationItemsPerOrder == 0) {
+ maxOfferItemsPerOrder = context.randRange(0, 1);
+ maxConsiderationItemsPerOrder = 1 - maxOfferItemsPerOrder;
+ }
+
+ OrderComponentsSpace[] memory components = new OrderComponentsSpace[](
+ totalOrders
+ );
+
+ bool someAvailable = false;
+
+ for (uint256 i; i < totalOrders; ++i) {
+ UnavailableReason reason = (
+ context.randRange(0, 1) == 0
+ ? UnavailableReason.AVAILABLE
+ // Don't fuzz 5 (maxfulfilled satisfied), since it's a more
+ // of a consequence (to be handled in derivers) than a
+ // target.
+ : UnavailableReason(context.choice(Solarray.uint256s(1, 2, 3, 4, 6)))
+ );
+
+ if (reason == UnavailableReason.AVAILABLE) {
+ someAvailable = true;
+ }
+
+ uint256 bulkSigHeight = context.randRange(1, 24);
+
+ components[i] = OrderComponentsSpace({
+ offerer: Offerer(context.choice(Solarray.uint256s(1, 2, 4))),
+ zone: Zone(context.randEnum(0, 1)),
+ offer: generateOffer(maxOfferItemsPerOrder, context),
+ consideration: generateConsideration(
+ maxConsiderationItemsPerOrder,
+ context,
+ false
+ ),
+ orderType: BroadOrderType(context.randEnum(0, 2)),
+ // NOTE: unavailable times are inserted downstream.
+ time: Time(context.randEnum(1, 2)),
+ zoneHash: ZoneHash(context.randEnum(0, 2)),
+ signatureMethod: SignatureMethod(
+ context.choice(Solarray.uint256s(0, 1, 4))
+ ),
+ eoaSignatureType: EOASignature(context.randEnum(0, 3)),
+ bulkSigHeight: bulkSigHeight,
+ bulkSigIndex: context.randRange(0, 2 ** bulkSigHeight - 1),
+ conduit: ConduitChoice(context.randEnum(0, 2)),
+ tips: Tips(context.randEnum(0, 1)),
+ unavailableReason: reason,
+ extraData: ExtraData(context.randEnum(0, 1)),
+ rebate: ContractOrderRebate(context.randEnum(0, 4))
+ });
+
+ // Set up order components specific to contract order
+ if (components[i].orderType == BroadOrderType.CONTRACT) {
+ components[i].offerer == Offerer.CONTRACT_OFFERER;
+ components[i].signatureMethod == SignatureMethod.CONTRACT;
+ components[i].tips = Tips.NONE;
+ if (
+ components[i].unavailableReason ==
+ UnavailableReason.ALREADY_FULFILLED ||
+ components[i].unavailableReason ==
+ UnavailableReason.CANCELLED
+ ) {
+ components[i].unavailableReason = UnavailableReason(
+ context.choice(Solarray.uint256s(1, 2, 6))
+ );
+ }
+
+ // Contract orders must have fixed amounts.
+ for (uint256 j = 0; j < components[i].offer.length; ++j) {
+ components[i].offer[j].amount = Amount.FIXED;
+ // TODO: support wildcard resolution (note that the
+ // contract offerer needs to resolve these itself)
+ components[i].offer[j].criteria = Criteria.MERKLE;
+ }
+
+ for (
+ uint256 j = 0;
+ j < components[i].consideration.length;
+ ++j
+ ) {
+ components[i].consideration[j].amount = Amount.FIXED;
+ // TODO: support offerer returning other recipients.
+ components[i].consideration[j].recipient = Recipient
+ .OFFERER;
+ // TODO: support wildcard resolution (note that the
+ // contract offerer needs to resolve these itself)
+ components[i].consideration[j].criteria = Criteria.MERKLE;
+ }
+
+ if (
+ components[i].consideration.length == 0 &&
+ (components[i].rebate ==
+ ContractOrderRebate.LESS_CONSIDERATION_ITEMS ||
+ components[i].rebate ==
+ ContractOrderRebate.LESS_CONSIDERATION_ITEM_AMOUNTS)
+ ) {
+ components[i].rebate = ContractOrderRebate(
+ context.randEnum(0, 2)
+ );
+ }
+
+ if (
+ components[i].rebate ==
+ ContractOrderRebate.LESS_CONSIDERATION_ITEM_AMOUNTS
+ ) {
+ bool canReduceAmounts;
+ for (
+ uint256 j = 0;
+ j < components[i].consideration.length;
+ ++j
+ ) {
+ ItemType itemType = components[i]
+ .consideration[j]
+ .itemType;
+ if (
+ itemType != ItemType.ERC721 &&
+ itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ // NOTE: theoretically there could still be items with amount 1
+ // that would not be eligible for reducing consideration amounts.
+ canReduceAmounts = true;
+ break;
+ }
+ }
+
+ if (!canReduceAmounts) {
+ components[i].rebate = ContractOrderRebate(
+ context.randEnum(0, 3)
+ );
+ }
+ }
+
+ if (
+ components[i].rebate ==
+ ContractOrderRebate.MORE_OFFER_ITEM_AMOUNTS
+ ) {
+ bool canIncreaseAmounts;
+ for (uint256 j = 0; j < components[i].offer.length; ++j) {
+ ItemType itemType = components[i].offer[j].itemType;
+ if (
+ itemType != ItemType.ERC721 &&
+ itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ canIncreaseAmounts = true;
+ break;
+ }
+ }
+
+ if (!canIncreaseAmounts) {
+ components[i].rebate = ContractOrderRebate(
+ components[i].consideration.length == 0
+ ? context.randEnum(0, 1)
+ : context.choice(Solarray.uint256s(0, 1, 3))
+ );
+ }
+ }
+
+ // TODO: figure out how to support removing criteria items
+ // (just need to filter removed items out of resolvers)
+ if (
+ components[i].rebate ==
+ ContractOrderRebate.LESS_CONSIDERATION_ITEMS
+ ) {
+ ItemType itemType = components[i]
+ .consideration[components[i].consideration.length - 1]
+ .itemType;
+ if (
+ itemType == ItemType.ERC721_WITH_CRITERIA ||
+ itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ components[i].rebate = ContractOrderRebate(
+ context.randEnum(0, 1)
+ );
+ }
+ }
+ } else if (
+ components[i].unavailableReason ==
+ UnavailableReason.GENERATE_ORDER_FAILURE
+ ) {
+ components[i].unavailableReason = UnavailableReason(
+ context.randEnum(1, 4)
+ );
+ }
+
+ if (components[i].offerer == Offerer.EIP1271) {
+ components[i].signatureMethod = SignatureMethod.EIP1271;
+ }
+ }
+
+ if (!someAvailable) {
+ components[context.randRange(0, totalOrders - 1)]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+ }
+
+ FulfillmentStrategy memory strategy = (
+ FulfillmentGeneratorLib.getDefaultFulfillmentStrategy()
+ );
+
+ {
+ strategy.aggregationStrategy = AggregationStrategy(
+ context.randEnum(0, 2)
+ );
+
+ strategy.fulfillAvailableStrategy = FulfillAvailableStrategy(
+ context.randEnum(0, 3) // TODO: fuzz on filterable as well
+ );
+
+ strategy.matchStrategy = MatchStrategy(
+ context.randEnum(2, 2) // TODO: fuzz on more than MAX_INCLUSION
+ );
+ }
+
+ return
+ AdvancedOrdersSpace({
+ orders: components,
+ isMatchable: isMatchable,
+ maximumFulfilled: maximumFulfilled,
+ recipient: FulfillmentRecipient(context.randEnum(0, 3)),
+ conduit: ConduitChoice(context.randEnum(0, 2)),
+ caller: Caller(context.randEnum(0, 6)),
+ strategy: strategy
+ });
+ }
+
+ function generateOffer(
+ uint256 maxOfferItemsPerOrder,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (OfferItemSpace[] memory) {
+ if (context.basicOrderCategory == BasicOrderCategory.NONE) {
+ uint256 len = context.randRange(0, maxOfferItemsPerOrder);
+
+ OfferItemSpace[] memory offer = new OfferItemSpace[](len);
+ for (uint256 i; i < len; ++i) {
+ offer[i] = OfferItemSpace({
+ itemType: ItemType(
+ context.randRange(0, 10) != 0
+ ? context.randEnum(0, 3)
+ : context.randEnum(4, 5)
+ ),
+ tokenIndex: TokenIndex(context.randEnum(0, 2)),
+ criteria: Criteria(context.randEnum(0, 1)),
+ amount: Amount(context.randEnum(0, 2))
+ });
+ }
+
+ return offer;
+ } else {
+ OfferItemSpace[] memory offer = new OfferItemSpace[](1);
+ offer[0] = OfferItemSpace({
+ itemType: ItemType(
+ context.basicOrderCategory == BasicOrderCategory.LISTING
+ ? context.randEnum(2, 3)
+ : 1
+ ),
+ tokenIndex: TokenIndex(context.randEnum(0, 2)),
+ criteria: Criteria(0),
+ amount: Amount(context.randEnum(0, 2))
+ });
+
+ context.basicOfferSpace = offer[0];
+
+ return offer;
+ }
+ }
+
+ function generateConsideration(
+ uint256 maxConsiderationItemsPerOrder,
+ FuzzGeneratorContext memory context,
+ bool atLeastOne
+ ) internal pure returns (ConsiderationItemSpace[] memory) {
+ bool isBasic = context.basicOrderCategory != BasicOrderCategory.NONE;
+
+ uint256 len = context.randRange(
+ (isBasic || atLeastOne) ? 1 : 0,
+ ((isBasic || atLeastOne) && maxConsiderationItemsPerOrder == 0)
+ ? 1
+ : maxConsiderationItemsPerOrder
+ );
+
+ ConsiderationItemSpace[]
+ memory consideration = new ConsiderationItemSpace[](len);
+
+ if (!isBasic) {
+ for (uint256 i; i < len; ++i) {
+ consideration[i] = ConsiderationItemSpace({
+ itemType: ItemType(
+ context.randRange(0, 10) != 0
+ ? context.randEnum(0, 3)
+ : context.randEnum(4, 5)
+ ),
+ tokenIndex: TokenIndex(context.randEnum(0, 2)),
+ criteria: Criteria(context.randEnum(0, 1)),
+ amount: atLeastOne
+ ? Amount.FIXED
+ : Amount(context.randEnum(0, 2)),
+ recipient: atLeastOne
+ ? Recipient.OFFERER
+ : Recipient(context.randEnum(0, 4))
+ });
+ }
+ } else {
+ consideration[0] = ConsiderationItemSpace({
+ itemType: ItemType(
+ context.basicOrderCategory == BasicOrderCategory.BID
+ ? context.randEnum(2, 3)
+ : context.randEnum(0, 1)
+ ),
+ tokenIndex: TokenIndex(context.randEnum(0, 2)),
+ criteria: Criteria(0),
+ amount: Amount.FIXED, // Always fixed
+ recipient: Recipient.OFFERER // Always offerer
+ });
+
+ for (uint256 i = 1; i < len; ++i) {
+ consideration[i] = ConsiderationItemSpace({
+ itemType: context.basicOfferSpace.itemType,
+ tokenIndex: context.basicOfferSpace.tokenIndex,
+ criteria: Criteria(0),
+ // TODO: sum(amounts) must be less than offer amount, right
+ // now this is enforced in a hacky way
+ amount: Amount.FIXED, // Always fixed
+ recipient: atLeastOne
+ ? Recipient.OFFERER
+ : Recipient(context.randEnum(0, 4))
+ });
+ }
+ }
+ return consideration;
+ }
+
+ function empty() internal pure returns (AdvancedOrdersSpace memory) {
+ return
+ AdvancedOrdersSpace({
+ orders: new OrderComponentsSpace[](0),
+ isMatchable: false,
+ maximumFulfilled: 0,
+ recipient: FulfillmentRecipient.ZERO,
+ conduit: ConduitChoice.NONE,
+ caller: Caller.TEST_CONTRACT,
+ strategy: FulfillmentGeneratorLib
+ .getDefaultFulfillmentStrategy()
+ });
+ }
+}
+
+/**
+ * @dev Convert an AdvancedOrdersSpace struct representing a given order
+ * permutation into an array of AdvancedOrder structs with valid random
+ * parameters.
+ */
+library AdvancedOrdersSpaceGenerator {
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ using FulfillmentGeneratorLib for OrderDetails[];
+
+ using BroadOrderTypeGenerator for AdvancedOrder;
+ using ConduitGenerator for ConduitChoice;
+ using ConsiderationItemSpaceGenerator for ConsiderationItemSpace;
+ using ExtraDataGenerator for AdvancedOrder;
+ using FulfillmentRecipientGenerator for FulfillmentRecipient;
+ using FuzzHelpers for AdvancedOrder[];
+ using MatchComponentType for MatchComponent;
+ using OfferItemSpaceGenerator for OfferItemSpace;
+ using OrderComponentsSpaceGenerator for OrderComponentsSpace;
+ using PRNGHelpers for FuzzGeneratorContext;
+ using SignatureGenerator for AdvancedOrder;
+ using TimeGenerator for OrderParameters;
+ using CallerGenerator for Caller;
+
+ function generate(
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal returns (AdvancedOrder[] memory) {
+ uint256 len = bound(space.orders.length, 0, 10);
+ AdvancedOrder[] memory orders = new AdvancedOrder[](len);
+
+ // Build orders.
+ _buildOrders(orders, space, context);
+
+ // Ensure that orders are not entirely empty of items.
+ _handleInsertIfAllEmpty(orders, context);
+ _handleInsertIfAllFilterable(orders, context, space);
+
+ // Handle match case.
+ if (space.isMatchable) {
+ _ensureAllAvailable(space);
+ _handleInsertIfAllConsiderationEmpty(orders, context);
+ _handleInsertIfAllMatchFilterable(orders, context);
+ _squareUpRemainders(orders, space, context);
+ space.maximumFulfilled = orders.length;
+ } else {
+ if (len > 1) {
+ _adjustUnavailable(orders, space, context);
+ } else {
+ _ensureAllAvailable(space);
+ }
+ _ensureDirectSupport(orders, space, context);
+ }
+
+ bool contractOrderFound;
+ for (uint256 i = 0; i < orders.length; ++i) {
+ AdvancedOrder memory order = orders[i];
+
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ contractOrderFound = true;
+ }
+
+ orders[i] = order.withCoercedAmountsForPartialFulfillment();
+
+ OrderParameters memory orderParams = order.parameters;
+
+ if (
+ space.orders[i].tips == Tips.NONE ||
+ orderParams.orderType == OrderType.CONTRACT
+ ) {
+ orders[i].parameters.totalOriginalConsiderationItems = (
+ orders[i].parameters.consideration.length
+ );
+ }
+
+ if (orderParams.orderType == OrderType.CONTRACT) {
+ order.parameters = orderParams
+ .withOrderType(OrderType.CONTRACT)
+ .withOfferer(address(context.contractOfferer));
+
+ for (uint256 j = 0; j < orderParams.offer.length; ++j) {
+ OfferItem memory item = orderParams.offer[j];
+
+ if (item.startAmount != 0) {
+ order.parameters.offer[j].endAmount = item.startAmount;
+ } else {
+ order.parameters.offer[j].startAmount = item.endAmount;
+ }
+
+ if (
+ uint256(item.itemType) > 3 &&
+ item.identifierOrCriteria == 0
+ ) {
+ order.parameters.offer[j].itemType = ItemType(
+ uint256(item.itemType) - 2
+ );
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(uint256(i), uint256(j), Side.OFFER)
+ );
+ order.parameters.offer[j].identifierOrCriteria = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .wildcardIdentifierForGivenItemHash(itemHash);
+ }
+ }
+
+ for (uint256 j = 0; j < orderParams.consideration.length; ++j) {
+ ConsiderationItem memory item = (
+ orderParams.consideration[j]
+ );
+
+ if (item.startAmount != 0) {
+ order.parameters.consideration[j].endAmount = (
+ item.startAmount
+ );
+ } else {
+ order.parameters.consideration[j].startAmount = (
+ item.endAmount
+ );
+ }
+
+ if (
+ uint256(item.itemType) > 3 &&
+ item.identifierOrCriteria == 0
+ ) {
+ order.parameters.consideration[j].itemType = ItemType(
+ uint256(item.itemType) - 2
+ );
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(
+ uint256(i),
+ uint256(j),
+ Side.CONSIDERATION
+ )
+ );
+ order
+ .parameters
+ .consideration[j]
+ .identifierOrCriteria = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .wildcardIdentifierForGivenItemHash(itemHash);
+ }
+
+ // TODO: support offerer returning other recipients.
+ order.parameters.consideration[j].recipient = payable(
+ address(context.contractOfferer)
+ );
+ }
+ }
+ }
+
+ // Set up a random base counter and nonce, which will be used to set the
+ // counter and nonce for each offerer in the `_signOrders` function.
+ context.counter = context.randRange(0, type(uint128).max);
+ context.contractOffererNonce = context.randRange(0, type(uint128).max);
+
+ // Sign orders and add the hashes to the context.
+ _signOrders(space, orders, context);
+
+ return orders;
+ }
+
+ function generateRecipient(
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (address) {
+ return space.recipient.generate(context);
+ }
+
+ function generateCaller(
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal view returns (address) {
+ return space.caller.generate(context);
+ }
+
+ function generateFulfillerConduitKey(
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (bytes32) {
+ return space.conduit.generate(context).key;
+ }
+
+ function _ensureDirectSupport(
+ AdvancedOrder[] memory orders,
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal {
+ // Ensure no native offer items on non-contract order types
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderParameters memory order = orders[i].parameters;
+ if (order.orderType == OrderType.CONTRACT) {
+ continue;
+ }
+
+ for (uint256 j = 0; j < order.offer.length; ++j) {
+ OfferItem memory item = order.offer[j];
+ if (item.itemType == ItemType.NATIVE) {
+ // Generate a new offer and make sure it has no native items
+ orders[i].parameters.offer[j] = space
+ .orders[i]
+ .offer[j]
+ .generate(context, true, i, j);
+ }
+ }
+ }
+ }
+
+ function _ensureAllAvailable(
+ AdvancedOrdersSpace memory space
+ ) internal pure {
+ for (uint256 i = 0; i < space.orders.length; ++i) {
+ space.orders[i].unavailableReason = UnavailableReason.AVAILABLE;
+ }
+ }
+
+ function _buildOrders(
+ AdvancedOrder[] memory orders,
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal {
+ for (uint256 i; i < orders.length; ++i) {
+ OrderParameters memory orderParameters = space.orders[i].generate(
+ context,
+ false, // ensureDirectSupport false: allow native offer items
+ i
+ );
+ orders[i] = OrderLib
+ .empty()
+ .withParameters(orderParameters)
+ .toAdvancedOrder({
+ numerator: 1,
+ denominator: 1,
+ extraData: bytes("")
+ })
+ .withBroadOrderType(space.orders[i].orderType, context)
+ .withGeneratedExtraData(space.orders[i].extraData, context);
+ }
+ }
+
+ function _adjustUnavailable(
+ AdvancedOrder[] memory orders,
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal pure {
+ for (uint256 i = 0; i < orders.length; ++i) {
+ _adjustUnavailable(
+ orders[i],
+ space.orders[i].unavailableReason,
+ context
+ );
+ }
+ }
+
+ function _adjustUnavailable(
+ AdvancedOrder memory order,
+ UnavailableReason reason,
+ FuzzGeneratorContext memory context
+ ) internal pure {
+ OrderParameters memory parameters = order.parameters;
+ // UnavailableReason.AVAILABLE => take no action
+ // UnavailableReason.CANCELLED => state will be conformed in amend phase
+ // UnavailableReason.ALREADY_FULFILLED => state will be conformed in
+ // amend phase
+ // UnavailableReason.MAX_FULFILLED_SATISFIED => should never hit this
+ // UnavailableReason.GENERATE_ORDER_FAILURE => handled downstream
+ if (reason == UnavailableReason.EXPIRED) {
+ parameters = parameters.withGeneratedTime(
+ Time(context.randEnum(3, 4)),
+ context
+ );
+ } else if (reason == UnavailableReason.STARTS_IN_FUTURE) {
+ parameters = parameters.withGeneratedTime(
+ Time.STARTS_IN_FUTURE,
+ context
+ );
+ }
+ }
+
+ struct SquareUpRemaindersInfra {
+ MatchComponent[] remainders;
+ CriteriaResolver[] resolvers;
+ uint256 resolvedIdentifier;
+ ItemType resolvedItemType;
+ ConsiderationItem item;
+ uint256 amount;
+ }
+
+ /**
+ * @dev This function gets the remainders from the match and inserts them
+ * into the orders. This is done to ensure that the orders are
+ * matchable. If there are consideration remainders, they are inserted
+ * into the orders on the offer side.
+ */
+ function _squareUpRemainders(
+ AdvancedOrder[] memory orders,
+ AdvancedOrdersSpace memory space,
+ FuzzGeneratorContext memory context
+ ) internal {
+ // Toss a bunch of stuff into a struct to avoid stack too deep errors.
+ SquareUpRemaindersInfra memory infra;
+
+ for (uint256 i = 0; i < orders.length; ++i) {
+ AdvancedOrder memory order = orders[i];
+ orders[i] = order.withCoercedAmountsForPartialFulfillment();
+ }
+
+ UnavailableReason[] memory unavailableReasons = new UnavailableReason[](
+ space.orders.length
+ );
+
+ for (uint256 i; i < space.orders.length; ++i) {
+ unavailableReasons[i] = space.orders[i].unavailableReason;
+ }
+
+ {
+ infra.resolvers = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .deriveCriteriaResolvers(orders);
+
+ bytes32[] memory orderHashes = orders.getOrderHashes(address(context.seaport));
+
+ OrderDetails[] memory details = orders.getOrderDetails(
+ infra.resolvers,
+ orderHashes,
+ unavailableReasons
+ );
+ // Get the remainders.
+ (, , infra.remainders) = details.getMatchedFulfillments();
+ }
+
+ // Iterate over the remainders and insert them into the orders.
+ for (uint256 i = 0; i < infra.remainders.length; ++i) {
+ {
+ uint8 orderIndex;
+ uint8 itemIndex;
+
+ // Unpack the remainder from the MatchComponent into its
+ // constituent parts.
+ (infra.amount, orderIndex, itemIndex) = infra.remainders[i].unpack();
+
+ // Get the consideration item with the remainder.
+ infra.item = orders[orderIndex].parameters.consideration[itemIndex];
+
+ infra.resolvedIdentifier = infra.item.identifierOrCriteria;
+ infra.resolvedItemType = infra.item.itemType;
+ if (
+ infra.item.itemType == ItemType.ERC721_WITH_CRITERIA ||
+ infra.item.itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ infra.resolvedItemType = _convertCriteriaItemType(infra.item.itemType);
+ if (infra.item.identifierOrCriteria == 0) {
+ bytes32 itemHash = keccak256(
+ abi.encodePacked(
+ uint256(orderIndex),
+ uint256(itemIndex),
+ Side.CONSIDERATION
+ )
+ );
+ infra.resolvedIdentifier = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .wildcardIdentifierForGivenItemHash(itemHash);
+ } else {
+ infra.resolvedIdentifier = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .resolvableIdentifierForGivenCriteria(
+ infra.item.identifierOrCriteria
+ )
+ .resolvedIdentifier;
+ }
+ }
+ }
+
+ // Pick a random order to insert the remainder into.
+ uint256 orderInsertionIndex = context.randRange(
+ 0,
+ orders.length - 1
+ );
+
+ OfferItem memory newItem;
+ {
+ uint256 amountGivenPartial = _applyInverseAndRoundUp(
+ infra.amount,
+ uint256(orders[orderInsertionIndex].numerator),
+ uint256(orders[orderInsertionIndex].denominator)
+ );
+
+ newItem = OfferItem({
+ itemType: infra.resolvedItemType,
+ token: infra.item.token,
+ identifierOrCriteria: infra.resolvedIdentifier,
+ startAmount: amountGivenPartial,
+ endAmount: amountGivenPartial
+ });
+ }
+
+ // Create a new offer array with room for the remainder.
+ OfferItem[] memory newOffer = new OfferItem[](
+ orders[orderInsertionIndex].parameters.offer.length + 1
+ );
+
+ // If the targeted order has no offer, just add the remainder to the
+ // new offer.
+ if (orders[orderInsertionIndex].parameters.offer.length == 0) {
+ newOffer[0] = newItem;
+ } else {
+ // If the targeted order has an offer, pick a random index to
+ // insert the remainder into.
+ uint256 itemInsertionIndex = context.randRange(
+ 0,
+ orders[orderInsertionIndex].parameters.offer.length - 1
+ );
+
+ // Copy the offer items from the targeted order into the new
+ // offer array. This loop handles everything before the
+ // insertion index.
+ for (uint256 j = 0; j < itemInsertionIndex; ++j) {
+ newOffer[j] = orders[orderInsertionIndex].parameters.offer[
+ j
+ ];
+ }
+
+ // Insert the remainder into the new offer array at the
+ // insertion index.
+ newOffer[itemInsertionIndex] = newItem;
+
+ // Copy the offer items after the insertion index into the new
+ // offer array.
+ for (
+ uint256 j = itemInsertionIndex + 1;
+ j < newOffer.length;
+ ++j
+ ) {
+ newOffer[j] = orders[orderInsertionIndex].parameters.offer[
+ j - 1
+ ];
+ }
+
+ // shift any wildcard offer items.
+ context.testHelpers.criteriaResolverHelper().shiftWildcards(
+ orderInsertionIndex,
+ Side.OFFER,
+ itemInsertionIndex,
+ newOffer.length - 1
+ );
+ }
+
+ bytes32 newOfferHash = keccak256(abi.encode(newOffer));
+
+ bytes32 existingOfferHash = keccak256(
+ abi.encode(orders[orderInsertionIndex].parameters.offer)
+ );
+
+ if (newOfferHash == existingOfferHash) {
+ // If the offer hash is the same, then the offer is unchanged.
+ // This can happen if the offer is empty and the remainder is
+ // inserted at index 0. In this case, we can just skip this
+ // iteration.
+ revert("FuzzGenerators: offer hash unchanged");
+ }
+
+ // Replace the offer in the targeted order with the new offer.
+ orders[orderInsertionIndex].parameters.offer = newOffer;
+ }
+
+ // TODO: remove this check once high confidence in the mechanic has been
+ // established (this just fails fast to rule out downstream issues)
+ if (infra.remainders.length > 0) {
+ infra.resolvers = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .deriveCriteriaResolvers(orders);
+ bytes32[] memory orderHashes = orders.getOrderHashes(address(context.seaport));
+ OrderDetails[] memory details = orders.getOrderDetails(
+ infra.resolvers,
+ orderHashes,
+ unavailableReasons
+ );
+ // Get the remainders.
+ (, , infra.remainders) = details.getMatchedFulfillments();
+
+ if (infra.remainders.length > 0) {
+ // NOTE: this may be caused by inserting offer items into orders
+ // with partial fill fractions. The amount on the item that is
+ // inserted should be increased based on fraction in that case.
+ revert("FuzzGenerators: could not satisfy remainders");
+ }
+ }
+ }
+
+ function _convertCriteriaItemType(
+ ItemType itemType
+ ) internal pure returns (ItemType) {
+ if (itemType == ItemType.ERC721_WITH_CRITERIA) {
+ return ItemType.ERC721;
+ } else if (itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ return ItemType.ERC1155;
+ } else {
+ revert(
+ "AdvancedOrdersSpaceGenerator: amount deriver helper resolving non criteria item type"
+ );
+ }
+ }
+
+ function _applyInverseAndRoundUp(
+ uint256 amount,
+ uint256 numerator,
+ uint256 denominator
+ ) internal pure returns (uint256 newAmount) {
+ if (numerator == denominator) {
+ return amount;
+ }
+
+ uint256 newAmountSansRounding = (amount * denominator) / numerator;
+
+ newAmount =
+ newAmountSansRounding +
+ ((denominator -
+ ((newAmountSansRounding * numerator) % denominator)) /
+ numerator);
+
+ if ((newAmount * numerator) % denominator != 0) {
+ revert("AdvancedOrdersSpaceGenerator: inverse change failed");
+ }
+
+ if ((newAmount * numerator) / denominator < amount) {
+ revert("AdvancedOrdersSpaceGenerator: inverse not rounded up");
+ }
+ }
+
+ function _handleInsertIfAllEmpty(
+ AdvancedOrder[] memory orders,
+ FuzzGeneratorContext memory context
+ ) internal {
+ bool allEmpty = true;
+
+ // Iterate over the orders and check if they have any offer or
+ // consideration items in them. As soon as we find one that does, set
+ // allEmpty to false and break out of the loop.
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderParameters memory orderParams = orders[i].parameters;
+ if (
+ orderParams.offer.length + orderParams.consideration.length > 0
+ ) {
+ allEmpty = false;
+ break;
+ }
+ }
+
+ // If all the orders are empty, insert a consideration item into a
+ // random order.
+ if (allEmpty) {
+ uint256 orderInsertionIndex = context.randRange(
+ 0,
+ orders.length - 1
+ );
+ OrderParameters memory orderParams = orders[orderInsertionIndex]
+ .parameters;
+
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](
+ 1
+ );
+ consideration[0] = TestStateGenerator
+ .generateConsideration(1, context, true)[0].generate(
+ context,
+ orderParams.offerer,
+ orderInsertionIndex,
+ 0
+ );
+
+ orderParams.consideration = consideration;
+ }
+ }
+
+ function _handleInsertIfAllConsiderationEmpty(
+ AdvancedOrder[] memory orders,
+ FuzzGeneratorContext memory context
+ ) internal {
+ bool allEmpty = true;
+
+ // Iterate over the orders and check if they have any consideration
+ // items in them. As soon as we find one that does, set allEmpty to
+ // false and break out of the loop.
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderParameters memory orderParams = orders[i].parameters;
+ if (orderParams.consideration.length > 0) {
+ allEmpty = false;
+ break;
+ }
+ }
+
+ // If all the orders are empty, insert a consideration item into a
+ // random order.
+ if (allEmpty) {
+ uint256 orderInsertionIndex = context.randRange(
+ 0,
+ orders.length - 1
+ );
+ OrderParameters memory orderParams = orders[orderInsertionIndex]
+ .parameters;
+
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](
+ 1
+ );
+ consideration[0] = TestStateGenerator
+ .generateConsideration(1, context, true)[0].generate(
+ context,
+ orderParams.offerer,
+ orderInsertionIndex,
+ 0
+ );
+
+ orderParams.consideration = consideration;
+ }
+ }
+
+ /**
+ * @dev Handle orders with only filtered executions. Note: technically
+ * orders with no unfiltered consideration items can still be called in
+ * some cases via fulfillAvailable as long as there are offer items
+ * that don't have to be filtered as well. Also note that this does not
+ * account for unfilterable matchOrders combinations yet. But the
+ * baseline behavior is that an order with no explicit executions,
+ * Seaport will revert.
+ */
+ function _handleInsertIfAllFilterable(
+ AdvancedOrder[] memory orders,
+ FuzzGeneratorContext memory context,
+ AdvancedOrdersSpace memory space
+ ) internal {
+ bool allFilterable = true;
+ address caller = context.caller == address(0)
+ ? address(this)
+ : context.caller;
+
+ // Iterate over the orders and check if there's a single instance of a
+ // non-filterable consideration item. If there is, set allFilterable to
+ // false and break out of the loop. Skip unavailable orders as well.
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderParameters memory order = orders[i].parameters;
+
+ if (
+ space.orders[i].unavailableReason != UnavailableReason.AVAILABLE
+ ) {
+ continue;
+ }
+
+ for (uint256 j = 0; j < order.consideration.length; ++j) {
+ ConsiderationItem memory item = order.consideration[j];
+
+ if (item.recipient != caller) {
+ allFilterable = false;
+ break;
+ }
+ }
+
+ if (!allFilterable) {
+ break;
+ }
+ }
+
+ // If they're all filterable, then add a consideration item to one of
+ // the orders and ensure that it is available.
+ if (allFilterable) {
+ OrderParameters memory orderParams;
+
+ // Pick a random order to insert the consideration item into and
+ // iterate from that index to the end of the orders array. At the
+ // end of the loop, start back at the beginning
+ // (orders[orderInsertionIndex % orders.length]) and iterate on. As
+ // soon as an order with consideration items is found, break out of
+ // the loop. The orderParams variable will be set to the order with
+ // consideration items. There's chance that no order will have
+ // consideration items, in which case the orderParams variable will
+ // be set to those of the last order iterated over.
+ uint256 orderInsertionIndex = context.randRange(
+ 0,
+ orders.length - 1
+ );
+ for (
+ ;
+ orderInsertionIndex < orders.length * 2;
+ ++orderInsertionIndex
+ ) {
+ orderParams = orders[orderInsertionIndex % orders.length]
+ .parameters;
+
+ if (orderParams.consideration.length != 0) {
+ break;
+ }
+ }
+
+ // If there are no consideration items in any of the orders, then
+ // add a consideration item to a random order.
+ if (orderParams.consideration.length == 0) {
+ // Pick a random order to insert the consideration item into.
+ orderInsertionIndex = context.randRange(0, orders.length - 1);
+
+ // Set the orderParams variable to the parameters of the order
+ // that was picked.
+ orderParams = orders[orderInsertionIndex].parameters;
+
+ // Provision a new consideration item array with a single
+ // element.
+ ConsiderationItem[]
+ memory consideration = new ConsiderationItem[](1);
+
+ // Generate a consideration item and add it to the consideration
+ // item array. The `true` argument indicates that the
+ // consideration item will be unfilterable.
+ consideration[0] = TestStateGenerator
+ .generateConsideration(1, context, true)[0].generate(
+ context,
+ orderParams.offerer,
+ orderInsertionIndex,
+ 0
+ );
+
+ // Set the consideration item array on the order parameters.
+ orderParams.consideration = consideration;
+ }
+
+ space
+ .orders[orderInsertionIndex % orders.length]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+
+ // Pick a random consideration item to modify.
+ uint256 itemIndex = context.randRange(
+ 0,
+ orderParams.consideration.length - 1
+ );
+
+ // Make the recipient an address other than the caller so that
+ // it produces a non-filterable transfer.
+ if (orderParams.orderType != OrderType.CONTRACT) {
+ if (caller != context.alice.addr) {
+ orderParams.consideration[itemIndex].recipient = payable(
+ context.alice.addr
+ );
+ } else {
+ orderParams.consideration[itemIndex].recipient = payable(
+ context.bob.addr
+ );
+ }
+ }
+ }
+ }
+
+ // TODO: figure out a better way to do this; right now it always inserts a
+ // random consideration item on some order with a recipient that is never
+ // used for offerers
+ function _handleInsertIfAllMatchFilterable(
+ AdvancedOrder[] memory orders,
+ FuzzGeneratorContext memory context
+ ) internal {
+ OrderParameters memory orderParams;
+
+ // Pick a random order to insert the consideration item into and
+ // iterate from that index to the end of the orders array. At the
+ // end of the loop, start back at the beginning
+ // (orders[orderInsertionIndex % orders.length]) and iterate on. As
+ // soon as an order with consideration items is found, break out of
+ // the loop. The orderParams variable will be set to the order with
+ // consideration items. There's chance that no order will have
+ // consideration items, in which case the orderParams variable will
+ // be set to those of the last order iterated over.
+ for (
+ uint256 orderInsertionIndex = context.randRange(
+ 0,
+ orders.length - 1
+ );
+ orderInsertionIndex < orders.length * 2;
+ ++orderInsertionIndex
+ ) {
+ orderParams = orders[orderInsertionIndex % orders.length]
+ .parameters;
+
+ if (orderParams.consideration.length != 0) {
+ break;
+ }
+ }
+
+ // If there are no consideration items in any of the orders, then
+ // add a consideration item to a random order.
+ if (orderParams.consideration.length == 0) {
+ // Pick a random order to insert the consideration item into.
+ uint256 orderInsertionIndex = context.randRange(
+ 0,
+ orders.length - 1
+ );
+
+ // Set the orderParams variable to the parameters of the order
+ // that was picked.
+ orderParams = orders[orderInsertionIndex].parameters;
+
+ // Provision a new consideration item array with a single
+ // element.
+ ConsiderationItem[] memory consideration = new ConsiderationItem[](
+ 1
+ );
+
+ // Generate a consideration item and add it to the consideration
+ // item array. The `true` argument indicates that the
+ // consideration item will be unfilterable.
+ consideration[0] = TestStateGenerator
+ .generateConsideration(1, context, true)[0].generate(
+ context,
+ orderParams.offerer,
+ orderInsertionIndex,
+ 0
+ );
+
+ // Set the consideration item array on the order parameters.
+ orderParams.consideration = consideration;
+ }
+
+ // Pick a random consideration item to modify.
+ uint256 itemIndex = context.randRange(
+ 0,
+ orderParams.consideration.length - 1
+ );
+
+ // Make the recipient an address other than any offerer so that
+ // it produces a non-filterable transfer.
+ orderParams.consideration[itemIndex].recipient = payable(
+ orderParams.orderType != OrderType.CONTRACT
+ ? context.dillon.addr
+ : address(context.contractOfferer)
+ );
+ }
+
+ function _signOrders(
+ AdvancedOrdersSpace memory space,
+ AdvancedOrder[] memory orders,
+ FuzzGeneratorContext memory context
+ ) internal {
+ // Iterate over the orders and sign them.
+ for (uint256 i = 0; i < orders.length; ++i) {
+ // Set up variables.
+ AdvancedOrder memory order = orders[i];
+
+ // Skip contract orders since they do not have signatures
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ uint256 contractOffererSpecificContractNonce = context
+ .contractOffererNonce +
+ uint256(uint160(order.parameters.offerer));
+ // Just for convenience of having them both in one place.
+ FuzzInscribers.inscribeContractOffererNonce(
+ order.parameters.offerer,
+ contractOffererSpecificContractNonce,
+ context.seaport
+ );
+ continue;
+ }
+
+ // Get the counter for the offerer.
+ uint256 offererSpecificCounter = context.counter +
+ uint256(uint160(order.parameters.offerer));
+
+ FuzzInscribers.inscribeCounter(
+ order.parameters.offerer,
+ offererSpecificCounter,
+ context.seaport
+ );
+
+ bytes32 orderHash = order.getTipNeutralizedOrderHash(
+ context.seaport,
+ offererSpecificCounter
+ );
+
+ // Replace the unsigned order with a signed order.
+ orders[i] = order.withGeneratedSignature(
+ space.orders[i].signatureMethod,
+ space.orders[i].eoaSignatureType,
+ space.orders[i].bulkSigHeight,
+ space.orders[i].bulkSigIndex,
+ space.orders[i].offerer,
+ order.parameters.offerer,
+ orderHash,
+ context
+ );
+ }
+ }
+
+ function _hasInvalidNativeOfferItems(
+ AdvancedOrder[] memory orders
+ ) internal pure returns (bool) {
+ for (uint256 i = 0; i < orders.length; ++i) {
+ OrderParameters memory orderParams = orders[i].parameters;
+ if (orderParams.orderType == OrderType.CONTRACT) {
+ continue;
+ }
+
+ for (uint256 j = 0; j < orderParams.offer.length; ++j) {
+ OfferItem memory item = orderParams.offer[j];
+
+ if (item.itemType == ItemType.NATIVE) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
+/**
+ * @dev Convert an OrderComponentsSpace struct into actual OrderParameters.
+ */
+library OrderComponentsSpaceGenerator {
+ using OrderParametersLib for OrderParameters;
+
+ using ConduitGenerator for ConduitChoice;
+ using ConsiderationItemSpaceGenerator for ConsiderationItemSpace[];
+ using OffererGenerator for Offerer;
+ using OfferItemSpaceGenerator for OfferItemSpace[];
+ using PRNGHelpers for FuzzGeneratorContext;
+ using TimeGenerator for OrderParameters;
+ using ZoneGenerator for OrderParameters;
+
+ function generate(
+ OrderComponentsSpace memory space,
+ FuzzGeneratorContext memory context,
+ bool ensureDirectSupport,
+ uint256 orderIndex
+ ) internal returns (OrderParameters memory) {
+ if (
+ space.offerer == Offerer.EIP1271 &&
+ space.signatureMethod == SignatureMethod.EOA
+ ) {
+ space.signatureMethod = SignatureMethod.EIP1271;
+ }
+
+ OrderParameters memory params;
+ {
+ address offerer;
+ if (space.signatureMethod == SignatureMethod.SELF_AD_HOC) {
+ offerer = context.caller;
+ } else {
+ offerer = space.offerer.generate(context);
+ }
+
+ params = OrderParametersLib
+ .empty()
+ .withOfferer(offerer)
+ .withOffer(
+ space.offer.generate(
+ context,
+ ensureDirectSupport,
+ orderIndex
+ )
+ )
+ .withConsideration(
+ space.consideration.generate(context, offerer, orderIndex)
+ )
+ .withConduitKey(space.conduit.generate(context).key);
+ }
+
+ // Choose an arbitrary number of tips based on the tip space
+ // (TODO: refactor as a library function)
+ params.totalOriginalConsiderationItems = (
+ (space.tips == Tips.TIPS && params.consideration.length != 0)
+ ? params.consideration.length -
+ context.randRange(1, params.consideration.length)
+ : params.consideration.length
+ );
+
+ return
+ params
+ .withGeneratedTime(space.time, context)
+ .withGeneratedZone(space.zone, context)
+ .withSalt(context.randRange(0, type(uint256).max));
+ }
+}
+
+/**
+ * @dev Select a conduit.
+ */
+library ConduitGenerator {
+ function generate(
+ ConduitChoice conduit,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (TestConduit memory) {
+ if (conduit == ConduitChoice.NONE) {
+ return
+ TestConduit({
+ key: bytes32(0),
+ addr: address(context.seaport)
+ });
+ } else if (conduit == ConduitChoice.ONE) {
+ return context.conduits[0];
+ } else if (conduit == ConduitChoice.TWO) {
+ return context.conduits[1];
+ } else {
+ revert("ConduitGenerator: invalid Conduit index");
+ }
+ }
+}
+
+/**
+ * @dev Generate parameters related to an order's "broad" type: full, partial,
+ * or contract.
+ */
+library BroadOrderTypeGenerator {
+ using PRNGHelpers for FuzzGeneratorContext;
+ using AdvancedOrderLib for AdvancedOrder;
+ using OrderParametersLib for OrderParameters;
+
+ function withBroadOrderType(
+ AdvancedOrder memory order,
+ BroadOrderType broadOrderType,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (AdvancedOrder memory) {
+ OrderParameters memory orderParams = order.parameters;
+ // NOTE: this assumes that the order type has been set to either
+ // FULL_OPEN (by .empty()) or FULL_RESTRICTED (by ZoneGenerator).
+ if (broadOrderType == BroadOrderType.PARTIAL) {
+ // Adjust the order type based on whether it is restricted
+ if (orderParams.orderType == OrderType.FULL_RESTRICTED) {
+ order.parameters = orderParams.withOrderType(
+ OrderType.PARTIAL_RESTRICTED
+ );
+ } else if (orderParams.orderType == OrderType.FULL_OPEN) {
+ order.parameters = orderParams.withOrderType(
+ OrderType.PARTIAL_OPEN
+ );
+ }
+
+ // TODO: get more sophisticated about this down the line
+ uint120 numerator = uint120(context.randRange(1, type(uint80).max));
+ uint120 denominator = uint120(
+ numerator * context.randRange(1, type(uint40).max)
+ );
+
+ return
+ order
+ .withNumerator(numerator)
+ .withDenominator(denominator)
+ .withCoercedAmountsForPartialFulfillment();
+ } else if (broadOrderType == BroadOrderType.CONTRACT) {
+ order.parameters = orderParams.withOrderType(OrderType.CONTRACT);
+ }
+
+ return order;
+ }
+}
+
+/**
+ * @dev Generate extra data on an order.
+ */
+library ExtraDataGenerator {
+ using PRNGHelpers for FuzzGeneratorContext;
+ using AdvancedOrderLib for AdvancedOrder;
+
+ function withGeneratedExtraData(
+ AdvancedOrder memory order,
+ ExtraData extraData,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (AdvancedOrder memory) {
+ if (extraData == ExtraData.NONE) {
+ return order.withExtraData("");
+ } else if (extraData == ExtraData.RANDOM) {
+ return
+ order.withExtraData(generateRandomBytesArray(context, 1, 4096));
+ } else {
+ revert("ExtraDataGenerator: unsupported ExtraData value");
+ }
+ }
+
+ function generateRandomBytesArray(
+ FuzzGeneratorContext memory context,
+ uint256 minSize,
+ uint256 maxSize
+ ) internal pure returns (bytes memory) {
+ uint256 length = context.randRange(minSize, maxSize);
+
+ // Allocate & round the size up to a whole word.
+ bytes memory arr = new bytes(((length + 31) / 32) * 32);
+
+ // Insert each random word in.
+ for (uint256 i = 0; i < length; i += 32) {
+ uint256 randomWord = context.rand();
+
+ assembly {
+ mstore(add(add(arr, 32), i), randomWord)
+ }
+ }
+
+ // Resize the array to the actual length.
+ assembly {
+ mstore(arr, length)
+ }
+
+ // Return the array.
+ return arr;
+ }
+}
+
+/**
+ * @dev Generate zone-related parameters for an order.
+ */
+library ZoneGenerator {
+ using PRNGHelpers for FuzzGeneratorContext;
+ using OrderParametersLib for OrderParameters;
+
+ function withGeneratedZone(
+ OrderParameters memory order,
+ Zone zone,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (OrderParameters memory) {
+ if (zone == Zone.NONE) {
+ return order;
+ } else if (zone == Zone.PASS) {
+ // generate random zone hash
+ bytes32 zoneHash = bytes32(context.randRange(0, type(uint256).max));
+ return
+ order
+ .withOrderType(OrderType.FULL_RESTRICTED)
+ .withZone(address(context.validatorZone))
+ .withZoneHash(zoneHash);
+ } else {
+ revert("ZoneGenerator: invalid Zone");
+ }
+ }
+}
+
+/**
+ * @dev Convert an array of generated OfferItemSpace structs into actual
+ * OfferItem structs.
+ */
+library OfferItemSpaceGenerator {
+ using OfferItemLib for OfferItem;
+
+ using AmountGenerator for OfferItem;
+ using CriteriaGenerator for OfferItem;
+ using TokenIndexGenerator for TokenIndex;
+ using PRNGHelpers for FuzzGeneratorContext;
+
+ function generate(
+ OfferItemSpace[] memory space,
+ FuzzGeneratorContext memory context,
+ bool ensureDirectSupport,
+ uint256 orderIndex
+ ) internal returns (OfferItem[] memory) {
+ uint256 len = bound(space.length, 0, 10);
+
+ OfferItem[] memory offerItems = new OfferItem[](len);
+
+ for (uint256 i; i < len; ++i) {
+ offerItems[i] = generate(
+ space[i],
+ context,
+ ensureDirectSupport,
+ orderIndex,
+ i
+ );
+ }
+ return offerItems;
+ }
+
+ function generate(
+ OfferItemSpace memory space,
+ FuzzGeneratorContext memory context,
+ bool ensureDirectSupport,
+ uint256 orderIndex,
+ uint256 itemIndex
+ ) internal returns (OfferItem memory) {
+ ItemType itemType = space.itemType;
+
+ if (ensureDirectSupport && itemType == ItemType.NATIVE) {
+ itemType = ItemType(context.randRange(1, 5));
+ }
+
+ OfferItem memory offerItem = OfferItemLib
+ .empty()
+ .withItemType(itemType)
+ .withToken(space.tokenIndex.generate(itemType, context))
+ .withGeneratedAmount(space.amount, context);
+
+ return
+ offerItem.withGeneratedIdentifierOrCriteria(
+ itemType,
+ space.criteria,
+ context,
+ orderIndex,
+ itemIndex
+ );
+ }
+}
+
+/**
+ * @dev Convert an array of generated ConsiderationItemSpace structs into actual
+ * ConsiderationItem structs.
+ */
+library ConsiderationItemSpaceGenerator {
+ using ConsiderationItemLib for ConsiderationItem;
+
+ using AmountGenerator for ConsiderationItem;
+ using CriteriaGenerator for ConsiderationItem;
+ using RecipientGenerator for Recipient;
+ using TokenIndexGenerator for TokenIndex;
+
+ function generate(
+ ConsiderationItemSpace[] memory space,
+ FuzzGeneratorContext memory context,
+ address offerer,
+ uint256 orderIndex
+ ) internal returns (ConsiderationItem[] memory) {
+ uint256 len = bound(space.length, 0, 10);
+
+ ConsiderationItem[] memory considerationItems = new ConsiderationItem[](
+ len
+ );
+
+ for (uint256 i; i < len; ++i) {
+ considerationItems[i] = generate(
+ space[i],
+ context,
+ offerer,
+ orderIndex,
+ i
+ );
+ }
+
+ return considerationItems;
+ }
+
+ function generate(
+ ConsiderationItemSpace memory space,
+ FuzzGeneratorContext memory context,
+ address offerer,
+ uint256 orderIndex,
+ uint256 itemIndex
+ ) internal returns (ConsiderationItem memory) {
+ ConsiderationItem memory considerationItem = ConsiderationItemLib
+ .empty()
+ .withItemType(space.itemType)
+ .withToken(space.tokenIndex.generate(space.itemType, context))
+ .withGeneratedAmount(space.amount, context)
+ .withRecipient(space.recipient.generate(context, offerer));
+
+ return
+ considerationItem.withGeneratedIdentifierOrCriteria(
+ space.itemType,
+ space.criteria,
+ context,
+ orderIndex,
+ itemIndex
+ );
+ }
+}
+
+/**
+ * @dev Generate an order signature using the given method and params.
+ */
+library SignatureGenerator {
+ using LibPRNG for LibPRNG.PRNG;
+
+ using AdvancedOrderLib for AdvancedOrder;
+ using OrderParametersLib for OrderParameters;
+
+ using FuzzHelpers for AdvancedOrder;
+ using OffererGenerator for Offerer;
+
+ using ExtraDataGenerator for FuzzGeneratorContext;
+
+ struct SigInfra {
+ bytes32 digest;
+ uint8 v;
+ bytes32 r;
+ bytes32 s;
+ }
+
+ function withGeneratedSignature(
+ AdvancedOrder memory order,
+ SignatureMethod method,
+ EOASignature eoaSignatureType,
+ uint256 bulkSigHeight,
+ uint256 bulkSigIndex,
+ Offerer offerer,
+ address offererAddress,
+ bytes32 orderHash,
+ FuzzGeneratorContext memory context
+ ) internal returns (AdvancedOrder memory) {
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ return order;
+ }
+
+ bytes memory signature;
+
+ SigInfra memory infra = SigInfra({
+ digest: bytes32(0),
+ v: 0,
+ r: bytes32(0),
+ s: bytes32(0)
+ });
+
+ if (method == SignatureMethod.EOA) {
+ uint256 offererKey = offerer.getKey(context);
+
+ if (eoaSignatureType == EOASignature.STANDARD) {
+ infra.digest = _getDigest(orderHash, context);
+ (infra.v, infra.r, infra.s) = context.vm.sign(
+ offererKey,
+ infra.digest
+ );
+ signature = abi.encodePacked(infra.r, infra.s, infra.v);
+
+ _checkSig(
+ infra.digest,
+ infra.v,
+ infra.r,
+ infra.s,
+ offerer,
+ context
+ );
+ return order.withSignature(signature);
+ } else if (eoaSignatureType == EOASignature.EIP2098) {
+ infra.digest = _getDigest(orderHash, context);
+ (infra.v, infra.r, infra.s) = context.vm.sign(
+ offererKey,
+ infra.digest
+ );
+
+ {
+ uint256 yParity;
+ if (infra.v == 27) {
+ yParity = 0;
+ } else {
+ yParity = 1;
+ }
+ uint256 yParityAndS = (yParity << 255) | uint256(infra.s);
+ signature = abi.encodePacked(infra.r, yParityAndS);
+ }
+
+ _checkSig(
+ infra.digest,
+ infra.v,
+ infra.r,
+ infra.s,
+ offerer,
+ context
+ );
+ return order.withSignature(signature);
+ } else if (eoaSignatureType == EOASignature.BULK) {
+ signature = _getBulkSig(
+ order,
+ bulkSigHeight,
+ bulkSigIndex,
+ offererKey,
+ false,
+ context
+ );
+ return order.withSignature(signature);
+ } else if (eoaSignatureType == EOASignature.BULK2098) {
+ signature = _getBulkSig(
+ order,
+ bulkSigHeight,
+ bulkSigIndex,
+ offererKey,
+ true,
+ context
+ );
+ return order.withSignature(signature);
+ } else {
+ revert("SignatureGenerator: Invalid EOA signature type");
+ }
+ } else {
+ // For all others, generate a random signature half the time
+ if (context.prng.next() % 2 == 0) {
+ signature = context.generateRandomBytesArray(1, 4096);
+ }
+
+ if (method == SignatureMethod.EIP1271) {
+ infra.digest = _getDigest(orderHash, context);
+ EIP1271Offerer(payable(offererAddress)).registerSignature(
+ infra.digest,
+ signature
+ );
+ } else if (
+ method != SignatureMethod.SELF_AD_HOC &&
+ method != SignatureMethod.CONTRACT &&
+ method != SignatureMethod.VALIDATE
+ ) {
+ revert("SignatureGenerator: Invalid signature method");
+ }
+
+ return order.withSignature(signature);
+ }
+ }
+
+ function _getDigest(
+ bytes32 orderHash,
+ FuzzGeneratorContext memory context
+ ) internal view returns (bytes32 digest) {
+ (, bytes32 domainSeparator, ) = context.seaport.information();
+ bytes memory message = abi.encodePacked(
+ bytes2(0x1901),
+ domainSeparator,
+ orderHash
+ );
+ digest = keccak256(message);
+ }
+
+ function _getBulkSig(
+ AdvancedOrder memory order,
+ uint256 height,
+ uint256 index,
+ uint256 offererKey,
+ bool useEIP2098,
+ FuzzGeneratorContext memory context
+ ) internal returns (bytes memory signature) {
+ EIP712MerkleTree merkleTree = new EIP712MerkleTree();
+
+ // Pass the hash into `signSparseBulkOrder` instead of the order
+ // components, since we need to neutralize the tip for validation to
+ // work.
+ bytes32 orderHash = order.getTipNeutralizedOrderHash(context.seaport);
+
+ signature = merkleTree.signSparseBulkOrder(
+ context.seaport,
+ offererKey,
+ orderHash,
+ height,
+ uint24(index),
+ useEIP2098
+ );
+ }
+
+ function _checkSig(
+ bytes32 digest,
+ uint8 v,
+ bytes32 r,
+ bytes32 s,
+ Offerer offerer,
+ FuzzGeneratorContext memory context
+ ) internal pure {
+ address recovered = ecrecover(digest, v, r, s);
+ if (recovered != offerer.generate(context) || recovered == address(0)) {
+ revert("SignatureGenerator: Invalid signature");
+ }
+ }
+}
+
+/**
+ * @dev Select a test token by index.
+ */
+library TokenIndexGenerator {
+ function generate(
+ TokenIndex tokenIndex,
+ ItemType itemType,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (address) {
+ if (itemType == ItemType.NATIVE) {
+ return address(0);
+ }
+
+ uint256 i = uint8(tokenIndex);
+
+ if (itemType == ItemType.ERC20) {
+ return address(context.erc20s[i]);
+ } else if (
+ itemType == ItemType.ERC721 ||
+ itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return address(context.erc721s[i]);
+ } else if (
+ itemType == ItemType.ERC1155 ||
+ itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ return address(context.erc1155s[i]);
+ } else {
+ revert("TokenIndexGenerator: Invalid itemType");
+ }
+ }
+}
+
+/**
+ * @dev Generate order start and end timestamps.
+ */
+library TimeGenerator {
+ using LibPRNG for LibPRNG.PRNG;
+ using OrderParametersLib for OrderParameters;
+
+ function withGeneratedTime(
+ OrderParameters memory order,
+ Time time,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (OrderParameters memory) {
+ uint256 low;
+ uint256 high;
+
+ if (time == Time.STARTS_IN_FUTURE) {
+ uint256 a = bound(
+ context.prng.next(),
+ context.timestamp + 1,
+ type(uint40).max
+ );
+ uint256 b = bound(
+ context.prng.next(),
+ context.timestamp + 1,
+ type(uint40).max
+ );
+ low = a < b ? a : b;
+ high = a > b ? a : b;
+ }
+ if (time == Time.EXACT_START) {
+ low = context.timestamp;
+ high = bound(
+ context.prng.next(),
+ context.timestamp + 1,
+ type(uint40).max
+ );
+ }
+ if (time == Time.ONGOING) {
+ low = bound(context.prng.next(), 0, context.timestamp - 1);
+ high = bound(
+ context.prng.next(),
+ context.timestamp + 1,
+ type(uint40).max
+ );
+ }
+ if (time == Time.EXACT_END) {
+ low = bound(context.prng.next(), 0, context.timestamp - 1);
+ high = context.timestamp;
+ }
+ if (time == Time.EXPIRED) {
+ uint256 a = bound(context.prng.next(), 0, context.timestamp - 1);
+ uint256 b = bound(context.prng.next(), 0, context.timestamp - 1);
+ low = a < b ? a : b;
+ high = a > b ? a : b;
+ }
+ return order.withStartTime(low).withEndTime(high);
+ }
+}
+
+/**
+ * @dev Generate offer and consideration item amounts.
+ */
+library AmountGenerator {
+ using OfferItemLib for OfferItem;
+ using ConsiderationItemLib for ConsiderationItem;
+
+ using LibPRNG for LibPRNG.PRNG;
+
+ function withGeneratedAmount(
+ OfferItem memory item,
+ Amount amount,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (OfferItem memory) {
+ // Assumes ordering, might be dangerous
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return item.withStartAmount(1).withEndAmount(1);
+ }
+
+ uint256 a = bound(context.prng.next(), 1, 100_000_000e18);
+ uint256 b = bound(context.prng.next(), 1, 100_000_000e18);
+
+ // TODO: Work out a better way to handle this
+ if (context.basicOrderCategory == BasicOrderCategory.BID) {
+ a *= 1000;
+ b *= 1000;
+ }
+
+ uint256 high = a > b ? a : b;
+ uint256 low = a < b ? a : b;
+
+ if (
+ amount == Amount.FIXED ||
+ context.basicOrderCategory != BasicOrderCategory.NONE
+ ) {
+ return item.withStartAmount(high).withEndAmount(high);
+ }
+ if (amount == Amount.ASCENDING) {
+ return item.withStartAmount(low).withEndAmount(high);
+ }
+ if (amount == Amount.DESCENDING) {
+ return item.withStartAmount(high).withEndAmount(low);
+ }
+ return item;
+ }
+
+ function withGeneratedAmount(
+ ConsiderationItem memory item,
+ Amount amount,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (ConsiderationItem memory) {
+ // Assumes ordering, might be dangerous
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return item.withStartAmount(1).withEndAmount(1);
+ }
+
+ uint256 a = bound(context.prng.next(), 1, 100_000_000e18);
+ uint256 b = bound(context.prng.next(), 1, 100_000_000e18);
+
+ uint256 high = a > b ? a : b;
+ uint256 low = a < b ? a : b;
+
+ if (
+ amount == Amount.FIXED ||
+ context.basicOrderCategory != BasicOrderCategory.NONE
+ ) {
+ return item.withStartAmount(high).withEndAmount(high);
+ }
+ if (amount == Amount.ASCENDING) {
+ return item.withStartAmount(low).withEndAmount(high);
+ }
+ if (amount == Amount.DESCENDING) {
+ return item.withStartAmount(high).withEndAmount(low);
+ }
+ return item;
+ }
+}
+
+/**
+ * @dev Generate a recipient address.
+ */
+library RecipientGenerator {
+ using LibPRNG for LibPRNG.PRNG;
+
+ function generate(
+ Recipient recipient,
+ FuzzGeneratorContext memory context,
+ address offerer
+ ) internal pure returns (address) {
+ if (
+ recipient == Recipient.OFFERER ||
+ context.basicOrderCategory != BasicOrderCategory.NONE
+ ) {
+ return offerer;
+ } else if (recipient == Recipient.RECIPIENT) {
+ return context.caller;
+ } else if (recipient == Recipient.DILLON) {
+ return context.dillon.addr;
+ } else if (recipient == Recipient.EVE) {
+ return context.eve.addr;
+ } else if (recipient == Recipient.FRANK) {
+ return context.frank.addr;
+ } else {
+ revert("Invalid recipient");
+ }
+ }
+}
+
+/**
+ * @dev Generate offer and consideration criteria.
+ */
+library CriteriaGenerator {
+ using OfferItemLib for OfferItem;
+ using ConsiderationItemLib for ConsiderationItem;
+
+ using LibPRNG for LibPRNG.PRNG;
+
+ function withGeneratedIdentifierOrCriteria(
+ ConsiderationItem memory item,
+ ItemType itemType,
+ Criteria criteria,
+ FuzzGeneratorContext memory context,
+ uint256 orderIndex,
+ uint256 itemIndex
+ ) internal returns (ConsiderationItem memory) {
+ if (itemType == ItemType.NATIVE || itemType == ItemType.ERC20) {
+ return item.withIdentifierOrCriteria(0);
+ } else if (itemType == ItemType.ERC721) {
+ item = item.withIdentifierOrCriteria(
+ context.starting721offerIndex++
+ );
+ return item;
+ } else if (itemType == ItemType.ERC1155) {
+ return
+ item.withIdentifierOrCriteria(
+ context.potential1155TokenIds[
+ context.prng.next() %
+ context.potential1155TokenIds.length
+ ]
+ );
+ // Else, item is a criteria-based item
+ } else {
+ if (criteria == Criteria.MERKLE) {
+ // Resolve a random tokenId from a random number of random tokenIds
+ uint256 derivedCriteria = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .generateCriteriaMetadata(
+ context.prng,
+ itemType == ItemType.ERC721_WITH_CRITERIA
+ ? context.starting721offerIndex++
+ : type(uint256).max
+ );
+ // NOTE: resolvable identifier and proof are now registrated on CriteriaResolverHelper
+
+ // Return the item with the Merkle root of the random tokenId
+ // as criteria
+ return item.withIdentifierOrCriteria(derivedCriteria);
+ } else {
+ // Select and register an identifier
+ context.testHelpers.criteriaResolverHelper().generateWildcard(
+ context.prng,
+ itemType == ItemType.ERC721_WITH_CRITERIA
+ ? context.starting721offerIndex++
+ : type(uint256).max,
+ orderIndex,
+ itemIndex,
+ Side.CONSIDERATION
+ );
+
+ // Return wildcard criteria item with identifier 0
+ return item.withIdentifierOrCriteria(0);
+ }
+ }
+ }
+
+ function withGeneratedIdentifierOrCriteria(
+ OfferItem memory item,
+ ItemType itemType,
+ Criteria criteria,
+ FuzzGeneratorContext memory context,
+ uint256 orderIndex,
+ uint256 itemIndex
+ ) internal returns (OfferItem memory) {
+ if (itemType == ItemType.NATIVE || itemType == ItemType.ERC20) {
+ return item.withIdentifierOrCriteria(0);
+ } else if (itemType == ItemType.ERC721) {
+ item = item.withIdentifierOrCriteria(
+ context.starting721offerIndex++
+ );
+ return item;
+ } else if (itemType == ItemType.ERC1155) {
+ return
+ item.withIdentifierOrCriteria(
+ context.potential1155TokenIds[
+ context.prng.next() %
+ context.potential1155TokenIds.length
+ ]
+ );
+ } else {
+ if (criteria == Criteria.MERKLE) {
+ // Resolve a random tokenId from a random number of random tokenIds
+ uint256 derivedCriteria = context
+ .testHelpers
+ .criteriaResolverHelper()
+ .generateCriteriaMetadata(
+ context.prng,
+ itemType == ItemType.ERC721_WITH_CRITERIA
+ ? context.starting721offerIndex++
+ : type(uint256).max
+ );
+ // NOTE: resolvable identifier and proof are now registrated on CriteriaResolverHelper
+
+ // Return the item with the Merkle root of the random tokenId
+ // as criteria
+ return item.withIdentifierOrCriteria(derivedCriteria);
+ } else {
+ // Select and register an identifier
+ context.testHelpers.criteriaResolverHelper().generateWildcard(
+ context.prng,
+ itemType == ItemType.ERC721_WITH_CRITERIA
+ ? context.starting721offerIndex++
+ : type(uint256).max,
+ orderIndex,
+ itemIndex,
+ Side.OFFER
+ );
+
+ // Return wildcard criteria item with identifier 0
+ return item.withIdentifierOrCriteria(0);
+ }
+ }
+ }
+}
+
+/**
+ * @dev Generate offerer address and key.
+ */
+library OffererGenerator {
+ function generate(
+ Offerer offerer,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (address) {
+ if (offerer == Offerer.TEST_CONTRACT) {
+ return context.self;
+ } else if (offerer == Offerer.ALICE) {
+ return context.alice.addr;
+ } else if (offerer == Offerer.BOB) {
+ return context.bob.addr;
+ } else if (offerer == Offerer.CONTRACT_OFFERER) {
+ return address(context.contractOfferer);
+ } else if (offerer == Offerer.EIP1271) {
+ return address(context.eip1271Offerer);
+ } else {
+ revert("OffererGenerator: invalid offerer");
+ }
+ }
+
+ function getKey(
+ Offerer offerer,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (uint256) {
+ if (offerer == Offerer.TEST_CONTRACT) {
+ return 0;
+ } else if (offerer == Offerer.ALICE) {
+ return context.alice.key;
+ } else if (offerer == Offerer.BOB) {
+ return context.bob.key;
+ } else if (offerer == Offerer.CONTRACT_OFFERER) {
+ revert("getKey -- contract offerer");
+ } else {
+ revert("Invalid offerer");
+ }
+ }
+}
+
+/**
+ * @dev Generate a fulfillment recipient address.
+ */
+library FulfillmentRecipientGenerator {
+ function generate(
+ FulfillmentRecipient recipient,
+ FuzzGeneratorContext memory context
+ ) internal pure returns (address) {
+ if (recipient == FulfillmentRecipient.ZERO) {
+ return address(0);
+ } else if (recipient == FulfillmentRecipient.ALICE) {
+ return context.alice.addr;
+ } else if (recipient == FulfillmentRecipient.BOB) {
+ return context.bob.addr;
+ } else if (recipient == FulfillmentRecipient.EVE) {
+ return context.eve.addr;
+ } else {
+ revert("Invalid fulfillment recipient");
+ }
+ }
+}
+
+/**
+ * @dev Generate a caller address.
+ */
+library CallerGenerator {
+ function generate(
+ Caller caller,
+ FuzzGeneratorContext memory context
+ ) internal view returns (address) {
+ if (caller == Caller.TEST_CONTRACT) {
+ return address(this);
+ } else if (caller == Caller.ALICE) {
+ return context.alice.addr;
+ } else if (caller == Caller.BOB) {
+ return context.bob.addr;
+ } else if (caller == Caller.CAROL) {
+ return context.carol.addr;
+ } else if (caller == Caller.DILLON) {
+ return context.dillon.addr;
+ } else if (caller == Caller.EVE) {
+ return context.eve.addr;
+ } else if (caller == Caller.FRANK) {
+ return context.frank.addr;
+ } else {
+ revert("Invalid caller");
+ }
+ }
+}
+
+/**
+ * @dev Helpers for generating random values
+ */
+library PRNGHelpers {
+ using LibPRNG for LibPRNG.PRNG;
+
+ function randEnum(
+ FuzzGeneratorContext memory context,
+ uint8 min,
+ uint8 max
+ ) internal pure returns (uint8) {
+ return uint8(bound(context.prng.next(), min, max));
+ }
+
+ function randRange(
+ FuzzGeneratorContext memory context,
+ uint256 min,
+ uint256 max
+ ) internal pure returns (uint256) {
+ return bound(context.prng.next(), min, max);
+ }
+
+ function rand(
+ FuzzGeneratorContext memory context
+ ) internal pure returns (uint256) {
+ return context.prng.next();
+ }
+
+ function choice(
+ FuzzGeneratorContext memory context,
+ uint256[] memory arr
+ ) internal pure returns (uint256) {
+ return arr[context.prng.next() % arr.length];
+ }
+}
+
+/**
+ * @dev Implementation cribbed from forge-std bound
+ */
+function bound(
+ uint256 x,
+ uint256 min,
+ uint256 max
+) pure returns (uint256 result) {
+ require(min <= max, "Max is less than min.");
+ // If x is between min and max, return x directly. This is to ensure that
+ // dictionary values do not get shifted if the min is nonzero.
+ if (x >= min && x <= max) return x;
+
+ uint256 size = max - min + 1;
+
+ // If the value is 0, 1, 2, 3, warp that to min, min+1, min+2, min+3.
+ // Similarly for the UINT256_MAX side. This helps ensure coverage of the
+ // min/max values.
+ if (x <= 3 && size > x) return min + x;
+ if (x >= type(uint256).max - 3 && size > type(uint256).max - x)
+ return max - (type(uint256).max - x);
+
+ // Otherwise, wrap x into the range [min, max], i.e. the range is inclusive.
+ if (x > max) {
+ uint256 diff = x - max;
+ uint256 rem = diff % size;
+ if (rem == 0) return max;
+ result = min + rem - 1;
+ } else if (x < min) {
+ uint256 diff = min - x;
+ uint256 rem = diff % size;
+ if (rem == 0) return min;
+ result = max - rem + 1;
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzHelpers.sol b/test/foundry/new/helpers/FuzzHelpers.sol
new file mode 100644
index 000000000..eea3fb7d6
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzHelpers.sol
@@ -0,0 +1,970 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrderLib,
+ ConsiderationItemLib,
+ MatchComponent,
+ MatchComponentType,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib,
+ SeaportArrays,
+ ZoneParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ Fulfillment,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem,
+ ZoneParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import {
+ BasicOrderRouteType,
+ BasicOrderType,
+ ItemType,
+ OrderType,
+ Side
+} from "seaport-sol/SeaportEnums.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import {
+ ContractOffererInterface
+} from "seaport-sol/ContractOffererInterface.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { ZoneInterface } from "seaport-sol/ZoneInterface.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { FuzzInscribers } from "./FuzzInscribers.sol";
+
+import { assume } from "./VmUtils.sol";
+
+/**
+ * @dev The "structure" of the order.
+ * - BASIC: adheres to basic construction rules.
+ * - STANDARD: does not adhere to basic construction rules.
+ * - ADVANCED: requires criteria resolution, partial fulfillment, and/or
+ * extraData.
+ */
+enum Structure {
+ BASIC,
+ STANDARD,
+ ADVANCED
+}
+
+/**
+ * @dev The "type" of the order.
+ * - OPEN: FULL_OPEN or PARTIAL_OPEN orders.
+ * - RESTRICTED: FULL_RESTRICTED or PARTIAL_RESTRICTED orders.
+ * - CONTRACT: CONTRACT orders
+ */
+enum Type {
+ OPEN,
+ RESTRICTED,
+ CONTRACT
+}
+
+/**
+ * @dev The "family" of method that can fulfill the order.
+ * - SINGLE: methods that accept a single order.
+ * (fulfillOrder, fulfillAdvancedOrder, fulfillBasicOrder,
+ * fulfillBasicOrder_efficient_6GL6yc)
+ * - COMBINED: methods that accept multiple orders.
+ * (fulfillAvailableOrders, fulfillAvailableAdvancedOrders, matchOrders,
+ * matchAdvancedOrders, cancel, validate)
+ */
+enum Family {
+ SINGLE,
+ COMBINED
+}
+
+/**
+ * @dev The "state" of the order.
+ * - UNUSED: New, not validated, cancelled, or partially/fully filled.
+ * - VALIDATED: Order has been validated, but not cancelled or filled.
+ * - CANCELLED: Order has been cancelled.
+ * - PARTIALLY_FILLED: Order is partially filled.
+ * - FULLY_FILLED: Order is fully filled.
+ */
+enum State {
+ UNUSED,
+ VALIDATED,
+ CANCELLED,
+ PARTIALLY_FILLED,
+ FULLY_FILLED
+}
+
+/**
+ * @dev The "result" of execution.
+ * - FULFILLMENT: Order should be fulfilled.
+ * - UNAVAILABLE: Order should be skipped.
+ * - VALIDATE: Order should be validated.
+ * - CANCEL: Order should be cancelled.
+ */
+enum Result {
+ FULFILLMENT,
+ UNAVAILABLE,
+ VALIDATE,
+ CANCEL
+}
+
+/**
+ * @notice Stateless helpers for Fuzz tests.
+ */
+library FuzzHelpers {
+ using OrderLib for Order;
+ using OrderComponentsLib for OrderComponents;
+ using OrderParametersLib for OrderParameters;
+ using OfferItemLib for OfferItem[];
+ using ConsiderationItemLib for ConsiderationItem[];
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using ZoneParametersLib for AdvancedOrder;
+ using ZoneParametersLib for AdvancedOrder[];
+ using FuzzInscribers for AdvancedOrder;
+
+ event ExpectedGenerateOrderDataHash(bytes32 dataHash);
+
+ function _gcd(uint256 a, uint256 b) internal pure returns (uint256) {
+ if (b == 0) {
+ return a;
+ } else {
+ return _gcd(b, a % b);
+ }
+ }
+
+ function _lcm(
+ uint256 a,
+ uint256 b,
+ uint256 gcdValue
+ ) internal returns (uint256 result) {
+ bool success;
+ (success, result) = _tryMul(a, b);
+
+ if (success) {
+ return result / gcdValue;
+ } else {
+ uint256 candidate = a / gcdValue;
+ if (candidate * gcdValue == a) {
+ (success, result) = _tryMul(candidate, b);
+ if (success) {
+ return result;
+ } else {
+ candidate = b / gcdValue;
+ if (candidate * gcdValue == b) {
+ (success, result) = _tryMul(candidate, a);
+ if (success) {
+ return result;
+ }
+ }
+ }
+ }
+
+ assume(false, "cannot_derive_lcm_for_partial_fill");
+ }
+
+ return result / gcdValue;
+ }
+
+ function _tryMul(
+ uint256 a,
+ uint256 b
+ ) internal pure returns (bool, uint256) {
+ unchecked {
+ if (a == 0) {
+ return (true, 0);
+ }
+
+ uint256 c = a * b;
+
+ if (c / a != b) {
+ return (false, 0);
+ }
+
+ return (true, c);
+ }
+ }
+
+ function findSmallestDenominator(
+ uint256[] memory numbers
+ ) internal returns (uint256 denominator) {
+ require(
+ numbers.length > 0,
+ "FuzzHelpers: Input array must not be empty"
+ );
+
+ bool initialValueSet = false;
+
+ uint256 gcdValue;
+ uint256 lcmValue;
+
+ for (uint256 i = 0; i < numbers.length; i++) {
+ uint256 number = numbers[i];
+
+ if (number == 0) {
+ continue;
+ }
+
+ if (!initialValueSet) {
+ initialValueSet = true;
+ gcdValue = number;
+ lcmValue = number;
+ continue;
+ }
+
+ gcdValue = _gcd(gcdValue, number);
+ lcmValue = _lcm(lcmValue, number, gcdValue);
+ }
+
+ if (gcdValue == 0) {
+ return 0;
+ }
+
+ denominator = lcmValue / gcdValue;
+
+ // TODO: this should support up to uint120, work out
+ // how to fly closer to the sun on this
+ if (denominator > type(uint80).max) {
+ return 0;
+ }
+ }
+
+ function getTotalFractionalizableAmounts(
+ OrderParameters memory order
+ ) internal pure returns (uint256) {
+ if (
+ order.orderType == OrderType.PARTIAL_OPEN ||
+ order.orderType == OrderType.PARTIAL_RESTRICTED
+ ) {
+ return 2 * (order.offer.length + order.consideration.length);
+ }
+
+ return 0;
+ }
+
+ function getSmallestDenominator(
+ OrderParameters memory order
+ ) internal returns (uint256 smallestDenominator, bool canScaleUp) {
+ canScaleUp = true;
+
+ uint256 totalFractionalizableAmounts = (
+ getTotalFractionalizableAmounts(order)
+ );
+
+ if (totalFractionalizableAmounts != 0) {
+ uint256[] memory numbers = new uint256[](
+ totalFractionalizableAmounts
+ );
+
+ uint256 numberIndex = 0;
+
+ for (uint256 j = 0; j < order.offer.length; ++j) {
+ OfferItem memory item = order.offer[j];
+ numbers[numberIndex++] = item.startAmount;
+ numbers[numberIndex++] = item.endAmount;
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ canScaleUp = false;
+ }
+ }
+
+ for (uint256 j = 0; j < order.consideration.length; ++j) {
+ ConsiderationItem memory item = order.consideration[j];
+ numbers[numberIndex++] = item.startAmount;
+ numbers[numberIndex++] = item.endAmount;
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ canScaleUp = false;
+ }
+ }
+
+ smallestDenominator = findSmallestDenominator(numbers);
+ } else {
+ smallestDenominator = 0;
+ }
+ }
+
+ /**
+ * @dev Get the "quantity" of orders to process, equal to the number of
+ * orders in the provided array.
+ *
+ * @param orders array of AdvancedOrders.
+ *
+ * @custom:return quantity of orders to process.
+ */
+ function getQuantity(
+ AdvancedOrder[] memory orders
+ ) internal pure returns (uint256) {
+ return orders.length;
+ }
+
+ /**
+ * @dev Get the "family" of method that can fulfill these orders.
+ *
+ * @param orders array of AdvancedOrders.
+ *
+ * @custom:return family of method that can fulfill these orders.
+ */
+ function getFamily(
+ AdvancedOrder[] memory orders
+ ) internal pure returns (Family) {
+ uint256 quantity = getQuantity(orders);
+ if (quantity > 1) {
+ return Family.COMBINED;
+ }
+ return Family.SINGLE;
+ }
+
+ /**
+ * @dev Get the "state" of the given order.
+ *
+ * @param order an AdvancedOrder.
+ * @param seaport a SeaportInterface, either reference or optimized.
+ *
+ * @custom:return state of the given order.
+ */
+ function getState(
+ AdvancedOrder memory order,
+ SeaportInterface seaport
+ ) internal view returns (State) {
+ uint256 counter = seaport.getCounter(order.parameters.offerer);
+ bytes32 orderHash = seaport.getOrderHash(
+ order.parameters.toOrderComponents(counter)
+ );
+ (
+ bool isValidated,
+ bool isCancelled,
+ uint256 totalFilled,
+ uint256 totalSize
+ ) = seaport.getOrderStatus(orderHash);
+
+ if (totalFilled != 0 && totalSize != 0 && totalFilled == totalSize)
+ return State.FULLY_FILLED;
+ if (totalFilled != 0 && totalSize != 0) return State.PARTIALLY_FILLED;
+ if (isCancelled) return State.CANCELLED;
+ if (isValidated) return State.VALIDATED;
+ return State.UNUSED;
+ }
+
+ /**
+ * @dev Get the "type" of the given order.
+ *
+ * @param order an AdvancedOrder.
+ *
+ * @custom:return type of the given order (in the sense of the enum defined
+ * above in this file, not ConsiderationStructs' OrderType).
+ */
+ function getType(AdvancedOrder memory order) internal pure returns (Type) {
+ OrderType orderType = order.parameters.orderType;
+ if (
+ orderType == OrderType.FULL_OPEN ||
+ orderType == OrderType.PARTIAL_OPEN
+ ) {
+ return Type.OPEN;
+ } else if (
+ orderType == OrderType.FULL_RESTRICTED ||
+ orderType == OrderType.PARTIAL_RESTRICTED
+ ) {
+ return Type.RESTRICTED;
+ } else if (orderType == OrderType.CONTRACT) {
+ return Type.CONTRACT;
+ } else {
+ revert("FuzzEngine: Type not found");
+ }
+ }
+
+ /**
+ * @dev Get the "structure" of the given order.
+ *
+ * @param order an AdvancedOrder.
+ * @param seaport a SeaportInterface, either reference or optimized.
+ *
+ * @custom:return structure of the given order.
+ */
+ function getStructure(
+ AdvancedOrder memory order,
+ address seaport
+ ) internal view returns (Structure) {
+ // If the order has extraData, it's advanced
+ if (order.extraData.length > 0) return Structure.ADVANCED;
+
+ // If the order has numerator or denominator, it's advanced
+ if (order.numerator != 0 || order.denominator != 0) {
+ if (order.numerator < order.denominator) {
+ return Structure.ADVANCED;
+ }
+ }
+
+ (bool hasCriteria, bool hasNonzeroCriteria) = _checkCriteria(order);
+ bool isContractOrder = order.parameters.orderType == OrderType.CONTRACT;
+
+ // If any non-contract item has criteria, it's advanced,
+ if (hasCriteria) {
+ // Unless it's a contract order
+ if (isContractOrder) {
+ // And the contract order's critera are all zero
+ if (hasNonzeroCriteria) {
+ return Structure.ADVANCED;
+ }
+ } else {
+ return Structure.ADVANCED;
+ }
+ }
+
+ if (getBasicOrderTypeEligibility(order, seaport)) {
+ return Structure.BASIC;
+ }
+
+ return Structure.STANDARD;
+ }
+
+ function getStructure(
+ AdvancedOrder[] memory orders,
+ address seaport
+ ) internal view returns (Structure) {
+ if (orders.length == 1) {
+ return getStructure(orders[0], seaport);
+ }
+
+ for (uint256 i; i < orders.length; i++) {
+ Structure structure = getStructure(orders[i], seaport);
+ if (structure == Structure.ADVANCED) {
+ return Structure.ADVANCED;
+ }
+ }
+
+ return Structure.STANDARD;
+ }
+
+ /**
+ * @dev Inspect an AdvancedOrder and check that it is eligible for the
+ * fulfillBasic functions.
+ *
+ * @param order an AdvancedOrder.
+ * @param seaport a SeaportInterface, either reference or optimized.
+ *
+ * @custom:return true if the order is eligible for the fulfillBasic
+ * functions, false otherwise.
+ */
+ function getBasicOrderTypeEligibility(
+ AdvancedOrder memory order,
+ address seaport
+ ) internal view returns (bool) {
+ uint256 i;
+ ConsiderationItem[] memory consideration = order
+ .parameters
+ .consideration;
+ OfferItem[] memory offer = order.parameters.offer;
+
+ // Order must contain exactly one offer item and one or more
+ // consideration items.
+ if (offer.length != 1) {
+ return false;
+ }
+ if (
+ consideration.length == 0 ||
+ order.parameters.totalOriginalConsiderationItems == 0
+ ) {
+ return false;
+ }
+
+ // The order cannot have a contract order type.
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ return false;
+
+ // Note: the order type is combined with the “route” into a single
+ // BasicOrderType with a value between 0 and 23; there are 4
+ // supported order types (full open, partial open, full restricted,
+ // partial restricted) and 6 routes (ETH ⇒ ERC721, ETH ⇒ ERC1155,
+ // ERC20 ⇒ ERC721, ERC20 ⇒ ERC1155, ERC721 ⇒ ERC20, ERC1155 ⇒ ERC20)
+ }
+
+ // Order cannot specify a partial fraction to fill.
+ if (order.denominator > 1 && (order.numerator < order.denominator)) {
+ return false;
+ }
+
+ // Order cannot be partially filled.
+ SeaportInterface seaportInterface = SeaportInterface(seaport);
+ uint256 counter = seaportInterface.getCounter(order.parameters.offerer);
+ OrderComponents memory orderComponents = order
+ .parameters
+ .toOrderComponents(counter);
+ bytes32 orderHash = seaportInterface.getOrderHash(orderComponents);
+ (, , uint256 totalFilled, uint256 totalSize) = seaportInterface
+ .getOrderStatus(orderHash);
+
+ if (totalFilled != totalSize) {
+ return false;
+ }
+
+ // Order cannot contain any criteria-based items.
+ for (i = 0; i < consideration.length; ++i) {
+ if (
+ consideration[i].itemType == ItemType.ERC721_WITH_CRITERIA ||
+ consideration[i].itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ return false;
+ }
+ }
+
+ if (
+ offer[0].itemType == ItemType.ERC721_WITH_CRITERIA ||
+ offer[0].itemType == ItemType.ERC1155_WITH_CRITERIA
+ ) {
+ return false;
+ }
+
+ // Order cannot contain any extraData.
+ if (order.extraData.length != 0) {
+ return false;
+ }
+
+ // Order must contain exactly one NFT item.
+ uint256 totalNFTs;
+ if (
+ offer[0].itemType == ItemType.ERC721 ||
+ offer[0].itemType == ItemType.ERC1155
+ ) {
+ totalNFTs += 1;
+ }
+ for (i = 0; i < consideration.length; ++i) {
+ if (
+ consideration[i].itemType == ItemType.ERC721 ||
+ consideration[i].itemType == ItemType.ERC1155
+ ) {
+ totalNFTs += 1;
+ }
+ }
+
+ if (totalNFTs != 1) {
+ return false;
+ }
+
+ // The one NFT must appear either as the offer item or as the first
+ // consideration item.
+ if (
+ offer[0].itemType != ItemType.ERC721 &&
+ offer[0].itemType != ItemType.ERC1155 &&
+ consideration[0].itemType != ItemType.ERC721 &&
+ consideration[0].itemType != ItemType.ERC1155
+ ) {
+ return false;
+ }
+
+ // All items that are not the NFT must share the same item type and
+ // token (and the identifier must be zero).
+ if (
+ offer[0].itemType == ItemType.ERC721 ||
+ offer[0].itemType == ItemType.ERC1155
+ ) {
+ ItemType expectedItemType = consideration[0].itemType;
+ address expectedToken = consideration[0].token;
+
+ for (i = 0; i < consideration.length; ++i) {
+ if (consideration[i].itemType != expectedItemType) {
+ return false;
+ }
+
+ if (consideration[i].token != expectedToken) {
+ return false;
+ }
+
+ if (consideration[i].identifierOrCriteria != 0) {
+ return false;
+ }
+ }
+ }
+
+ if (
+ consideration[0].itemType == ItemType.ERC721 ||
+ consideration[0].itemType == ItemType.ERC1155
+ ) {
+ if (consideration.length >= 2) {
+ ItemType expectedItemType = offer[0].itemType;
+ address expectedToken = offer[0].token;
+ for (i = 1; i < consideration.length; ++i) {
+ if (consideration[i].itemType != expectedItemType) {
+ return false;
+ }
+
+ if (consideration[i].token != expectedToken) {
+ return false;
+ }
+
+ if (consideration[i].identifierOrCriteria != 0) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // The offerer must be the recipient of the first consideration item.
+ if (consideration[0].recipient != order.parameters.offerer) {
+ return false;
+ }
+
+ // If the NFT is the first consideration item, the sum of the amounts of
+ // all the other consideration items cannot exceed the amount of the
+ // offer item.
+ if (
+ consideration[0].itemType == ItemType.ERC721 ||
+ consideration[0].itemType == ItemType.ERC1155
+ ) {
+ uint256 totalConsiderationAmount;
+ for (i = 1; i < consideration.length; ++i) {
+ totalConsiderationAmount += consideration[i].startAmount;
+ }
+
+ if (totalConsiderationAmount > offer[0].startAmount) {
+ return false;
+ }
+
+ // Note: these cases represent a “bid” for an NFT, and the non-NFT
+ // consideration items (i.e. the “payment tokens”) are sent directly
+ // from the offerer to each recipient; this means that the fulfiller
+ // accepting the bid does not need to have approval set for the
+ // payment tokens.
+ }
+
+ // All items must have startAmount == endAmount
+ if (offer[0].startAmount != offer[0].endAmount) {
+ return false;
+ }
+ for (i = 0; i < consideration.length; ++i) {
+ if (consideration[i].startAmount != consideration[i].endAmount) {
+ return false;
+ }
+ }
+
+ // The offer item cannot have a native token type.
+ if (offer[0].itemType == ItemType.NATIVE) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @dev Get the BasicOrderType for a given advanced order.
+ *
+ * @param order The advanced order.
+ *
+ * @return basicOrderType The BasicOrderType.
+ */
+ function getBasicOrderType(
+ AdvancedOrder memory order
+ ) internal pure returns (BasicOrderType basicOrderType) {
+ // Get the route (ETH ⇒ ERC721, etc.) for the order.
+ BasicOrderRouteType route = getBasicOrderRouteType(order);
+
+ // Get the order type (restricted, etc.) for the order.
+ OrderType orderType = order.parameters.orderType;
+
+ // Multiply the route by 4 and add the order type to get the
+ // BasicOrderType.
+ assembly {
+ basicOrderType := add(orderType, mul(route, 4))
+ }
+ }
+
+ /**
+ * @dev Get the BasicOrderRouteType for a given advanced order.
+ *
+ * @param order The advanced order.
+ *
+ * @return route The BasicOrderRouteType.
+ */
+ function getBasicOrderRouteType(
+ AdvancedOrder memory order
+ ) internal pure returns (BasicOrderRouteType route) {
+ // Get the route (ETH ⇒ ERC721, etc.) for the order.
+ ItemType providingItemType = order.parameters.consideration[0].itemType;
+ ItemType offeredItemType = order.parameters.offer[0].itemType;
+
+ if (providingItemType == ItemType.NATIVE) {
+ if (offeredItemType == ItemType.ERC721) {
+ route = BasicOrderRouteType.ETH_TO_ERC721;
+ } else if (offeredItemType == ItemType.ERC1155) {
+ route = BasicOrderRouteType.ETH_TO_ERC1155;
+ }
+ } else if (providingItemType == ItemType.ERC20) {
+ if (offeredItemType == ItemType.ERC721) {
+ route = BasicOrderRouteType.ERC20_TO_ERC721;
+ } else if (offeredItemType == ItemType.ERC1155) {
+ route = BasicOrderRouteType.ERC20_TO_ERC1155;
+ }
+ } else if (providingItemType == ItemType.ERC721) {
+ if (offeredItemType == ItemType.ERC20) {
+ route = BasicOrderRouteType.ERC721_TO_ERC20;
+ }
+ } else if (providingItemType == ItemType.ERC1155) {
+ if (offeredItemType == ItemType.ERC20) {
+ route = BasicOrderRouteType.ERC1155_TO_ERC20;
+ }
+ }
+ }
+
+ /**
+ * @dev Derive ZoneParameters from a given restricted order and return
+ * the expected calldata hash for the call to validateOrder.
+ *
+ * @param orders The restricted orders.
+ * @param seaport The Seaport address.
+ * @param fulfiller The fulfiller.
+ * @param maximumFulfilled The maximum number of orders to fulfill.
+ * @param criteriaResolvers The criteria resolvers.
+ * @param maximumFulfilled The maximum number of orders to fulfill.
+ * @param unavailableReasons The availability status.
+ *
+ * @return calldataHashes The derived calldata hashes.
+ */
+ function getExpectedZoneCalldataHash(
+ AdvancedOrder[] memory orders,
+ address seaport,
+ address fulfiller,
+ CriteriaResolver[] memory criteriaResolvers,
+ uint256 maximumFulfilled,
+ UnavailableReason[] memory unavailableReasons
+ ) internal view returns (bytes32[] memory calldataHashes) {
+ calldataHashes = new bytes32[](orders.length);
+
+ ZoneParameters[] memory zoneParameters = orders.getZoneParameters(
+ fulfiller,
+ maximumFulfilled,
+ seaport,
+ criteriaResolvers,
+ unavailableReasons
+ );
+
+ for (uint256 i; i < zoneParameters.length; ++i) {
+ // Derive the expected calldata hash for the call to validateOrder
+ calldataHashes[i] = keccak256(
+ abi.encodeCall(ZoneInterface.validateOrder, (zoneParameters[i]))
+ );
+ }
+ }
+
+ /**
+ * @dev Get the orderHashes of an array of AdvancedOrders and return
+ * the expected calldata hashes for calls to validateOrder.
+ */
+ function getExpectedContractOffererCalldataHashes(
+ FuzzTestContext memory context
+ ) internal pure returns (bytes32[2][] memory) {
+ AdvancedOrder[] memory orders = context.executionState.orders;
+ address fulfiller = context.executionState.caller;
+
+ bytes32[] memory orderHashes = new bytes32[](orders.length);
+ for (uint256 i = 0; i < orderHashes.length; ++i) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason !=
+ UnavailableReason.AVAILABLE
+ ) {
+ orderHashes[i] = bytes32(0);
+ } else {
+ orderHashes[i] = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+ }
+ }
+
+ bytes32[2][] memory calldataHashes = new bytes32[2][](orders.length);
+
+ // Iterate over contract orders to derive calldataHashes
+ for (uint256 i; i < orders.length; ++i) {
+ AdvancedOrder memory order = orders[i];
+
+ // calldataHashes for non-contract orders should be null
+ if (getType(order) != Type.CONTRACT) {
+ continue;
+ }
+
+ SpentItem[] memory minimumReceived = order
+ .parameters
+ .offer
+ .toSpentItemArray();
+
+ SpentItem[] memory maximumSpent = order
+ .parameters
+ .consideration
+ .toSpentItemArray();
+
+ // Derive the expected calldata hash for the call to generateOrder
+ calldataHashes[i][0] = keccak256(
+ abi.encodeCall(
+ ContractOffererInterface.generateOrder,
+ (fulfiller, minimumReceived, maximumSpent, order.extraData)
+ )
+ );
+
+ uint256 shiftedOfferer = uint256(
+ uint160(order.parameters.offerer)
+ ) << 96;
+
+ // Get counter of the order offerer
+ uint256 counter = shiftedOfferer ^ uint256(orderHashes[i]);
+
+ // Derive the expected calldata hash for the call to ratifyOrder
+ calldataHashes[i][1] = keccak256(
+ abi.encodeCall(
+ ContractOffererInterface.ratifyOrder,
+ (
+ context.executionState.orderDetails[i].offer,
+ context.executionState.orderDetails[i].consideration,
+ order.extraData,
+ orderHashes,
+ counter
+ )
+ )
+ );
+ }
+
+ return calldataHashes;
+ }
+
+ /**
+ * @dev Call `validate` on an AdvancedOrders and return the success bool.
+ * This function can be treated as a wrapper around Seaport's
+ * `validate` function. It is used to validate an AdvancedOrder that
+ * thas a tip added onto it. Calling it on an AdvancedOrder that does
+ * not have a tip is identical to calling Seaport's `validate` function
+ * directly. Seaport handles tips gracefully inside of the top level
+ * fulfill and match functions, but since we're adding tips early in
+ * the fuzz test lifecycle, it's necessary to flip them back and forth
+ * when we need to validate orders. Note: they're two different orders,
+ * so e.g. cancelling or validating order with a tip on it is not the
+ * same as cancelling the order without a tip on it.
+ */
+ function validateTipNeutralizedOrder(
+ AdvancedOrder memory order,
+ FuzzTestContext memory context
+ ) internal returns (bool validated) {
+ order.inscribeOrderStatusValidated(true, context.seaport);
+ return true;
+ }
+
+ function cancelTipNeutralizedOrder(
+ AdvancedOrder memory order,
+ SeaportInterface seaport
+ ) internal view returns (bytes32 orderHash) {
+ // Get the orderHash using the tweaked OrderComponents.
+ orderHash = order.getTipNeutralizedOrderHash(seaport);
+ }
+
+ /**
+ * @dev Check all offer and consideration items for criteria.
+ *
+ * @param order The advanced order.
+ *
+ * @return hasCriteria Whether any offer or consideration item has
+ * criteria.
+ * @return hasNonzeroCriteria Whether any offer or consideration item has
+ * nonzero criteria.
+ */
+ function _checkCriteria(
+ AdvancedOrder memory order
+ ) internal pure returns (bool hasCriteria, bool hasNonzeroCriteria) {
+ // Check if any offer item has criteria
+ OfferItem[] memory offer = order.parameters.offer;
+ for (uint256 i; i < offer.length; ++i) {
+ OfferItem memory offerItem = offer[i];
+ ItemType itemType = offerItem.itemType;
+ hasCriteria = (itemType == ItemType.ERC721_WITH_CRITERIA ||
+ itemType == ItemType.ERC1155_WITH_CRITERIA);
+ if (hasCriteria) {
+ return (hasCriteria, offerItem.identifierOrCriteria != 0);
+ }
+ }
+
+ // Check if any consideration item has criteria
+ ConsiderationItem[] memory consideration = order
+ .parameters
+ .consideration;
+ for (uint256 i; i < consideration.length; ++i) {
+ ConsiderationItem memory considerationItem = consideration[i];
+ ItemType itemType = considerationItem.itemType;
+ hasCriteria = (itemType == ItemType.ERC721_WITH_CRITERIA ||
+ itemType == ItemType.ERC1155_WITH_CRITERIA);
+ if (hasCriteria) {
+ return (
+ hasCriteria,
+ considerationItem.identifierOrCriteria != 0
+ );
+ }
+ }
+
+ return (false, false);
+ }
+}
+
+function _locateCurrentAmount(
+ uint256 startAmount,
+ uint256 endAmount,
+ uint256 startTime,
+ uint256 endTime,
+ bool roundUp
+) view returns (uint256 amount) {
+ // Only modify end amount if it doesn't already equal start amount.
+ if (startAmount != endAmount) {
+ // Declare variables to derive in the subsequent unchecked scope.
+ uint256 duration;
+ uint256 elapsed;
+ uint256 remaining;
+
+ // Skip underflow checks as startTime <= block.timestamp < endTime.
+ unchecked {
+ // Derive the duration for the order and place it on the stack.
+ duration = endTime - startTime;
+
+ // Derive time elapsed since the order started & place on stack.
+ elapsed = block.timestamp - startTime;
+
+ // Derive time remaining until order expires and place on stack.
+ remaining = duration - elapsed;
+ }
+
+ // Aggregate new amounts weighted by time with rounding factor.
+ uint256 totalBeforeDivision = ((startAmount * remaining) +
+ (endAmount * elapsed));
+
+ // Use assembly to combine operations and skip divide-by-zero check.
+ assembly {
+ // Multiply by iszero(iszero(totalBeforeDivision)) to ensure
+ // amount is set to zero if totalBeforeDivision is zero,
+ // as intermediate overflow can occur if it is zero.
+ amount := mul(
+ iszero(iszero(totalBeforeDivision)),
+ // Subtract 1 from the numerator and add 1 to the result if
+ // roundUp is true to get the proper rounding direction.
+ // Division is performed with no zero check as duration
+ // cannot be zero as long as startTime < endTime.
+ add(div(sub(totalBeforeDivision, roundUp), duration), roundUp)
+ )
+ }
+
+ // Return the current amount.
+ return amount;
+ }
+
+ // Return the original amount as startAmount == endAmount.
+ return endAmount;
+}
diff --git a/test/foundry/new/helpers/FuzzInscribers.sol b/test/foundry/new/helpers/FuzzInscribers.sol
new file mode 100644
index 000000000..136ce896b
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzInscribers.sol
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { vm } from "./VmUtils.sol";
+
+import { AdvancedOrder, OrderStatus } from "seaport-sol/SeaportStructs.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { AdvancedOrderLib } from "seaport-sol/SeaportSol.sol";
+
+/**
+ * @notice "Inscribers" are helpers that set Seaport state directly by modifying
+ * contract storage. For example, changing order status, setting
+ * contract nonces, and setting counters.
+ */
+library FuzzInscribers {
+ using AdvancedOrderLib for AdvancedOrder;
+
+ uint256 constant wipeDenominatorMask =
+ 0x000000000000000000000000000000ffffffffffffffffffffffffffffffffff;
+
+ uint256 constant wipeNumeratorMask =
+ 0xffffffffffffffffffffffffffffff000000000000000000000000000000ffff;
+
+ /**
+ * @dev Inscribe an entire order status struct.
+ *
+ * @param order The order to inscribe.
+ * @param orderStatus The order status to inscribe.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeOrderStatusComprehensive(
+ AdvancedOrder memory order,
+ OrderStatus memory orderStatus,
+ SeaportInterface seaport
+ ) internal {
+ inscribeOrderStatusValidated(order, orderStatus.isValidated, seaport);
+ inscribeOrderStatusCancelled(order, orderStatus.isCancelled, seaport);
+ inscribeOrderStatusNumerator(order, orderStatus.numerator, seaport);
+ inscribeOrderStatusDenominator(order, orderStatus.denominator, seaport);
+ }
+
+ /**
+ * @dev Inscribe an entire order status struct, except for the numerator.
+ *
+ * @param order The order to inscribe.
+ * @param numerator The numerator to inscribe.
+ * @param denominator The denominator to inscribe.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeOrderStatusNumeratorAndDenominator(
+ AdvancedOrder memory order,
+ uint120 numerator,
+ uint120 denominator,
+ SeaportInterface seaport
+ ) internal {
+ inscribeOrderStatusNumerator(order, numerator, seaport);
+ inscribeOrderStatusDenominator(order, denominator, seaport);
+ }
+
+ /**
+ * @dev Inscribe just the `isValidated` field of an order status struct.
+ *
+ * @param order The order to inscribe.
+ * @param isValidated The boolean value to set for the `isValidated` field.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeOrderStatusValidated(
+ AdvancedOrder memory order,
+ bool isValidated,
+ SeaportInterface seaport
+ ) internal {
+ // Get the order hash.
+ bytes32 orderHash = order.getTipNeutralizedOrderHash(seaport);
+
+ bytes32 orderHashStorageSlot = _getStorageSlotForOrderHash(
+ orderHash,
+ seaport
+ );
+ bytes32 rawOrderStatus = vm.load(
+ address(seaport),
+ orderHashStorageSlot
+ );
+
+ // NOTE: This will permit putting an order in a 0x0...0101 state.
+ // In other words, it will allow you to inscribe an order as
+ // both validated and cancelled at the same time, which is not
+ // possible in actual Seaport.
+
+ assembly {
+ rawOrderStatus := and(
+ sub(0, add(1, iszero(isValidated))),
+ or(isValidated, rawOrderStatus)
+ )
+ }
+
+ // Store the new raw order status.
+ vm.store(address(seaport), orderHashStorageSlot, rawOrderStatus);
+
+ // Get the fresh baked order status straight from Seaport.
+ (bool isValidatedOrganicValue, , , ) = seaport.getOrderStatus(
+ orderHash
+ );
+
+ if (isValidated != isValidatedOrganicValue) {
+ revert("FuzzInscribers/inscribeOrderStatusValidated: Mismatch");
+ }
+ }
+
+ /**
+ * @dev Inscribe just the `isCancelled` field of an order status struct.
+ *
+ * @param order The order to inscribe.
+ * @param isCancelled The boolean value to set for the `isCancelled` field.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeOrderStatusCancelled(
+ AdvancedOrder memory order,
+ bool isCancelled,
+ SeaportInterface seaport
+ ) internal {
+ // Get the order hash.
+ bytes32 orderHash = order.getTipNeutralizedOrderHash(seaport);
+ inscribeOrderStatusCancelled(orderHash, isCancelled, seaport);
+ }
+
+ function inscribeOrderStatusCancelled(
+ bytes32 orderHash,
+ bool isCancelled,
+ SeaportInterface seaport
+ ) internal {
+ bytes32 orderHashStorageSlot = _getStorageSlotForOrderHash(
+ orderHash,
+ seaport
+ );
+ bytes32 rawOrderStatus = vm.load(
+ address(seaport),
+ orderHashStorageSlot
+ );
+
+ // NOTE: This will not permit putting an order in a 0x0...0101 state. If
+ // An order that's validated is inscribed as cancelled, it will
+ // be devalidated also.
+
+ assembly {
+ rawOrderStatus := and(
+ sub(sub(0, 1), mul(iszero(isCancelled), 0x100)),
+ or(
+ shl(8, isCancelled),
+ and(mul(sub(0, 0x102), isCancelled), rawOrderStatus)
+ )
+ )
+ }
+
+ // Store the new raw order status.
+ vm.store(address(seaport), orderHashStorageSlot, rawOrderStatus);
+
+ // Get the fresh baked order status straight from Seaport.
+ (
+ bool isValidatedOrganicValue,
+ bool isCancelledOrganicValue,
+ ,
+
+ ) = seaport.getOrderStatus(orderHash);
+
+ if (isCancelled != isCancelledOrganicValue) {
+ revert("FuzzInscribers/inscribeOrderStatusCancelled: Mismatch");
+ }
+
+ if (isCancelledOrganicValue && isValidatedOrganicValue) {
+ revert(
+ "FuzzInscribers/inscribeOrderStatusCancelled: Invalid state"
+ );
+ }
+ }
+
+ /**
+ * @dev Inscribe just the `numerator` field of an order status struct.
+ *
+ * @param order The order to inscribe.
+ * @param numerator The numerator to inscribe.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeOrderStatusNumerator(
+ AdvancedOrder memory order,
+ uint120 numerator,
+ SeaportInterface seaport
+ ) internal {
+ // Get the order hash, storage slot, and raw order status.
+ bytes32 orderHash = order.getTipNeutralizedOrderHash(seaport);
+ bytes32 orderHashStorageSlot = _getStorageSlotForOrderHash(
+ orderHash,
+ seaport
+ );
+ bytes32 rawOrderStatus = vm.load(
+ address(seaport),
+ orderHashStorageSlot
+ );
+
+ // Convert the numerator to bytes.
+ bytes32 numeratorBytes = bytes32(uint256(numerator));
+
+ assembly {
+ // Shift the inputted numerator bytes to the left by 16 so they're
+ // lined up in the right spot.
+ numeratorBytes := shl(16, numeratorBytes)
+ // Zero out the existing numerator bytes.
+ rawOrderStatus := and(rawOrderStatus, wipeNumeratorMask)
+ // Or the inputted numerator bytes into the raw order status.
+ rawOrderStatus := or(rawOrderStatus, numeratorBytes)
+ }
+
+ // Store the new raw order status.
+ vm.store(address(seaport), orderHashStorageSlot, rawOrderStatus);
+ }
+
+ /**
+ * @dev Inscribe just the `denominator` field of an order status struct.
+ *
+ * @param order The order to inscribe.
+ * @param denominator The denominator to inscribe.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeOrderStatusDenominator(
+ AdvancedOrder memory order,
+ uint120 denominator,
+ SeaportInterface seaport
+ ) internal {
+ // Get the order hash, storage slot, and raw order status.
+ bytes32 orderHash = order.getTipNeutralizedOrderHash(seaport);
+ bytes32 orderHashStorageSlot = _getStorageSlotForOrderHash(
+ orderHash,
+ seaport
+ );
+ bytes32 rawOrderStatus = vm.load(
+ address(seaport),
+ orderHashStorageSlot
+ );
+
+ // Convert the denominator to bytes.
+ bytes32 denominatorBytes = bytes32(uint256(denominator));
+
+ assembly {
+ // Shift the inputted denominator bytes to the left by 136 so
+ // they're lined up in the right spot.
+ denominatorBytes := shl(136, denominatorBytes)
+ // Zero out the existing denominator bytes.
+ rawOrderStatus := and(rawOrderStatus, wipeDenominatorMask)
+ // Or the inputted denominator bytes into the raw order status.
+ rawOrderStatus := or(rawOrderStatus, denominatorBytes)
+ }
+
+ // Store the new raw order status.
+ vm.store(address(seaport), orderHashStorageSlot, rawOrderStatus);
+ }
+
+ /**
+ * @dev Inscribe the contract offerer nonce.
+ *
+ * @param contractOfferer The contract offerer to inscribe the nonce for.
+ * @param nonce The nonce to inscribe.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeContractOffererNonce(
+ address contractOfferer,
+ uint256 nonce,
+ SeaportInterface seaport
+ ) internal {
+ // Get the storage slot for the contract offerer's nonce.
+ bytes32 contractOffererNonceStorageSlot = _getStorageSlotForContractNonce(
+ contractOfferer,
+ seaport
+ );
+
+ // Store the new nonce.
+ vm.store(
+ address(seaport),
+ contractOffererNonceStorageSlot,
+ bytes32(nonce)
+ );
+ }
+
+ /**
+ * @dev Inscribe the counter for an offerer.
+ *
+ * @param offerer The offerer to inscribe the counter for.
+ * @param counter The counter to inscribe.
+ * @param seaport The Seaport instance.
+ *
+ */
+ function inscribeCounter(
+ address offerer,
+ uint256 counter,
+ SeaportInterface seaport
+ ) internal {
+ // Get the storage slot for the counter.
+ bytes32 counterStorageSlot = _getStorageSlotForCounter(
+ offerer,
+ seaport
+ );
+
+ // Store the new counter.
+ vm.store(address(seaport), counterStorageSlot, bytes32(counter));
+ }
+
+ function _getStorageSlotForOrderHash(
+ bytes32 orderHash,
+ SeaportInterface seaport
+ ) private returns (bytes32) {
+ vm.record();
+ seaport.getOrderStatus(orderHash);
+ (bytes32[] memory readAccesses, ) = vm.accesses(address(seaport));
+
+ uint256 expectedReadAccessCount = 4;
+
+ string memory profile = vm.envOr(
+ "FOUNDRY_PROFILE",
+ string("optimized")
+ );
+
+ if (
+ keccak256(abi.encodePacked(profile)) ==
+ keccak256(abi.encodePacked("optimized")) ||
+ keccak256(abi.encodePacked(profile)) ==
+ keccak256(abi.encodePacked("test")) ||
+ keccak256(abi.encodePacked(profile)) ==
+ keccak256(abi.encodePacked("lite"))
+ ) {
+ expectedReadAccessCount = 1;
+ }
+
+ require(
+ readAccesses.length == expectedReadAccessCount,
+ "Expected a different number of read accesses."
+ );
+
+ return readAccesses[0];
+ }
+
+ function _getStorageSlotForContractNonce(
+ address contractOfferer,
+ SeaportInterface seaport
+ ) private returns (bytes32) {
+ vm.record();
+ seaport.getContractOffererNonce(contractOfferer);
+ (bytes32[] memory readAccesses, ) = vm.accesses(address(seaport));
+
+ require(readAccesses.length == 1, "Expected 1 read access.");
+
+ return readAccesses[0];
+ }
+
+ function _getStorageSlotForCounter(
+ address offerer,
+ SeaportInterface seaport
+ ) private returns (bytes32) {
+ vm.record();
+ seaport.getCounter(offerer);
+ (bytes32[] memory readAccesses, ) = vm.accesses(address(seaport));
+
+ require(readAccesses.length == 1, "Expected 1 read access.");
+
+ return readAccesses[0];
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzMutationHelpers.sol b/test/foundry/new/helpers/FuzzMutationHelpers.sol
new file mode 100644
index 000000000..2c45ed18e
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzMutationHelpers.sol
@@ -0,0 +1,1186 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { AdvancedOrder } from "seaport-sol/SeaportStructs.sol";
+
+import { Side } from "seaport-sol/SeaportEnums.sol";
+
+import { FuzzGeneratorContext } from "./FuzzGeneratorContextLib.sol";
+
+import { PRNGHelpers } from "./FuzzGenerators.sol";
+
+import { FuzzTestContext, MutationState } from "./FuzzTestContextLib.sol";
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import { assume } from "./VmUtils.sol";
+
+import { Failure } from "./FuzzMutationSelectorLib.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ Execution,
+ OfferItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+enum MutationContextDerivation {
+ GENERIC, // No specific selection
+ ORDER, // Selecting an order
+ CRITERIA_RESOLVER // Selecting a criteria resolver
+}
+
+struct IneligibilityFilter {
+ Failure[] failures;
+ MutationContextDerivation derivationMethod;
+ bytes32 ineligibleMutationFilter; // stores a function pointer
+}
+
+struct FailureDetails {
+ string name;
+ bytes4 mutationSelector;
+ bytes4 errorSelector;
+ MutationContextDerivation derivationMethod;
+ bytes32 revertReasonDeriver; // stores a function pointer
+}
+
+library FailureEligibilityLib {
+ using LibPRNG for LibPRNG.PRNG;
+
+ function ensureFilterSetForEachFailure(
+ IneligibilityFilter[] memory failuresAndFilters
+ ) internal pure {
+ for (uint256 i = 0; i < uint256(Failure.length); ++i) {
+ Failure failure = Failure(i);
+
+ bool foundFailure = false;
+
+ for (uint256 j = 0; j < failuresAndFilters.length; ++j) {
+ Failure[] memory failures = failuresAndFilters[j].failures;
+
+ for (uint256 k = 0; k < failures.length; ++k) {
+ foundFailure = (failure == failures[k]);
+
+ if (foundFailure) {
+ break;
+ }
+ }
+
+ if (foundFailure) {
+ break;
+ }
+ }
+
+ if (!foundFailure) {
+ revert(
+ string.concat(
+ "FailureEligibilityLib: no filter located for failure #",
+ _toString(i)
+ )
+ );
+ }
+ }
+ }
+
+ function extractFirstFilterForFailure(
+ IneligibilityFilter[] memory failuresAndFilters,
+ Failure failure
+ ) internal pure returns (bytes32 filter) {
+ bool foundFailure = false;
+ uint256 i;
+
+ for (i = 0; i < failuresAndFilters.length; ++i) {
+ Failure[] memory failures = failuresAndFilters[i].failures;
+
+ for (uint256 j = 0; j < failures.length; ++j) {
+ foundFailure = (failure == failures[j]);
+
+ if (foundFailure) {
+ break;
+ }
+ }
+
+ if (foundFailure) {
+ break;
+ }
+ }
+
+ if (!foundFailure) {
+ revert(
+ string.concat(
+ "FailureEligibilityLib: no filter extractable for failure #",
+ _toString(uint256(failure))
+ )
+ );
+ }
+
+ return failuresAndFilters[i].ineligibleMutationFilter;
+ }
+
+ function setIneligibleFailure(
+ FuzzTestContext memory context,
+ Failure ineligibleFailure
+ ) internal pure {
+ // Set the respective boolean for the ineligible failure.
+ context.expectations.ineligibleFailures[
+ uint256(ineligibleFailure)
+ ] = true;
+ }
+
+ function setIneligibleFailures(
+ FuzzTestContext memory context,
+ Failure[] memory ineligibleFailures
+ ) internal pure {
+ for (uint256 i = 0; i < ineligibleFailures.length; ++i) {
+ // Set the respective boolean for each ineligible failure.
+ context.expectations.ineligibleFailures[
+ uint256(ineligibleFailures[i])
+ ] = true;
+ }
+ }
+
+ function getEligibleFailures(
+ FuzzTestContext memory context
+ ) internal pure returns (Failure[] memory eligibleFailures) {
+ eligibleFailures = new Failure[](uint256(Failure.length));
+
+ uint256 totalEligibleFailures = 0;
+ for (
+ uint256 i = 0;
+ i < context.expectations.ineligibleFailures.length;
+ ++i
+ ) {
+ // If the boolean is not set, the failure is still eligible.
+ if (!context.expectations.ineligibleFailures[i]) {
+ eligibleFailures[totalEligibleFailures++] = Failure(i);
+ }
+ }
+
+ // Update the eligibleFailures array with the actual length.
+ assembly {
+ mstore(eligibleFailures, totalEligibleFailures)
+ }
+ }
+
+ function selectEligibleFailure(
+ FuzzTestContext memory context
+ ) internal returns (Failure eligibleFailure) {
+ LibPRNG.PRNG memory prng = LibPRNG.PRNG(context.fuzzParams.seed ^ 0xff);
+
+ Failure[] memory eligibleFailures = getEligibleFailures(context);
+
+ // TODO: remove this vm.assume as soon as at least one case is found
+ // for any permutation of orders.
+ assume(eligibleFailures.length > 0, "no_eligible_failures");
+
+ if (eligibleFailures.length == 0) {
+ revert("FailureEligibilityLib: no eligible failure found");
+ }
+
+ return eligibleFailures[prng.next() % eligibleFailures.length];
+ }
+
+ function _toString(uint256 value) private pure returns (string memory) {
+ if (value == 0) {
+ return "0";
+ }
+
+ uint256 tempValue = value;
+ uint256 length;
+
+ while (tempValue != 0) {
+ length++;
+ tempValue /= 10;
+ }
+
+ bytes memory strBytes = new bytes(length);
+ while (value != 0) {
+ strBytes[--length] = bytes1(uint8(48) + uint8(value % 10));
+ value /= 10;
+ }
+
+ return string(strBytes);
+ }
+}
+
+library MutationEligibilityLib {
+ using Failarray for Failure;
+ using LibPRNG for LibPRNG.PRNG;
+ using FailureEligibilityLib for FuzzTestContext;
+
+ error NoEligibleIndexFound();
+
+ function withOrder(
+ Failure failure,
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibilityFilter
+ ) internal pure returns (IneligibilityFilter memory) {
+ return
+ IneligibilityFilter(
+ failure.one(),
+ MutationContextDerivation.ORDER,
+ fn(ineligibilityFilter)
+ );
+ }
+
+ function withOrder(
+ Failure[] memory failures,
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibilityFilter
+ ) internal pure returns (IneligibilityFilter memory) {
+ return
+ IneligibilityFilter(
+ failures,
+ MutationContextDerivation.ORDER,
+ fn(ineligibilityFilter)
+ );
+ }
+
+ function withCriteria(
+ Failure failure,
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibilityFilter
+ ) internal pure returns (IneligibilityFilter memory) {
+ return
+ IneligibilityFilter(
+ failure.one(),
+ MutationContextDerivation.CRITERIA_RESOLVER,
+ fn(ineligibilityFilter)
+ );
+ }
+
+ function withCriteria(
+ Failure[] memory failures,
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibilityFilter
+ ) internal pure returns (IneligibilityFilter memory) {
+ return
+ IneligibilityFilter(
+ failures,
+ MutationContextDerivation.CRITERIA_RESOLVER,
+ fn(ineligibilityFilter)
+ );
+ }
+
+ function withGeneric(
+ Failure failure,
+ function(FuzzTestContext memory)
+ internal
+ returns (bool) ineligibilityFilter
+ ) internal pure returns (IneligibilityFilter memory) {
+ return
+ IneligibilityFilter(
+ failure.one(),
+ MutationContextDerivation.GENERIC,
+ fn(ineligibilityFilter)
+ );
+ }
+
+ function withGeneric(
+ Failure[] memory failures,
+ function(FuzzTestContext memory)
+ internal
+ returns (bool) ineligibilityFilter
+ ) internal pure returns (IneligibilityFilter memory) {
+ return
+ IneligibilityFilter(
+ failures,
+ MutationContextDerivation.GENERIC,
+ fn(ineligibilityFilter)
+ );
+ }
+
+ function setAllIneligibleFailures(
+ FuzzTestContext memory context,
+ IneligibilityFilter[] memory failuresAndFilters
+ ) internal {
+ for (uint256 i = 0; i < failuresAndFilters.length; ++i) {
+ IneligibilityFilter memory failuresAndFilter = (
+ failuresAndFilters[i]
+ );
+
+ if (
+ failuresAndFilter.derivationMethod ==
+ MutationContextDerivation.GENERIC
+ ) {
+ setIneligibleFailures(
+ context,
+ asIneligibleGenericMutationFilter(
+ failuresAndFilter.ineligibleMutationFilter
+ ),
+ failuresAndFilter.failures
+ );
+ } else if (
+ failuresAndFilter.derivationMethod ==
+ MutationContextDerivation.ORDER
+ ) {
+ setIneligibleFailures(
+ context,
+ asIneligibleOrderBasedMutationFilter(
+ failuresAndFilter.ineligibleMutationFilter
+ ),
+ failuresAndFilter.failures
+ );
+ } else if (
+ failuresAndFilter.derivationMethod ==
+ MutationContextDerivation.CRITERIA_RESOLVER
+ ) {
+ setIneligibleFailures(
+ context,
+ asIneligibleCriteriaBasedMutationFilter(
+ failuresAndFilter.ineligibleMutationFilter
+ ),
+ failuresAndFilter.failures
+ );
+ } else {
+ revert(
+ "MutationEligibilityLib: unknown derivation method when setting failures"
+ );
+ }
+ }
+ }
+
+ function setIneligibleFailures(
+ FuzzTestContext memory context,
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleMutationFilter,
+ Failure[] memory ineligibleFailures
+ ) internal {
+ if (hasNoEligibleOrders(context, ineligibleMutationFilter)) {
+ context.setIneligibleFailures(ineligibleFailures);
+ }
+ }
+
+ function setIneligibleFailures(
+ FuzzTestContext memory context,
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleMutationFilter,
+ Failure[] memory ineligibleFailures
+ ) internal {
+ if (hasNoEligibleCriteriaResolvers(context, ineligibleMutationFilter)) {
+ context.setIneligibleFailures(ineligibleFailures);
+ }
+ }
+
+ function setIneligibleFailures(
+ FuzzTestContext memory context,
+ function(FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleMutationFilter,
+ Failure[] memory ineligibleFailures
+ ) internal {
+ if (hasNoEligibleFailures(context, ineligibleMutationFilter)) {
+ context.setIneligibleFailures(ineligibleFailures);
+ }
+ }
+
+ function hasNoEligibleOrders(
+ FuzzTestContext memory context,
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleCondition
+ ) internal returns (bool) {
+ for (uint256 i; i < context.executionState.orders.length; i++) {
+ // Once an eligible order is found, return false.
+ if (
+ !ineligibleCondition(
+ context.executionState.orders[i],
+ i,
+ context
+ )
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function hasNoEligibleCriteriaResolvers(
+ FuzzTestContext memory context,
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleCondition
+ ) internal returns (bool) {
+ for (
+ uint256 i;
+ i < context.executionState.criteriaResolvers.length;
+ i++
+ ) {
+ // Once an eligible criteria resolver is found, return false.
+ if (
+ !ineligibleCondition(
+ context.executionState.criteriaResolvers[i],
+ i,
+ context
+ )
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function hasNoEligibleFailures(
+ FuzzTestContext memory context,
+ function(FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleCondition
+ ) internal returns (bool) {
+ // If the failure is not eligible for selection, return false.
+ if (!ineligibleCondition(context)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function getIneligibleOrders(
+ FuzzTestContext memory context,
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ view
+ returns (bool) condition
+ ) internal view returns (bool[] memory ineligibleOrders) {
+ AdvancedOrder[] memory orders = context.executionState.orders;
+ ineligibleOrders = new bool[](orders.length);
+ for (uint256 i; i < orders.length; i++) {
+ if (condition(orders[i], i, context)) {
+ // Set the respective boolean for the ineligible order.
+ ineligibleOrders[i] = true;
+ }
+ }
+ }
+
+ function getIneligibleCriteriaResolvers(
+ FuzzTestContext memory context,
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ view
+ returns (bool) condition
+ ) internal view returns (bool[] memory ineligibleCriteriaResolvers) {
+ CriteriaResolver[] memory resolvers = (
+ context.executionState.criteriaResolvers
+ );
+ ineligibleCriteriaResolvers = new bool[](resolvers.length);
+ for (uint256 i; i < resolvers.length; i++) {
+ if (condition(resolvers[i], i, context)) {
+ // Set respective boolean for the ineligible criteria resolver.
+ ineligibleCriteriaResolvers[i] = true;
+ }
+ }
+ }
+
+ function getEligibleIndex(
+ FuzzTestContext memory context,
+ bool[] memory eligibleElements
+ ) internal pure returns (uint256) {
+ LibPRNG.PRNG memory prng = LibPRNG.PRNG(context.fuzzParams.seed ^ 0xff);
+
+ uint256[] memory eligibleIndexes = new uint256[](
+ eligibleElements.length
+ );
+
+ uint256 totalEligible = 0;
+ for (uint256 i = 0; i < eligibleElements.length; ++i) {
+ // If the boolean is not set, the element is still eligible.
+ if (!eligibleElements[i]) {
+ eligibleIndexes[totalEligible++] = i;
+ }
+ }
+
+ if (totalEligible == 0) {
+ revert NoEligibleIndexFound();
+ }
+
+ return eligibleIndexes[prng.next() % totalEligible];
+ }
+
+ function selectEligibleOrder(
+ FuzzTestContext memory context,
+ bytes32 ineligibilityFilter
+ )
+ internal
+ view
+ returns (AdvancedOrder memory eligibleOrder, uint256 orderIndex)
+ {
+ bool[] memory eligibleOrders = getIneligibleOrders(
+ context,
+ MutationEligibilityLib.asIneligibleOrderBasedMutationFilter(
+ ineligibilityFilter
+ )
+ );
+
+ orderIndex = getEligibleIndex(context, eligibleOrders);
+ eligibleOrder = context.executionState.orders[orderIndex];
+ }
+
+ function selectEligibleCriteriaResolver(
+ FuzzTestContext memory context,
+ bytes32 ineligibilityFilter
+ )
+ internal
+ view
+ returns (
+ CriteriaResolver memory eligibleCriteriaResolver,
+ uint256 criteriaResolverIndex
+ )
+ {
+ bool[] memory eligibleResolvers = getIneligibleCriteriaResolvers(
+ context,
+ MutationEligibilityLib.asIneligibleCriteriaBasedMutationFilter(
+ ineligibilityFilter
+ )
+ );
+
+ criteriaResolverIndex = getEligibleIndex(context, eligibleResolvers);
+ eligibleCriteriaResolver = context.executionState.criteriaResolvers[
+ criteriaResolverIndex
+ ];
+ }
+
+ function fn(
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleMutationFilter
+ ) internal pure returns (bytes32 ptr) {
+ assembly {
+ ptr := ineligibleMutationFilter
+ }
+ }
+
+ function fn(
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleMutationFilter
+ ) internal pure returns (bytes32 ptr) {
+ assembly {
+ ptr := ineligibleMutationFilter
+ }
+ }
+
+ function fn(
+ function(FuzzTestContext memory)
+ internal
+ returns (bool) ineligibleMutationFilter
+ ) internal pure returns (bytes32 ptr) {
+ assembly {
+ ptr := ineligibleMutationFilter
+ }
+ }
+
+ function asIneligibleGenericMutationFilter(
+ bytes32 ptr
+ )
+ internal
+ pure
+ returns (
+ function(FuzzTestContext memory)
+ internal
+ view
+ returns (bool) ineligibleMutationFilter
+ )
+ {
+ assembly {
+ ineligibleMutationFilter := ptr
+ }
+ }
+
+ function asIneligibleOrderBasedMutationFilter(
+ bytes32 ptr
+ )
+ internal
+ pure
+ returns (
+ function(AdvancedOrder memory, uint256, FuzzTestContext memory)
+ internal
+ view
+ returns (bool) ineligibleMutationFilter
+ )
+ {
+ assembly {
+ ineligibleMutationFilter := ptr
+ }
+ }
+
+ function asIneligibleCriteriaBasedMutationFilter(
+ bytes32 ptr
+ )
+ internal
+ pure
+ returns (
+ function(CriteriaResolver memory, uint256, FuzzTestContext memory)
+ internal
+ view
+ returns (bool) ineligibleMutationFilter
+ )
+ {
+ assembly {
+ ineligibleMutationFilter := ptr
+ }
+ }
+}
+
+library MutationContextDeriverLib {
+ using MutationEligibilityLib for FuzzTestContext;
+ using PRNGHelpers for FuzzGeneratorContext;
+
+ function deriveMutationContext(
+ FuzzTestContext memory context,
+ MutationContextDerivation derivationMethod,
+ bytes32 ineligibilityFilter // use a function pointer
+ ) internal view returns (MutationState memory mutationState) {
+ if (derivationMethod == MutationContextDerivation.ORDER) {
+ (AdvancedOrder memory order, uint256 orderIndex) = context
+ .selectEligibleOrder(ineligibilityFilter);
+
+ mutationState.selectedOrder = order;
+ mutationState.selectedOrderIndex = orderIndex;
+ mutationState.selectedOrderHash = context
+ .executionState
+ .orderDetails[orderIndex].orderHash;
+ mutationState.side = Side(context.generatorContext.randEnum(0, 1));
+ mutationState.selectedArbitraryAddress = address(
+ uint160(
+ context.generatorContext.randRange(3, type(uint160).max)
+ )
+ );
+ } else if (
+ derivationMethod == MutationContextDerivation.CRITERIA_RESOLVER
+ ) {
+ (CriteriaResolver memory resolver, uint256 resolverIndex) = context
+ .selectEligibleCriteriaResolver(ineligibilityFilter);
+
+ mutationState.selectedCriteriaResolver = resolver;
+ mutationState.selectedCriteriaResolverIndex = resolverIndex;
+ } else if ((derivationMethod != MutationContextDerivation.GENERIC)) {
+ revert("MutationContextDeriverLib: unsupported derivation method");
+ }
+ }
+}
+
+library FailureDetailsHelperLib {
+ function withOrder(
+ bytes4 errorSelector,
+ string memory name,
+ bytes4 mutationSelector
+ ) internal pure returns (FailureDetails memory details) {
+ return
+ FailureDetails(
+ name,
+ mutationSelector,
+ errorSelector,
+ MutationContextDerivation.ORDER,
+ fn(defaultReason)
+ );
+ }
+
+ function withOrder(
+ bytes4 errorSelector,
+ string memory name,
+ bytes4 mutationSelector,
+ function(FuzzTestContext memory, MutationState memory, bytes4)
+ internal
+ view
+ returns (bytes memory) revertReasonDeriver
+ ) internal pure returns (FailureDetails memory details) {
+ return
+ FailureDetails(
+ name,
+ mutationSelector,
+ errorSelector,
+ MutationContextDerivation.ORDER,
+ fn(revertReasonDeriver)
+ );
+ }
+
+ function withCriteria(
+ bytes4 errorSelector,
+ string memory name,
+ bytes4 mutationSelector
+ ) internal pure returns (FailureDetails memory details) {
+ return
+ FailureDetails(
+ name,
+ mutationSelector,
+ errorSelector,
+ MutationContextDerivation.CRITERIA_RESOLVER,
+ fn(defaultReason)
+ );
+ }
+
+ function withCriteria(
+ bytes4 errorSelector,
+ string memory name,
+ bytes4 mutationSelector,
+ function(FuzzTestContext memory, MutationState memory, bytes4)
+ internal
+ view
+ returns (bytes memory) revertReasonDeriver
+ ) internal pure returns (FailureDetails memory details) {
+ return
+ FailureDetails(
+ name,
+ mutationSelector,
+ errorSelector,
+ MutationContextDerivation.CRITERIA_RESOLVER,
+ fn(revertReasonDeriver)
+ );
+ }
+
+ function withGeneric(
+ bytes4 errorSelector,
+ string memory name,
+ bytes4 mutationSelector
+ ) internal pure returns (FailureDetails memory details) {
+ return
+ FailureDetails(
+ name,
+ mutationSelector,
+ errorSelector,
+ MutationContextDerivation.GENERIC,
+ fn(defaultReason)
+ );
+ }
+
+ function withGeneric(
+ bytes4 errorSelector,
+ string memory name,
+ bytes4 mutationSelector,
+ function(FuzzTestContext memory, MutationState memory, bytes4)
+ internal
+ view
+ returns (bytes memory) revertReasonDeriver
+ ) internal pure returns (FailureDetails memory details) {
+ return
+ FailureDetails(
+ name,
+ mutationSelector,
+ errorSelector,
+ MutationContextDerivation.GENERIC,
+ fn(revertReasonDeriver)
+ );
+ }
+
+ function fn(
+ function(FuzzTestContext memory, MutationState memory, bytes4)
+ internal
+ view
+ returns (bytes memory) revertReasonGenerator
+ ) internal pure returns (bytes32 ptr) {
+ assembly {
+ ptr := revertReasonGenerator
+ }
+ }
+
+ function deriveRevertReason(
+ FuzzTestContext memory context,
+ MutationState memory mutationState,
+ bytes4 errorSelector,
+ bytes32 revertReasonDeriver
+ ) internal view returns (bytes memory) {
+ return
+ asRevertReasonGenerator(revertReasonDeriver)(
+ context,
+ mutationState,
+ errorSelector
+ );
+ }
+
+ function asRevertReasonGenerator(
+ bytes32 ptr
+ )
+ private
+ pure
+ returns (
+ function(FuzzTestContext memory, MutationState memory, bytes4)
+ internal
+ view
+ returns (bytes memory) revertReasonGenerator
+ )
+ {
+ assembly {
+ revertReasonGenerator := ptr
+ }
+ }
+
+ function defaultReason(
+ FuzzTestContext memory /* context */,
+ MutationState memory,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory) {
+ return abi.encodePacked(errorSelector);
+ }
+}
+
+library MutationHelpersLib {
+ function isFilteredOrNative(
+ FuzzTestContext memory context,
+ OfferItem memory item,
+ address offerer,
+ bytes32 conduitKey
+ ) internal pure returns (bool) {
+ // Native tokens are not filtered.
+ if (item.itemType == ItemType.NATIVE) {
+ return true;
+ }
+
+ // First look in explicit executions.
+ for (
+ uint256 i;
+ i < context.expectations.expectedExplicitExecutions.length;
+ ++i
+ ) {
+ Execution memory execution = context
+ .expectations
+ .expectedExplicitExecutions[i];
+ if (
+ execution.offerer == offerer &&
+ execution.conduitKey == conduitKey &&
+ execution.item.itemType == item.itemType &&
+ execution.item.token == item.token
+ ) {
+ return false;
+ }
+ }
+
+ // If we haven't found one yet, keep looking in implicit executions...
+ for (
+ uint256 i;
+ i < context.expectations.expectedImplicitPreExecutions.length;
+ ++i
+ ) {
+ Execution memory execution = context
+ .expectations
+ .expectedImplicitPreExecutions[i];
+ if (
+ execution.offerer == offerer &&
+ execution.conduitKey == conduitKey &&
+ execution.item.itemType == item.itemType &&
+ execution.item.token == item.token
+ ) {
+ return false;
+ }
+ }
+
+ // If we haven't found one yet, keep looking in implicit executions...
+ for (
+ uint256 i;
+ i < context.expectations.expectedImplicitPostExecutions.length;
+ ++i
+ ) {
+ Execution memory execution = context
+ .expectations
+ .expectedImplicitPostExecutions[i];
+ if (
+ execution.offerer == offerer &&
+ execution.conduitKey == conduitKey &&
+ execution.item.itemType == item.itemType &&
+ execution.item.token == item.token
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function isFilteredOrNative(
+ FuzzTestContext memory context,
+ ConsiderationItem memory item
+ ) internal pure returns (bool) {
+ if (item.itemType == ItemType.NATIVE) {
+ return true;
+ }
+
+ address caller = context.executionState.caller;
+ bytes32 conduitKey = context.executionState.fulfillerConduitKey;
+
+ // First look in explicit executions.
+ for (
+ uint256 i;
+ i < context.expectations.expectedExplicitExecutions.length;
+ ++i
+ ) {
+ Execution memory execution = context
+ .expectations
+ .expectedExplicitExecutions[i];
+ if (
+ execution.offerer == caller &&
+ execution.conduitKey == conduitKey &&
+ execution.item.itemType == item.itemType &&
+ execution.item.token == item.token
+ ) {
+ return false;
+ }
+ }
+
+ // If we haven't found one yet, keep looking in implicit executions...
+ for (
+ uint256 i;
+ i < context.expectations.expectedImplicitPreExecutions.length;
+ ++i
+ ) {
+ Execution memory execution = context
+ .expectations
+ .expectedImplicitPreExecutions[i];
+ if (
+ execution.offerer == caller &&
+ execution.conduitKey == conduitKey &&
+ execution.item.itemType == item.itemType &&
+ execution.item.token == item.token
+ ) {
+ return false;
+ }
+ }
+
+ // If we haven't found one yet, keep looking in implicit executions...
+ for (
+ uint256 i;
+ i < context.expectations.expectedImplicitPostExecutions.length;
+ ++i
+ ) {
+ Execution memory execution = context
+ .expectations
+ .expectedImplicitPostExecutions[i];
+ if (
+ execution.offerer == caller &&
+ execution.conduitKey == conduitKey &&
+ execution.item.itemType == item.itemType &&
+ execution.item.token == item.token
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+library Failarray {
+ function one(Failure a) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](1);
+ arr[0] = a;
+ return arr;
+ }
+
+ function and(
+ Failure a,
+ Failure b
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](2);
+ arr[0] = a;
+ arr[1] = b;
+ return arr;
+ }
+
+ function and(
+ Failure a,
+ Failure b,
+ Failure c
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](3);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ return arr;
+ }
+
+ function and(
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](4);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ return arr;
+ }
+
+ function and(
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d,
+ Failure e
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](5);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ return arr;
+ }
+
+ function and(
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d,
+ Failure e,
+ Failure f
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](6);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ return arr;
+ }
+
+ function and(
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d,
+ Failure e,
+ Failure f,
+ Failure g
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](7);
+ arr[0] = a;
+ arr[1] = b;
+ arr[2] = c;
+ arr[3] = d;
+ arr[4] = e;
+ arr[5] = f;
+ arr[6] = g;
+ return arr;
+ }
+
+ function and(
+ Failure[] memory originalArr,
+ Failure a
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](originalArr.length + 1);
+
+ for (uint256 i = 0; i < originalArr.length; ++i) {
+ arr[i] = originalArr[i];
+ }
+
+ arr[originalArr.length] = a;
+
+ return arr;
+ }
+
+ function and(
+ Failure[] memory originalArr,
+ Failure a,
+ Failure b
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](originalArr.length + 2);
+
+ for (uint256 i = 0; i < originalArr.length; ++i) {
+ arr[i] = originalArr[i];
+ }
+
+ arr[originalArr.length] = a;
+ arr[originalArr.length + 1] = b;
+
+ return arr;
+ }
+
+ function and(
+ Failure[] memory originalArr,
+ Failure a,
+ Failure b,
+ Failure c
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](originalArr.length + 3);
+
+ for (uint256 i = 0; i < originalArr.length; ++i) {
+ arr[i] = originalArr[i];
+ }
+
+ arr[originalArr.length] = a;
+ arr[originalArr.length + 1] = b;
+ arr[originalArr.length + 2] = c;
+
+ return arr;
+ }
+
+ function and(
+ Failure[] memory originalArr,
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](originalArr.length + 4);
+
+ for (uint256 i = 0; i < originalArr.length; ++i) {
+ arr[i] = originalArr[i];
+ }
+
+ arr[originalArr.length] = a;
+ arr[originalArr.length + 1] = b;
+ arr[originalArr.length + 2] = c;
+ arr[originalArr.length + 3] = d;
+
+ return arr;
+ }
+
+ function and(
+ Failure[] memory originalArr,
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d,
+ Failure e
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](originalArr.length + 5);
+
+ for (uint256 i = 0; i < originalArr.length; ++i) {
+ arr[i] = originalArr[i];
+ }
+
+ arr[originalArr.length] = a;
+ arr[originalArr.length + 1] = b;
+ arr[originalArr.length + 2] = c;
+ arr[originalArr.length + 3] = d;
+ arr[originalArr.length + 4] = e;
+
+ return arr;
+ }
+
+ function and(
+ Failure[] memory originalArr,
+ Failure a,
+ Failure b,
+ Failure c,
+ Failure d,
+ Failure e,
+ Failure f
+ ) internal pure returns (Failure[] memory) {
+ Failure[] memory arr = new Failure[](originalArr.length + 6);
+
+ for (uint256 i = 0; i < originalArr.length; ++i) {
+ arr[i] = originalArr[i];
+ }
+
+ arr[originalArr.length] = a;
+ arr[originalArr.length + 1] = b;
+ arr[originalArr.length + 2] = c;
+ arr[originalArr.length + 3] = d;
+ arr[originalArr.length + 4] = e;
+ arr[originalArr.length + 5] = f;
+
+ return arr;
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzMutationSelectorLib.sol b/test/foundry/new/helpers/FuzzMutationSelectorLib.sol
new file mode 100644
index 000000000..6972295dd
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzMutationSelectorLib.sol
@@ -0,0 +1,1354 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ ReceivedItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import { FuzzTestContext, MutationState } from "./FuzzTestContextLib.sol";
+import { FuzzMutations, MutationFilters } from "./FuzzMutations.sol";
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import {
+ FailureEligibilityLib,
+ MutationEligibilityLib,
+ Failarray,
+ FailureDetails,
+ FailureDetailsHelperLib,
+ IneligibilityFilter,
+ MutationContextDerivation,
+ MutationContextDeriverLib
+} from "./FuzzMutationHelpers.sol";
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import {
+ SignatureVerificationErrors
+} from "../../../../contracts/interfaces/SignatureVerificationErrors.sol";
+
+import {
+ ConsiderationEventsAndErrors
+} from "../../../../contracts/interfaces/ConsiderationEventsAndErrors.sol";
+
+import {
+ FulfillmentApplicationErrors
+} from "../../../../contracts/interfaces/FulfillmentApplicationErrors.sol";
+
+import {
+ CriteriaResolutionErrors
+} from "../../../../contracts/interfaces/CriteriaResolutionErrors.sol";
+
+import {
+ TokenTransferrerErrors
+} from "../../../../contracts/interfaces/TokenTransferrerErrors.sol";
+
+import {
+ ZoneInteractionErrors
+} from "../../../../contracts/interfaces/ZoneInteractionErrors.sol";
+
+import {
+ AmountDerivationErrors
+} from "../../../../contracts/interfaces/AmountDerivationErrors.sol";
+
+import {
+ HashCalldataContractOfferer
+} from "../../../../contracts/test/HashCalldataContractOfferer.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../../contracts/test/HashValidationZoneOfferer.sol";
+
+/////////////////////// UPDATE THIS TO ADD FAILURE TESTS ///////////////////////
+enum Failure {
+ InvalidSignature, // EOA signature is incorrect length
+ InvalidSigner_BadSignature, // EOA signature has been tampered with
+ InvalidSigner_ModifiedOrder, // Order with no-code offerer has been tampered with
+ BadSignatureV, // EOA signature has bad v value
+ BadContractSignature_BadSignature, // 1271 call to offerer, signature tampered with
+ BadContractSignature_ModifiedOrder, // Order with offerer with code tampered with
+ BadContractSignature_MissingMagic, // 1271 call to offerer, no magic value returned
+ ConsiderationLengthNotEqualToTotalOriginal_ExtraItems, // Tips on contract order or validate
+ ConsiderationLengthNotEqualToTotalOriginal_MissingItems, // Tips on contract order or validate
+ MissingOriginalConsiderationItems, // Consideration array shorter than totalOriginalConsiderationItems
+ InvalidTime_NotStarted, // Order with start time in the future
+ InvalidTime_Expired, // Order with end time in the past
+ InvalidConduit, // Order with invalid conduit
+ BadFraction_PartialContractOrder, // Contract order w/ numerator & denominator != 1
+ BadFraction_NoFill, // Order where numerator = 0
+ BadFraction_Overfill, // Order where numerator > denominator
+ CannotCancelOrder, // Caller cannot cancel order
+ OrderIsCancelled, // Order is cancelled
+ OrderAlreadyFilled, // Order is already filled
+ InvalidFulfillmentComponentData, // Fulfillment component data is invalid
+ MissingFulfillmentComponentOnAggregation, // Missing component
+ OfferAndConsiderationRequiredOnFulfillment, // Fulfillment missing offer or consideration
+ MismatchedFulfillmentOfferAndConsiderationComponents_Modified, // Fulfillment has mismatched offer and consideration components
+ MismatchedFulfillmentOfferAndConsiderationComponents_Swapped, // Fulfillment has mismatched offer and consideration components
+ Error_OfferItemMissingApproval, // Order has an offer item without sufficient approval
+ Error_CallerMissingApproval, // Order has a consideration item where caller is not approved
+ InvalidMsgValue, // Invalid msg.value amount
+ InsufficientNativeTokensSupplied, // Caller does not supply sufficient native tokens
+ NativeTokenTransferGenericFailure, // Insufficient native tokens with unspent offer items
+ CriteriaNotEnabledForItem, // Criteria resolver applied to non-criteria-based item
+ InvalidProof_Merkle, // Bad or missing proof for non-wildcard criteria item
+ InvalidProof_Wildcard, // Non-empty proof supplied for wildcard criteria item
+ OrderCriteriaResolverOutOfRange, // Criteria resolver refers to OOR order
+ OfferCriteriaResolverOutOfRange, // Criteria resolver refers to OOR offer item
+ ConsiderationCriteriaResolverOutOfRange, // Criteria resolver refers to OOR consideration item
+ UnresolvedOfferCriteria, // Missing criteria resolution for an offer item
+ UnresolvedConsiderationCriteria, // Missing criteria resolution for a consideration item
+ MissingItemAmount_OfferItem_FulfillAvailable, // Zero amount for offer item in fulfillAvailable
+ MissingItemAmount_OfferItem, // Zero amount for offer item in all other methods
+ MissingItemAmount_ConsiderationItem, // Zero amount for consideration item
+ InvalidContractOrder_generateReturnsInvalidEncoding, // Offerer generateOrder returns invalid data
+ InvalidContractOrder_generateReverts, // Offerer generateOrder reverts
+ InvalidContractOrder_ratifyReverts, // Offerer ratifyOrder reverts
+ InvalidContractOrder_InsufficientMinimumReceived, // too few minimum received items
+ InvalidContractOrder_IncorrectMinimumReceived, // incorrect (insufficient amount, wrong token, etc.) minimum received items
+ InvalidContractOrder_ExcessMaximumSpent, // too many maximum spent items
+ InvalidContractOrder_IncorrectMaximumSpent, // incorrect (too many, wrong token, etc.) maximum spent items
+ InvalidContractOrder_InvalidMagicValue, // Offerer did not return correct magic value
+ InvalidContractOrder_OfferAmountMismatch, // startAmount != endAmount on contract order offer item
+ InvalidContractOrder_ConsiderationAmountMismatch, // startAmount != endAmount on contract order consideration item
+ InvalidRestrictedOrder_reverts, // Zone validateOrder call reverts
+ InvalidRestrictedOrder_InvalidMagicValue, // Zone validateOrder call returns invalid magic value
+ NoContract, // Trying to transfer a token at an address that has no contract
+ UnusedItemParameters_Token, // Native item with non-zero token
+ UnusedItemParameters_Identifier, // Native or ERC20 item with non-zero identifier
+ InvalidERC721TransferAmount, // ERC721 transfer amount is not 1
+ ConsiderationNotMet, // Consideration item not fully credited (match case)
+ PartialFillsNotEnabledForOrder, // Partial fill on non-partial order type
+ InexactFraction, // numerator / denominator cannot be applied to item w/ no remainder
+ Panic_PartialFillOverflow, // numerator / denominator overflow current fill fraction
+ NoSpecifiedOrdersAvailable, // all fulfillAvailable executions are filtered
+ length // NOT A FAILURE; used to get the number of failures in the enum
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+library FuzzMutationSelectorLib {
+ using Failarray for Failure;
+ using Failarray for Failure[];
+ using FuzzEngineLib for FuzzTestContext;
+ using FailureDetailsLib for FuzzTestContext;
+ using FailureEligibilityLib for FuzzTestContext;
+ using MutationEligibilityLib for FuzzTestContext;
+ using MutationEligibilityLib for Failure;
+ using MutationEligibilityLib for Failure[];
+ using FailureEligibilityLib for IneligibilityFilter[];
+
+ function declareFilters()
+ internal
+ pure
+ returns (IneligibilityFilter[] memory failuresAndFilters)
+ {
+ // Set failure conditions as ineligible when no orders support them.
+ // Create abundantly long array to avoid potentially cryptic OOR errors.
+ failuresAndFilters = new IneligibilityFilter[](256);
+ uint256 i = 0;
+
+ /////////////////// UPDATE THIS TO ADD FAILURE TESTS ///////////////////
+ failuresAndFilters[i++] = Failure.InvalidSignature.withOrder(
+ MutationFilters.ineligibleForInvalidSignature
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidSigner_BadSignature
+ .and(Failure.InvalidSigner_ModifiedOrder)
+ .withOrder(MutationFilters.ineligibleForInvalidSigner);
+
+ failuresAndFilters[i++] = Failure
+ .InvalidTime_NotStarted
+ .and(Failure.InvalidTime_Expired)
+ .withOrder(MutationFilters.ineligibleForInvalidTime);
+
+ failuresAndFilters[i++] = Failure.InvalidConduit.withOrder(
+ MutationFilters.ineligibleForInvalidConduit
+ );
+
+ failuresAndFilters[i++] = Failure.BadSignatureV.withOrder(
+ MutationFilters.ineligibleForBadSignatureV
+ );
+
+ failuresAndFilters[i++] = Failure
+ .BadFraction_PartialContractOrder
+ .withOrder(
+ MutationFilters.ineligibleForBadFractionPartialContractOrder
+ );
+
+ failuresAndFilters[i++] = Failure.BadFraction_Overfill.withOrder(
+ MutationFilters.ineligibleForBadFraction
+ );
+
+ failuresAndFilters[i++] = Failure.BadFraction_NoFill.withOrder(
+ MutationFilters.ineligibleForBadFraction_noFill
+ );
+
+ failuresAndFilters[i++] = Failure.CannotCancelOrder.withOrder(
+ MutationFilters.ineligibleForCannotCancelOrder
+ );
+
+ failuresAndFilters[i++] = Failure.OrderIsCancelled.withOrder(
+ MutationFilters.ineligibleForOrderIsCancelled
+ );
+
+ failuresAndFilters[i++] = Failure.OrderAlreadyFilled.withOrder(
+ MutationFilters.ineligibleForOrderAlreadyFilled
+ );
+
+ failuresAndFilters[i++] = Failure
+ .BadContractSignature_BadSignature
+ .and(Failure.BadContractSignature_ModifiedOrder)
+ .and(Failure.BadContractSignature_MissingMagic)
+ .withOrder(MutationFilters.ineligibleForBadContractSignature);
+
+ failuresAndFilters[i++] = Failure
+ .MissingOriginalConsiderationItems
+ .withOrder(
+ MutationFilters.ineligibleForMissingOriginalConsiderationItems
+ );
+
+ failuresAndFilters[i++] = Failure
+ .ConsiderationLengthNotEqualToTotalOriginal_ExtraItems
+ .withOrder(
+ MutationFilters
+ .ineligibleForConsiderationLengthNotEqualToTotalOriginal
+ );
+
+ failuresAndFilters[i++] = Failure
+ .ConsiderationLengthNotEqualToTotalOriginal_MissingItems
+ .withOrder(
+ MutationFilters
+ .ineligibleForConsiderationLengthNotEqualToTotalOriginal
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidFulfillmentComponentData
+ .withGeneric(
+ MutationFilters.ineligibleForInvalidFulfillmentComponentData
+ );
+
+ failuresAndFilters[i++] = Failure
+ .MissingFulfillmentComponentOnAggregation
+ .withGeneric(
+ MutationFilters
+ .ineligibleForMissingFulfillmentComponentOnAggregation
+ );
+
+ failuresAndFilters[i++] = Failure
+ .OfferAndConsiderationRequiredOnFulfillment
+ .withGeneric(
+ MutationFilters
+ .ineligibleForOfferAndConsiderationRequiredOnFulfillment
+ );
+
+ failuresAndFilters[i++] = Failure
+ .MismatchedFulfillmentOfferAndConsiderationComponents_Modified
+ .withGeneric(
+ MutationFilters
+ .ineligibleForMismatchedFulfillmentOfferAndConsiderationComponents_Modified
+ );
+
+ failuresAndFilters[i++] = Failure
+ .MismatchedFulfillmentOfferAndConsiderationComponents_Swapped
+ .withGeneric(
+ MutationFilters
+ .ineligibleForMismatchedFulfillmentOfferAndConsiderationComponents_Swapped
+ );
+
+ failuresAndFilters[i++] = Failure
+ .Error_OfferItemMissingApproval
+ .withOrder(MutationFilters.ineligibleForOfferItemMissingApproval);
+
+ failuresAndFilters[i++] = Failure.Error_CallerMissingApproval.withOrder(
+ MutationFilters.ineligibleForCallerMissingApproval
+ );
+
+ failuresAndFilters[i++] = Failure.InvalidMsgValue.withGeneric(
+ MutationFilters.ineligibleForInvalidMsgValue
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InsufficientNativeTokensSupplied
+ .withGeneric(MutationFilters.ineligibleForInsufficientNativeTokens);
+
+ failuresAndFilters[i++] = Failure
+ .NativeTokenTransferGenericFailure
+ .withGeneric(
+ MutationFilters.ineligibleForNativeTokenTransferGenericFailure
+ );
+
+ failuresAndFilters[i++] = Failure.CriteriaNotEnabledForItem.withGeneric(
+ MutationFilters.ineligibleForCriteriaNotEnabledForItem
+ );
+
+ failuresAndFilters[i++] = Failure.InvalidProof_Merkle.withCriteria(
+ MutationFilters.ineligibleForInvalidProof_Merkle
+ );
+
+ failuresAndFilters[i++] = Failure.InvalidProof_Wildcard.withCriteria(
+ MutationFilters.ineligibleForInvalidProof_Wildcard
+ );
+
+ failuresAndFilters[i++] = Failure
+ .OrderCriteriaResolverOutOfRange
+ .withGeneric(MutationFilters.ineligibleWhenNotAdvanced);
+
+ failuresAndFilters[i++] = Failure
+ .OfferCriteriaResolverOutOfRange
+ .and(Failure.UnresolvedOfferCriteria)
+ .withCriteria(
+ MutationFilters.ineligibleForOfferCriteriaResolverFailure
+ );
+
+ failuresAndFilters[i++] = Failure
+ .ConsiderationCriteriaResolverOutOfRange
+ .and(Failure.UnresolvedConsiderationCriteria)
+ .withCriteria(
+ MutationFilters
+ .ineligibleForConsiderationCriteriaResolverFailure
+ );
+
+ failuresAndFilters[i++] = Failure
+ .MissingItemAmount_OfferItem_FulfillAvailable
+ .withGeneric(
+ MutationFilters
+ .ineligibleForMissingItemAmount_OfferItem_FulfillAvailable
+ );
+
+ failuresAndFilters[i++] = Failure.MissingItemAmount_OfferItem.withOrder(
+ MutationFilters.ineligibleForMissingItemAmount_OfferItem
+ );
+
+ failuresAndFilters[i++] = Failure
+ .MissingItemAmount_ConsiderationItem
+ .withOrder(
+ MutationFilters.ineligibleForMissingItemAmount_ConsiderationItem
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidContractOrder_generateReturnsInvalidEncoding
+ .and(Failure.InvalidContractOrder_generateReverts)
+ .withOrder(
+ MutationFilters.ineligibleWhenNotContractOrderOrFulfillAvailable
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidContractOrder_ratifyReverts
+ .and(Failure.InvalidContractOrder_InvalidMagicValue)
+ .withOrder(
+ MutationFilters.ineligibleWhenNotAvailableOrNotContractOrder
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidContractOrder_InsufficientMinimumReceived
+ .and(Failure.InvalidContractOrder_IncorrectMinimumReceived)
+ .and(Failure.InvalidContractOrder_OfferAmountMismatch)
+ .withOrder(
+ MutationFilters
+ .ineligibleWhenNotActiveTimeOrNotContractOrderOrNoOffer
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidContractOrder_ExcessMaximumSpent
+ .withOrder(
+ MutationFilters.ineligibleWhenNotActiveTimeOrNotContractOrder
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidContractOrder_IncorrectMaximumSpent
+ .and(Failure.InvalidContractOrder_ConsiderationAmountMismatch)
+ .withOrder(
+ MutationFilters
+ .ineligibleWhenNotActiveTimeOrNotContractOrderOrNoConsideration
+ );
+
+ failuresAndFilters[i++] = Failure
+ .InvalidRestrictedOrder_reverts
+ .and(Failure.InvalidRestrictedOrder_InvalidMagicValue)
+ .withOrder(
+ MutationFilters.ineligibleWhenNotAvailableOrNotRestrictedOrder
+ );
+
+ failuresAndFilters[i++] = Failure.NoContract.withGeneric(
+ MutationFilters.ineligibleForNoContract
+ );
+
+ failuresAndFilters[i++] = Failure.UnusedItemParameters_Token.withOrder(
+ MutationFilters.ineligibleForUnusedItemParameters_Token
+ );
+
+ failuresAndFilters[i++] = Failure
+ .UnusedItemParameters_Identifier
+ .withOrder(
+ MutationFilters.ineligibleForUnusedItemParameters_Identifier
+ );
+
+ failuresAndFilters[i++] = Failure.InvalidERC721TransferAmount.withOrder(
+ MutationFilters.ineligibleForInvalidERC721TransferAmount
+ );
+
+ failuresAndFilters[i++] = Failure.ConsiderationNotMet.withOrder(
+ MutationFilters.ineligibleForConsiderationNotMet
+ );
+
+ failuresAndFilters[i++] = Failure
+ .PartialFillsNotEnabledForOrder
+ .withOrder(
+ MutationFilters.ineligibleForPartialFillsNotEnabledForOrder
+ );
+
+ failuresAndFilters[i++] = Failure.InexactFraction.withOrder(
+ MutationFilters.ineligibleForInexactFraction
+ );
+
+ failuresAndFilters[i++] = Failure.Panic_PartialFillOverflow.withOrder(
+ MutationFilters.ineligibleForPanic_PartialFillOverflow
+ );
+
+ failuresAndFilters[i++] = Failure
+ .NoSpecifiedOrdersAvailable
+ .withGeneric(
+ MutationFilters.ineligibleForNoSpecifiedOrdersAvailable
+ );
+ ////////////////////////////////////////////////////////////////////////
+
+ // Set the actual length of the array.
+ assembly {
+ mstore(failuresAndFilters, i)
+ }
+ }
+
+ function selectMutation(
+ FuzzTestContext memory context
+ )
+ public
+ returns (
+ string memory name,
+ bytes4 mutationSelector,
+ bytes memory expectedRevertReason,
+ MutationState memory mutationState
+ )
+ {
+ // Mark each failure conditions as ineligible if no orders support them.
+ IneligibilityFilter[] memory failuresAndFilters = declareFilters();
+
+ // Ensure all failures have at least one associated filter.
+ failuresAndFilters.ensureFilterSetForEachFailure();
+
+ // Evaluate each filter and assign respective ineligible failures.
+ context.setAllIneligibleFailures(failuresAndFilters);
+
+ (name, mutationSelector, expectedRevertReason, mutationState) = context
+ .failureDetails(
+ context.selectEligibleFailure(),
+ failuresAndFilters
+ );
+ }
+}
+
+library FailureDetailsLib {
+ using FailureDetailsHelperLib for bytes4;
+ using FailureDetailsHelperLib for FuzzTestContext;
+ using MutationContextDeriverLib for FuzzTestContext;
+ using FailureEligibilityLib for IneligibilityFilter[];
+
+ bytes4 constant PANIC = bytes4(0x4e487b71);
+ bytes4 constant ERROR_STRING = bytes4(0x08c379a0);
+
+ function declareFailureDetails()
+ internal
+ pure
+ returns (FailureDetails[] memory failureDetailsArray)
+ {
+ // Set details, including error selector, name, mutation selector, and
+ // an optional function for deriving revert reasons, for each failure.
+ // Create a longer array to avoid potentially cryptic OOR errors.
+ failureDetailsArray = new FailureDetails[](uint256(Failure.length) + 9);
+ uint256 i = 0;
+
+ /////////////////// UPDATE THIS TO ADD FAILURE TESTS ///////////////////
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .InvalidSignature
+ .selector
+ .withOrder(
+ "InvalidSignature",
+ FuzzMutations.mutation_invalidSignature.selector
+ );
+
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .InvalidSigner
+ .selector
+ .withOrder(
+ "InvalidSigner_BadSignature",
+ FuzzMutations.mutation_invalidSigner_BadSignature.selector
+ );
+
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .InvalidSigner
+ .selector
+ .withOrder(
+ "InvalidSigner_ModifiedOrder",
+ FuzzMutations.mutation_invalidSigner_ModifiedOrder.selector
+ );
+
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .BadSignatureV
+ .selector
+ .withOrder(
+ "BadSignatureV",
+ FuzzMutations.mutation_badSignatureV.selector,
+ details_BadSignatureV
+ );
+
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .BadContractSignature
+ .selector
+ .withOrder(
+ "BadContractSignature_BadSignature",
+ FuzzMutations
+ .mutation_badContractSignature_BadSignature
+ .selector
+ );
+
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .BadContractSignature
+ .selector
+ .withOrder(
+ "BadContractSignature_ModifiedOrder",
+ FuzzMutations
+ .mutation_badContractSignature_ModifiedOrder
+ .selector
+ );
+
+ failureDetailsArray[i++] = SignatureVerificationErrors
+ .BadContractSignature
+ .selector
+ .withOrder(
+ "BadContractSignature_MissingMagic",
+ FuzzMutations
+ .mutation_badContractSignature_MissingMagic
+ .selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .ConsiderationLengthNotEqualToTotalOriginal
+ .selector
+ .withOrder(
+ "ConsiderationLengthNotEqualToTotalOriginal_ExtraItems",
+ FuzzMutations
+ .mutation_considerationLengthNotEqualToTotalOriginal_ExtraItems
+ .selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .ConsiderationLengthNotEqualToTotalOriginal
+ .selector
+ .withOrder(
+ "ConsiderationLengthNotEqualToTotalOriginal_MissingItens",
+ FuzzMutations
+ .mutation_considerationLengthNotEqualToTotalOriginal_MissingItems
+ .selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .MissingOriginalConsiderationItems
+ .selector
+ .withOrder(
+ "MissingOriginalConsiderationItems",
+ FuzzMutations
+ .mutation_missingOriginalConsiderationItems
+ .selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .InvalidTime
+ .selector
+ .withOrder(
+ "InvalidTime_NotStarted",
+ FuzzMutations.mutation_invalidTime_NotStarted.selector,
+ details_InvalidTime_NotStarted
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .InvalidTime
+ .selector
+ .withOrder(
+ "InvalidTime_Expired",
+ FuzzMutations.mutation_invalidTime_Expired.selector,
+ details_InvalidTime_Expired
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .InvalidConduit
+ .selector
+ .withOrder(
+ "InvalidConduit",
+ FuzzMutations.mutation_invalidConduit.selector,
+ details_InvalidConduit
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .BadFraction
+ .selector
+ .withOrder(
+ "BadFraction_PartialContractOrder",
+ FuzzMutations.mutation_badFraction_partialContractOrder.selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .BadFraction
+ .selector
+ .withOrder(
+ "BadFraction_NoFill",
+ FuzzMutations.mutation_badFraction_NoFill.selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .BadFraction
+ .selector
+ .withOrder(
+ "BadFraction_Overfill",
+ FuzzMutations.mutation_badFraction_Overfill.selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .CannotCancelOrder
+ .selector
+ .withOrder(
+ "CannotCancelOrder",
+ FuzzMutations.mutation_cannotCancelOrder.selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .OrderIsCancelled
+ .selector
+ .withOrder(
+ "OrderIsCancelled",
+ FuzzMutations.mutation_orderIsCancelled.selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .OrderAlreadyFilled
+ .selector
+ .withOrder(
+ "OrderAlreadyFilled",
+ FuzzMutations.mutation_orderAlreadyFilled.selector,
+ details_OrderAlreadyFilled
+ );
+
+ failureDetailsArray[i++] = FulfillmentApplicationErrors
+ .InvalidFulfillmentComponentData
+ .selector
+ .withGeneric(
+ "InvalidFulfillmentComponentData",
+ FuzzMutations.mutation_invalidFulfillmentComponentData.selector
+ );
+
+ failureDetailsArray[i++] = FulfillmentApplicationErrors
+ .MissingFulfillmentComponentOnAggregation
+ .selector
+ .withGeneric(
+ "MissingFulfillmentComponentOnAggregation",
+ FuzzMutations
+ .mutation_missingFulfillmentComponentOnAggregation
+ .selector,
+ details_MissingFulfillmentComponentOnAggregation
+ );
+
+ failureDetailsArray[i++] = FulfillmentApplicationErrors
+ .OfferAndConsiderationRequiredOnFulfillment
+ .selector
+ .withGeneric(
+ "OfferAndConsiderationRequiredOnFulfillment",
+ FuzzMutations
+ .mutation_offerAndConsiderationRequiredOnFulfillment
+ .selector
+ );
+
+ failureDetailsArray[i++] = FulfillmentApplicationErrors
+ .MismatchedFulfillmentOfferAndConsiderationComponents
+ .selector
+ .withGeneric(
+ "MismatchedFulfillmentOfferAndConsiderationComponents_Modified",
+ FuzzMutations
+ .mutation_mismatchedFulfillmentOfferAndConsiderationComponents_Modified
+ .selector,
+ details_MismatchedFulfillmentOfferAndConsiderationComponents
+ );
+
+ failureDetailsArray[i++] = FulfillmentApplicationErrors
+ .MismatchedFulfillmentOfferAndConsiderationComponents
+ .selector
+ .withGeneric(
+ "MismatchedFulfillmentOfferAndConsiderationComponents_Swapped",
+ FuzzMutations
+ .mutation_mismatchedFulfillmentOfferAndConsiderationComponents_Swapped
+ .selector,
+ details_MismatchedFulfillmentOfferAndConsiderationComponents
+ );
+
+ failureDetailsArray[i++] = ERROR_STRING.withOrder(
+ "Error_OfferItemMissingApproval",
+ FuzzMutations.mutation_offerItemMissingApproval.selector,
+ errorString("NOT_AUTHORIZED")
+ );
+
+ failureDetailsArray[i++] = ERROR_STRING.withOrder(
+ "Error_CallerMissingApproval",
+ FuzzMutations.mutation_callerMissingApproval.selector,
+ errorString("NOT_AUTHORIZED")
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .InvalidMsgValue
+ .selector
+ .withGeneric(
+ "InvalidMsgValue",
+ FuzzMutations.mutation_invalidMsgValue.selector,
+ details_InvalidMsgValue
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .InsufficientNativeTokensSupplied
+ .selector
+ .withGeneric(
+ "InsufficientNativeTokensSupplied",
+ FuzzMutations.mutation_insufficientNativeTokensSupplied.selector
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .NativeTokenTransferGenericFailure
+ .selector
+ .withGeneric(
+ "NativeTokenTransferGenericFailure",
+ FuzzMutations
+ .mutation_insufficientNativeTokensSupplied
+ .selector,
+ details_NativeTokenTransferGenericFailure
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .CriteriaNotEnabledForItem
+ .selector
+ .withGeneric(
+ "CriteriaNotEnabledForItem",
+ FuzzMutations.mutation_criteriaNotEnabledForItem.selector
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .InvalidProof
+ .selector
+ .withCriteria(
+ "InvalidProof_Merkle",
+ FuzzMutations.mutation_invalidMerkleProof.selector
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .InvalidProof
+ .selector
+ .withCriteria(
+ "InvalidProof_Wildcard",
+ FuzzMutations.mutation_invalidWildcardProof.selector
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .OrderCriteriaResolverOutOfRange
+ .selector
+ .withGeneric(
+ "OrderCriteriaResolverOutOfRange",
+ FuzzMutations.mutation_orderCriteriaResolverOutOfRange.selector,
+ details_withZero
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .OfferCriteriaResolverOutOfRange
+ .selector
+ .withCriteria(
+ "OfferCriteriaResolverOutOfRange",
+ FuzzMutations.mutation_offerCriteriaResolverOutOfRange.selector
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .ConsiderationCriteriaResolverOutOfRange
+ .selector
+ .withCriteria(
+ "ConsiderationCriteriaResolverOutOfRange",
+ FuzzMutations
+ .mutation_considerationCriteriaResolverOutOfRange
+ .selector
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .UnresolvedOfferCriteria
+ .selector
+ .withCriteria(
+ "UnresolvedOfferCriteria",
+ FuzzMutations.mutation_unresolvedCriteria.selector,
+ details_unresolvedCriteria
+ );
+
+ failureDetailsArray[i++] = CriteriaResolutionErrors
+ .UnresolvedConsiderationCriteria
+ .selector
+ .withCriteria(
+ "UnresolvedConsiderationCriteria",
+ FuzzMutations.mutation_unresolvedCriteria.selector,
+ details_unresolvedCriteria
+ );
+
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .MissingItemAmount
+ .selector
+ .withGeneric(
+ "MissingItemAmount_OfferItem_FulfillAvailable",
+ FuzzMutations
+ .mutation_missingItemAmount_OfferItem_FulfillAvailable
+ .selector
+ );
+
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .MissingItemAmount
+ .selector
+ .withOrder(
+ "MissingItemAmount_OfferItem",
+ FuzzMutations.mutation_missingItemAmount_OfferItem.selector
+ );
+
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .MissingItemAmount
+ .selector
+ .withOrder(
+ "MissingItemAmount_ConsiderationItem",
+ FuzzMutations
+ .mutation_missingItemAmount_ConsiderationItem
+ .selector
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_generateReturnsInvalidEncoding",
+ FuzzMutations
+ .mutation_invalidContractOrderGenerateReturnsInvalidEncoding
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_generateReverts",
+ FuzzMutations
+ .mutation_invalidContractOrderGenerateReturnsInvalidEncoding
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = HashCalldataContractOfferer
+ .HashCalldataContractOffererRatifyOrderReverts
+ .selector
+ .withOrder(
+ "InvalidContractOrder_ratifyReverts",
+ FuzzMutations
+ .mutation_invalidContractOrderRatifyReverts
+ .selector
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_InsufficientMinimumReceived",
+ FuzzMutations
+ .mutation_invalidContractOrderInsufficientMinimumReceived
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_IncorrectMinimumReceived",
+ FuzzMutations
+ .mutation_invalidContractOrderIncorrectMinimumReceived
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_ExcessMaximumSpent",
+ FuzzMutations
+ .mutation_invalidContractOrderExcessMaximumSpent
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_IncorrectMaximumSpent",
+ FuzzMutations
+ .mutation_invalidContractOrderIncorrectMaximumSpent
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_InvalidMagicValue",
+ FuzzMutations
+ .mutation_invalidContractOrderInvalidMagicValue
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_OfferAmountMismatch",
+ FuzzMutations
+ .mutation_invalidContractOrderOfferAmountMismatch
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidContractOrder
+ .selector
+ .withOrder(
+ "InvalidContractOrder_ConsiderationAmountMismatch",
+ FuzzMutations
+ .mutation_invalidContractOrderConsiderationAmountMismatch
+ .selector,
+ details_withOrderHash
+ );
+
+ failureDetailsArray[i++] = HashValidationZoneOfferer
+ .HashValidationZoneOffererValidateOrderReverts
+ .selector
+ .withOrder(
+ "InvalidRestrictedOrder_reverts",
+ FuzzMutations.mutation_invalidRestrictedOrderReverts.selector
+ );
+
+ failureDetailsArray[i++] = ZoneInteractionErrors
+ .InvalidRestrictedOrder
+ .selector
+ .withOrder(
+ "InvalidRestrictedOrder_InvalidMagicValue",
+ FuzzMutations
+ .mutation_invalidRestrictedOrderInvalidMagicValue
+ .selector,
+ details_withOrderHash
+ );
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .NoContract
+ .selector
+ .withGeneric(
+ "NoContract",
+ FuzzMutations.mutation_noContract.selector,
+ details_NoContract
+ );
+
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .UnusedItemParameters
+ .selector
+ .withOrder(
+ "UnusedItemParameters_Token",
+ FuzzMutations.mutation_unusedItemParameters_Token.selector
+ );
+
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .UnusedItemParameters
+ .selector
+ .withOrder(
+ "UnusedItemParameters_Identifier",
+ FuzzMutations.mutation_unusedItemParameters_Identifier.selector
+ );
+
+ failureDetailsArray[i++] = TokenTransferrerErrors
+ .InvalidERC721TransferAmount
+ .selector
+ .withOrder(
+ "InvalidERC721TransferAmount",
+ FuzzMutations.mutation_invalidERC721TransferAmount.selector,
+ details_InvalidERC721TransferAmount
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .ConsiderationNotMet
+ .selector
+ .withOrder(
+ "ConsiderationNotMet",
+ FuzzMutations.mutation_considerationNotMet.selector,
+ details_ConsiderationNotMet
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .PartialFillsNotEnabledForOrder
+ .selector
+ .withOrder(
+ "PartialFillsNotEnabledForOrder",
+ FuzzMutations.mutation_partialFillsNotEnabledForOrder.selector
+ );
+
+ failureDetailsArray[i++] = AmountDerivationErrors
+ .InexactFraction
+ .selector
+ .withOrder(
+ "InexactFraction",
+ FuzzMutations.mutation_inexactFraction.selector
+ );
+
+ failureDetailsArray[i++] = PANIC.withOrder(
+ "Panic_PartialFillOverflow",
+ FuzzMutations.mutation_partialFillOverflow.selector,
+ details_PanicOverflow
+ );
+
+ failureDetailsArray[i++] = ConsiderationEventsAndErrors
+ .NoSpecifiedOrdersAvailable
+ .selector
+ .withGeneric(
+ "NoSpecifiedOrderAvailable",
+ FuzzMutations.mutation_noSpecifiedOrdersAvailable.selector
+ );
+ ////////////////////////////////////////////////////////////////////////
+
+ if (i != uint256(Failure.length)) {
+ revert("FailureDetailsLib: incorrect # failures specified");
+ }
+
+ // Set the actual length of the array.
+ assembly {
+ mstore(failureDetailsArray, i)
+ }
+ }
+
+ //////////////////// ADD NEW FUNCTIONS HERE WHEN NEEDED ////////////////////
+ function details_NotAuthorized(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ "NOT_AUTHORIZED"
+ );
+ }
+
+ function details_PanicOverflow(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(errorSelector, 0x11);
+ }
+
+ function details_BadSignatureV(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(errorSelector, 0xff);
+ }
+
+ function details_InvalidTime_NotStarted(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal view returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ block.timestamp + 1,
+ block.timestamp + 2
+ );
+ }
+
+ function details_InvalidTime_Expired(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal view returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ block.timestamp - 1,
+ block.timestamp
+ );
+ }
+
+ function details_InvalidConduit(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal view returns (bytes memory expectedRevertReason) {
+ bytes32 conduitKey = keccak256("invalid conduit");
+ (address conduitAddr, ) = context.conduitController.getConduit(
+ conduitKey
+ );
+
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ conduitKey,
+ conduitAddr
+ );
+ }
+
+ function details_withOrderHash(
+ FuzzTestContext memory /* context */,
+ MutationState memory mutationState,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ mutationState.selectedOrderHash
+ );
+ }
+
+ function details_OrderAlreadyFilled(
+ FuzzTestContext memory context,
+ MutationState memory mutationState,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ context.executionState.orderDetails[mutationState.selectedOrderIndex].orderHash
+ );
+ }
+
+ function details_MissingFulfillmentComponentOnAggregation(
+ FuzzTestContext memory /* context */,
+ MutationState memory mutationState,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ uint8(mutationState.side)
+ );
+ }
+
+ function details_MismatchedFulfillmentOfferAndConsiderationComponents(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ uint256(0)
+ );
+ }
+
+ function details_withZero(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ uint256(0)
+ );
+ }
+
+ function details_InvalidMsgValue(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ uint256 value = context.executionState.value == 0 ? 1 : 0;
+ expectedRevertReason = abi.encodeWithSelector(errorSelector, value);
+ }
+
+ function details_NativeTokenTransferGenericFailure(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ uint256 totalImplicitExecutions = (
+ context.expectations.expectedImplicitPostExecutions.length
+ );
+ ReceivedItem memory item;
+ if (context.expectations.expectedNativeTokensReturned == 0) {
+ if (totalImplicitExecutions == 0) {
+ revert(
+ "FailureDetailsLib: not enough implicit executions for unspent item return"
+ );
+ }
+
+ bool foundNative;
+ for (uint256 i = totalImplicitExecutions - 1; i >= 0; --i) {
+ item = context
+ .expectations
+ .expectedImplicitPostExecutions[i]
+ .item;
+ if (item.itemType == ItemType.NATIVE) {
+ foundNative = true;
+ break;
+ }
+
+ if (i == 0) {
+ break;
+ }
+ }
+
+ if (!foundNative) {
+ revert(
+ "FailureDetailsLib: no unspent native token item located with no returned native tokens"
+ );
+ }
+ } else {
+ if (totalImplicitExecutions > 2) {
+ revert(
+ "FailureDetailsLib: not enough implicit executions for native token + unspent return"
+ );
+ }
+
+ bool foundNative;
+ for (uint256 i = totalImplicitExecutions - 1; i > 0; --i) {
+ item = context
+ .expectations
+ .expectedImplicitPostExecutions[i - 1]
+ .item;
+
+ if (item.itemType == ItemType.NATIVE) {
+ foundNative = true;
+ break;
+ }
+ }
+
+ if (!foundNative) {
+ revert(
+ "FailureDetailsLib: no unspent native token item located"
+ );
+ }
+ }
+
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ context.executionState.recipient == address(0)
+ ? context.executionState.caller
+ : context.executionState.recipient,
+ item.amount
+ );
+ }
+
+ function details_unresolvedCriteria(
+ FuzzTestContext memory /* context */,
+ MutationState memory mutationState,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ CriteriaResolver memory resolver = mutationState
+ .selectedCriteriaResolver;
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ resolver.orderIndex,
+ resolver.index
+ );
+ }
+
+ function details_InvalidERC721TransferAmount(
+ FuzzTestContext memory /* context */,
+ MutationState memory /* mutationState */,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(errorSelector, 2);
+ }
+
+ function details_ConsiderationNotMet(
+ FuzzTestContext memory /* context */,
+ MutationState memory mutationState,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ mutationState.selectedOrderIndex,
+ mutationState.selectedOrder.parameters.consideration.length,
+ 100
+ );
+ }
+
+ function details_NoContract(
+ FuzzTestContext memory /* context */,
+ MutationState memory mutationState,
+ bytes4 errorSelector
+ ) internal pure returns (bytes memory expectedRevertReason) {
+ expectedRevertReason = abi.encodeWithSelector(
+ errorSelector,
+ mutationState.selectedArbitraryAddress
+ );
+ }
+
+ function errorString(
+ string memory errorMessage
+ )
+ internal
+ pure
+ returns (
+ function(FuzzTestContext memory, MutationState memory, bytes4)
+ internal
+ pure
+ returns (bytes memory)
+ )
+ {
+ if (
+ keccak256(abi.encodePacked(errorMessage)) ==
+ keccak256(abi.encodePacked("NOT_AUTHORIZED"))
+ ) {
+ return details_NotAuthorized;
+ }
+
+ revert("FailureDetailsLib: unsupported error string");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ function failureDetails(
+ FuzzTestContext memory context,
+ Failure failure,
+ IneligibilityFilter[] memory failuresAndFilters
+ )
+ internal
+ view
+ returns (
+ string memory name,
+ bytes4 mutationSelector,
+ bytes memory revertReason,
+ MutationState memory
+ )
+ {
+ FailureDetails memory details = (
+ declareFailureDetails()[uint256(failure)]
+ );
+
+ MutationState memory mutationState = context.deriveMutationContext(
+ details.derivationMethod,
+ failuresAndFilters.extractFirstFilterForFailure(failure)
+ );
+
+ return (
+ details.name,
+ details.mutationSelector,
+ context.deriveRevertReason(
+ mutationState,
+ details.errorSelector,
+ details.revertReasonDeriver
+ ),
+ mutationState
+ );
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzMutations.sol b/test/foundry/new/helpers/FuzzMutations.sol
new file mode 100644
index 000000000..91329f9c9
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzMutations.sol
@@ -0,0 +1,3598 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType, Side } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ AdvancedOrderLib,
+ OrderParametersLib,
+ ConsiderationItemLib,
+ ItemType,
+ BasicOrderType,
+ ConsiderationItemLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ FulfillmentDetails,
+ OrderDetails
+} from "seaport-sol/fulfillments/lib/Structs.sol";
+
+import {
+ ContractOrderRebate,
+ UnavailableReason
+} from "seaport-sol/SpaceEnums.sol";
+
+import { FractionStatus, FractionUtil } from "./FractionUtil.sol";
+
+import {
+ AdvancedOrdersSpaceGenerator,
+ Offerer,
+ SignatureMethod
+} from "./FuzzGenerators.sol";
+
+import { FuzzTestContext, MutationState } from "./FuzzTestContextLib.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import { FuzzInscribers } from "./FuzzInscribers.sol";
+
+import { FulfillmentDetailsHelper, FuzzDerivers } from "./FuzzDerivers.sol";
+
+import { CheckHelpers } from "./FuzzSetup.sol";
+
+import { FuzzExecutor } from "./FuzzExecutor.sol";
+
+import { FuzzHelpers } from "./FuzzHelpers.sol";
+
+import {
+ MutationEligibilityLib,
+ MutationHelpersLib
+} from "./FuzzMutationHelpers.sol";
+
+import { EIP1271Offerer } from "./EIP1271Offerer.sol";
+
+import {
+ HashCalldataContractOfferer
+} from "../../../../contracts/test/HashCalldataContractOfferer.sol";
+
+import {
+ HashValidationZoneOfferer
+} from "../../../../contracts/test/HashValidationZoneOfferer.sol";
+
+import {
+ OffererZoneFailureReason
+} from "../../../../contracts/test/OffererZoneFailureReason.sol";
+
+interface TestERC20 {
+ function approve(address spender, uint256 amount) external;
+}
+
+interface TestNFT {
+ function setApprovalForAll(address operator, bool approved) external;
+}
+
+library MutationFilters {
+ using AdvancedOrderLib for AdvancedOrder;
+ using FulfillmentDetailsHelper for FuzzTestContext;
+ using FuzzDerivers for FuzzTestContext;
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for AdvancedOrder;
+ using MutationHelpersLib for FuzzTestContext;
+
+ // The following functions are ineligibility helpers. They're prefixed with
+ // `ineligibleWhen` and then have a description of what they check for. They
+ // can be stitched together to form the eligibility filter for a given
+ // mutation. The eligibility filters are prefixed with `ineligibleFor`
+ // followed by the name of the failure the mutation targets.
+
+ function ineligibleWhenUnavailable(
+ FuzzTestContext memory context,
+ uint256 orderIndex
+ ) internal pure returns (bool) {
+ return
+ context.executionState.orderDetails[orderIndex].unavailableReason !=
+ UnavailableReason.AVAILABLE;
+ }
+
+ function ineligibleWhenBasic(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+ if (
+ action == context.seaport.fulfillBasicOrder.selector ||
+ action ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenFulfillAvailable(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+
+ if (
+ action == context.seaport.fulfillAvailableOrders.selector ||
+ action == context.seaport.fulfillAvailableAdvancedOrders.selector
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenMatch(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+ if (
+ action == context.seaport.matchOrders.selector ||
+ action == context.seaport.matchAdvancedOrders.selector
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenNotMatch(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+ if (
+ action != context.seaport.matchOrders.selector &&
+ action != context.seaport.matchAdvancedOrders.selector
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenNotAdvanced(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+
+ if (
+ action == context.seaport.fulfillAvailableOrders.selector ||
+ action == context.seaport.fulfillOrder.selector ||
+ action == context.seaport.matchOrders.selector ||
+ ineligibleWhenBasic(context)
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenUnavailableOrNotAdvanced(
+ FuzzTestContext memory context,
+ uint256 orderIndex
+ ) internal view returns (bool) {
+ if (ineligibleWhenNotAdvanced(context)) {
+ return true;
+ }
+
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenContractOrder(
+ AdvancedOrder memory order
+ ) internal pure returns (bool) {
+ return order.parameters.orderType == OrderType.CONTRACT;
+ }
+
+ function ineligibleWhenNotAvailableOrContractOrder(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ if (ineligibleWhenContractOrder(order)) {
+ return true;
+ }
+
+ return ineligibleWhenUnavailable(context, orderIndex);
+ }
+
+ function ineligibleWhenNotContractOrder(
+ AdvancedOrder memory order
+ ) internal pure returns (bool) {
+ return order.parameters.orderType != OrderType.CONTRACT;
+ }
+
+ function ineligibleWhenNotRestrictedOrder(
+ AdvancedOrder memory order
+ ) internal pure returns (bool) {
+ return (order.parameters.orderType != OrderType.FULL_RESTRICTED &&
+ order.parameters.orderType != OrderType.PARTIAL_RESTRICTED);
+ }
+
+ function ineligibleWhenNotAvailableOrNotContractOrder(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ if (ineligibleWhenNotContractOrder(order)) {
+ return true;
+ }
+
+ return ineligibleWhenUnavailable(context, orderIndex);
+ }
+
+ function ineligibleWhenNotContractOrderOrFulfillAvailable(
+ AdvancedOrder memory order,
+ uint256 /* orderIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ if (ineligibleWhenNotContractOrder(order)) {
+ return true;
+ }
+ return ineligibleWhenFulfillAvailable(context);
+ }
+
+ function ineligibleWhenNotAvailableOrNotRestrictedOrder(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ if (ineligibleWhenNotRestrictedOrder(order)) {
+ return true;
+ }
+
+ return ineligibleWhenUnavailable(context, orderIndex);
+ }
+
+ function ineligibleWhenNotActiveTime(
+ AdvancedOrder memory order
+ ) internal view returns (bool) {
+ return (order.parameters.startTime > block.timestamp ||
+ order.parameters.endTime <= block.timestamp);
+ }
+
+ function ineligibleWhenNoConsiderationLength(
+ AdvancedOrder memory order
+ ) internal pure returns (bool) {
+ return order.parameters.consideration.length == 0;
+ }
+
+ function ineligibleWhenPastMaxFulfilled(
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ uint256 remainingFulfillable = context.executionState.maximumFulfilled;
+
+ if (remainingFulfillable == 0) {
+ return true;
+ }
+
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ remainingFulfillable -= 1;
+ }
+
+ if (remainingFulfillable == 0) {
+ return orderIndex > i;
+ }
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenNotActiveTimeOrNotContractOrder(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // TODO: get more precise about when this is allowed or not
+ if (
+ context.advancedOrdersSpace.orders[orderIndex].rebate !=
+ ContractOrderRebate.NONE
+ ) {
+ return true;
+ }
+
+ if (ineligibleWhenNotActiveTime(order)) {
+ return true;
+ }
+
+ if (ineligibleWhenPastMaxFulfilled(orderIndex, context)) {
+ return true;
+ }
+
+ if (ineligibleWhenNotContractOrder(order)) {
+ return true;
+ }
+
+ OffererZoneFailureReason failureReason = HashCalldataContractOfferer(
+ payable(order.parameters.offerer)
+ ).failureReasons(
+ context.executionState.orderDetails[orderIndex].orderHash
+ );
+
+ return (failureReason ==
+ OffererZoneFailureReason.ContractOfferer_generateReverts);
+ }
+
+ function ineligibleWhenNotActiveTimeOrNotContractOrderOrNoOffer(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ if (order.parameters.offer.length == 0) {
+ return true;
+ }
+
+ return
+ ineligibleWhenNotActiveTimeOrNotContractOrder(
+ order,
+ orderIndex,
+ context
+ );
+ }
+
+ function ineligibleWhenNotActiveTimeOrNotContractOrderOrNoConsideration(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ if (ineligibleWhenNoConsiderationLength(order)) {
+ return true;
+ }
+
+ return
+ ineligibleWhenNotActiveTimeOrNotContractOrder(
+ order,
+ orderIndex,
+ context
+ );
+ }
+
+ function ineligibleWhenOrderHasRebates(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ if (
+ context.executionState.orderDetails[orderIndex].offer.length !=
+ order.parameters.offer.length ||
+ context
+ .executionState
+ .orderDetails[orderIndex]
+ .consideration
+ .length !=
+ order.parameters.consideration.length
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenAnySignatureFailureRequired(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ if (
+ ineligibleWhenNotAvailableOrContractOrder(
+ order,
+ orderIndex,
+ context
+ )
+ ) {
+ return true;
+ }
+
+ if (order.parameters.offerer == context.executionState.caller) {
+ return true;
+ }
+
+ (bool isValidated, , , ) = context.seaport.getOrderStatus(
+ context.executionState.orderDetails[orderIndex].orderHash
+ );
+
+ if (isValidated) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenEOASignatureFailureRequire(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ if (
+ ineligibleWhenAnySignatureFailureRequired(
+ order,
+ orderIndex,
+ context
+ )
+ ) {
+ return true;
+ }
+
+ if (order.parameters.offerer.code.length != 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleWhenNotFulfillmentIngestingFunction(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+
+ if (
+ action == context.seaport.fulfillAdvancedOrder.selector ||
+ action == context.seaport.fulfillOrder.selector ||
+ ineligibleWhenBasic(context)
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // The following functions are ineligibility filters. These should
+ // encapsulate the logic for determining whether an order is ineligible
+ // for a given mutation. These functions are wired up with their
+ // corresponding mutation in `FuzzMutationSelectorLib.sol`.
+
+ function ineligibleForOfferItemMissingApproval(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ // The target failure can't be triggered if the order isn't available.
+ // Seaport only checks for approval when the order is available and
+ // therefore items might actually be transferred.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order doesn't have an
+ // offer item that is non-native and non-filtered. Native tokens don't
+ // have the approval concept and filtered items are not transferred so
+ // they don't get checked.
+ bool locatedEligibleOfferItem;
+ for (uint256 i = 0; i < order.parameters.offer.length; ++i) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (
+ !context.isFilteredOrNative(
+ item,
+ order.parameters.offerer,
+ order.parameters.conduitKey
+ )
+ ) {
+ locatedEligibleOfferItem = true;
+ break;
+ }
+ }
+
+ if (!locatedEligibleOfferItem) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForCallerMissingApproval(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggerer when calling the match
+ // functions because the caller does not provide any items during match
+ // actions.
+ if (ineligibleWhenMatch(context)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order isn't available.
+ // Approval is not required for items on unavailable orders as their
+ // associated transfers are not attempted.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // The target failure can't be triggered on some basic order routes; the
+ // caller does not need ERC20 approvals when accepting bids (as the
+ // offerer provides the ERC20 tokens).
+ uint256 eligibleItemTotal = order.parameters.consideration.length;
+ if (ineligibleWhenBasic(context)) {
+ if (order.parameters.offer[0].itemType == ItemType.ERC20) {
+ eligibleItemTotal = 1;
+ }
+ }
+
+ // The target failure can't be triggered if the order doesn't have a
+ // consideration item that is non-native and non-filtered. Native tokens
+ // don't have the approval concept and filtered items are not
+ // transferred so approvals are not required.
+ bool locatedEligibleConsiderationItem;
+ for (uint256 i = 0; i < eligibleItemTotal; ++i) {
+ ConsiderationItem memory item = order.parameters.consideration[i];
+ if (!context.isFilteredOrNative(item)) {
+ locatedEligibleConsiderationItem = true;
+ break;
+ }
+ }
+
+ if (!locatedEligibleConsiderationItem) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidMsgValue(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered when calling a non-basic
+ // function because only the BasicOrderFiller checks the msg.value and
+ // enforces payable and non-payable routes. Exception: reentrancy.
+ bytes4 action = context.action();
+ if (
+ action != context.seaport.fulfillBasicOrder.selector &&
+ action !=
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInsufficientNativeTokens(
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ // The target failure can't be triggered unless the context produces at
+ // least one native token transfer.
+ if (context.expectations.expectedImpliedNativeExecutions != 0) {
+ return true;
+ }
+
+ // The target failure cannot be triggered unless some amount of native
+ // tokens are actually required.
+ uint256 minimumRequired = context.expectations.minimumValue;
+
+ if (minimumRequired == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForNativeTokenTransferGenericFailure(
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ // The target failure can't be triggered unless the context produces at
+ // least one native token transfer.
+ if (context.expectations.expectedImpliedNativeExecutions == 0) {
+ return true;
+ }
+
+ // The target failure cannot be triggered unless some amount of native
+ // tokens are actually required.
+ uint256 minimumRequired = context.expectations.minimumValue;
+
+ if (minimumRequired == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForCriteriaNotEnabledForItem(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is an
+ // advanced type. Non-advanced functions don't have the criteria concept
+ // and the test framework doesn't pass them in.
+ if (ineligibleWhenNotAdvanced(context)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if there is no order that is
+ // available and has items.
+ bool locatedItem;
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ if (ineligibleWhenUnavailable(context, i)) {
+ continue;
+ }
+
+ AdvancedOrder memory order = context.executionState.previewedOrders[
+ i
+ ];
+ uint256 items = order.parameters.offer.length +
+ order.parameters.consideration.length;
+ if (items != 0) {
+ locatedItem = true;
+ break;
+ }
+ }
+
+ if (!locatedItem) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidProof_Merkle(
+ CriteriaResolver memory criteriaResolver,
+ uint256 /* criteriaResolverIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is an
+ // advanced type. Non-advanced functions don't have the criteria concept
+ // and the test framework doesn't pass them in. Further, the criteria
+ // resolver must point to an available order.
+ if (
+ ineligibleWhenUnavailableOrNotAdvanced(
+ context,
+ criteriaResolver.orderIndex
+ )
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if there is no criteria proof.
+ // The presence of a criteria proof serves as a proxy for non-wildcard
+ // criteria.
+ if (criteriaResolver.criteriaProof.length == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidProof_Wildcard(
+ CriteriaResolver memory criteriaResolver,
+ uint256 /* criteriaResolverIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is an
+ // advanced type. Non-advanced functions don't have the criteria concept
+ // and the test framework doesn't pass them in. Further, the criteria
+ // resolver must point to an available order.
+ if (
+ ineligibleWhenUnavailableOrNotAdvanced(
+ context,
+ criteriaResolver.orderIndex
+ )
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if there are one or criteria
+ // proofs. The presence of a criteria proof serves as a proxy for
+ // non-wildcard criteria.
+ if (criteriaResolver.criteriaProof.length != 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForOfferCriteriaResolverFailure(
+ CriteriaResolver memory criteriaResolver,
+ uint256 /* criteriaResolverIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is an
+ // advanced type. Non-advanced functions don't have the criteria concept
+ // and the test framework doesn't pass them in. Further, the criteria
+ // resolver must point to an available order.
+ if (
+ ineligibleWhenUnavailableOrNotAdvanced(
+ context,
+ criteriaResolver.orderIndex
+ )
+ ) {
+ return true;
+ }
+
+ // This filter handles the offer side. The next one handles the
+ // consideration side. They're split out because the mutations need to
+ // be done differently.
+ if (criteriaResolver.side != Side.OFFER) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForConsiderationCriteriaResolverFailure(
+ CriteriaResolver memory criteriaResolver,
+ uint256 /* criteriaResolverIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is an
+ // advanced type. Non-advanced functions don't have the criteria concept
+ // and the test framework doesn't pass them in. Further, the criteria
+ // resolver must point to an available order.
+ if (
+ ineligibleWhenUnavailableOrNotAdvanced(
+ context,
+ criteriaResolver.orderIndex
+ )
+ ) {
+ return true;
+ }
+
+ // This one handles the consideration side. The previous one handles
+ // the offer side.
+ if (criteriaResolver.side != Side.CONSIDERATION) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForConsiderationLengthNotEqualToTotalOriginal(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ // The target failure can't be triggered if the order isn't available.
+ // Seaport only compares the consideration length to the total original
+ // length if the order is not skipped.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // TODO: This is slightly overly restrictive. It's possible to trigger
+ // this by calling validate directly, which is not something the test
+ // framework does yet.
+ //
+ // The target failure can't be triggered if the order is not a contract
+ // order. Seaport only compares the consideration length to the total
+ // original length in `_getGeneratedOrder`, which is contract order
+ // specific (except in validate, as described in the TODO above).
+ if (order.parameters.orderType != OrderType.CONTRACT) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the consideration length is
+ // 0. TODO: this is a limitation of the current mutation; support cases
+ // where 0-length consideration can still trigger this error by
+ // increasing totalOriginalConsiderationItems rather than decreasing it.
+ if (ineligibleWhenNoConsiderationLength(order)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForMissingOriginalConsiderationItems(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ // The target failure can't be triggered if the order isn't available.
+ // Seaport only checks for missing original consideration items if the
+ // order is not skipped.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order is a contract
+ // order because this check lies on a branch taken only by non-contract
+ // orders.
+ if (ineligibleWhenContractOrder(order)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForBadContractSignature(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered if tampering with the signature
+ // has no effect, e.g. when the order is validated on chain or when the
+ // offerer is the caller.
+ if (
+ ineligibleWhenAnySignatureFailureRequired(
+ order,
+ orderIndex,
+ context
+ )
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the offerer is not a
+ // contract. Seaport only checks 1271 signatures if the offerer is a
+ // contract.
+ if (order.parameters.offerer.code.length == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Determine if an order is unavailable, has been validated, has an offerer
+ // with code, has an offerer equal to the caller, or is a contract order.
+ function ineligibleForInvalidSignature(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered if tampering with an EOA
+ // signature has no effect, e.g. when the order is validated on chain or
+ // when the offerer is the caller. If an order is already validated on
+ // chain, the signature that gets passed in isn't checked. If the
+ // caller is the offerer, that is an ad-hoc signature. The target
+ // failure can't be triggered if the offerer is a 1271 contract, because
+ // Seaport provides a different error message in that case
+ // (BadContractSignature).
+ if (
+ ineligibleWhenEOASignatureFailureRequire(order, orderIndex, context)
+ ) {
+ return true;
+ }
+
+ // NOTE: it is possible to hit the target failure with other signature
+ // lengths, but this test specifically targets ECDSA signatures.
+ //
+ // The target failure can't be triggered if the signature isn't a
+ // normal ECDSA signature or a compact 2098 signature.
+ if (order.signature.length != 64 && order.signature.length != 65) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidSigner(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered if tampering with an EOA
+ // signature has no effect, e.g. when the order is validated on chain or
+ // when the offerer is the caller. If an order is already validated on
+ // chain, the signature that gets passed in isn't checked. The target
+ // failure can't be triggered if the offerer is a 1271 contract, because
+ // Seaport provides a different error message in that case
+ // (BadContractSignature).
+ if (
+ ineligibleWhenEOASignatureFailureRequire(order, orderIndex, context)
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForBadSignatureV(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered if tampering with an EOA
+ // signature has no effect, e.g. when the order is validated on chain or
+ // when the offerer is the caller. If an order is already validated on
+ // chain, the signature that gets passed in isn't checked. The target
+ // failure can't be triggered if the offerer is a 1271 contract, because
+ // Seaport provides a different error message in that case
+ // (BadContractSignature).
+ if (
+ ineligibleWhenEOASignatureFailureRequire(order, orderIndex, context)
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the signature is a normal
+ // ECDSA signature because the v value is only checked if the signature
+ // is 65 bytes long.
+ if (order.signature.length != 65) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidTime(
+ AdvancedOrder memory /* order */,
+ uint256 /* orderIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered if the function call allows
+ // the order to be skipped.
+ if (ineligibleWhenFulfillAvailable(context)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidConduit(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal returns (bool) {
+ // The target failure can't be triggered if the function call allows
+ // the order to be skipped.
+ if (ineligibleWhenFulfillAvailable(context)) {
+ return true;
+ }
+
+ // This is just an optimization that allows the filter to bail out early
+ // and avoid a costly set of checks.
+ if (order.parameters.conduitKey == bytes32(0)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the conduit key on the order
+ // isn't used in an execution on a non-native item. Counduit validity is
+ // only checked when there's an execution.
+
+ // Get the fulfillment details.
+ FulfillmentDetails memory details = context.toFulfillmentDetails(
+ context.executionState.value
+ );
+
+ // Note: We're speculatively applying the mutation here and slightly
+ // breaking the rules. Make sure to undo this mutation.
+ bytes32 oldConduitKey = order.parameters.conduitKey;
+ details.orders[orderIndex].conduitKey = keccak256("invalid conduit");
+ (
+ Execution[] memory explicitExecutions,
+ ,
+ Execution[] memory implicitExecutionsPost,
+
+ ) = context.getExecutionsFromRegeneratedFulfillments(details);
+
+ // Look for invalid executions in explicit executions
+ bool locatedInvalidConduitExecution;
+ for (uint256 i; i < explicitExecutions.length; ++i) {
+ if (
+ explicitExecutions[i].conduitKey ==
+ keccak256("invalid conduit") &&
+ explicitExecutions[i].item.itemType != ItemType.NATIVE
+ ) {
+ locatedInvalidConduitExecution = true;
+ break;
+ }
+ }
+
+ // If we haven't found one yet, keep looking in implicit executions...
+ if (!locatedInvalidConduitExecution) {
+ for (uint256 i = 0; i < implicitExecutionsPost.length; ++i) {
+ if (
+ implicitExecutionsPost[i].conduitKey ==
+ keccak256("invalid conduit") &&
+ implicitExecutionsPost[i].item.itemType != ItemType.NATIVE
+ ) {
+ locatedInvalidConduitExecution = true;
+ break;
+ }
+ }
+ }
+
+ // Note: mutation is undone here as referenced above.
+ details.orders[orderIndex].conduitKey = oldConduitKey;
+
+ if (!locatedInvalidConduitExecution) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForBadFraction(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is an
+ // advanced type. Non-advanced functions don't have the fraction concept
+ // and the test framework doesn't pass them in.
+ if (ineligibleWhenNotAdvanced(context)) {
+ return true;
+ }
+
+ // TODO: In cases where an order is skipped since it's fully filled,
+ // cancelled, or generation failed, it's still possible to get a bad
+ // fraction error. We want to exclude cases where the time is wrong or
+ // maximum fulfilled has already been met. (So this check is
+ // over-excluding potentially eligible orders).
+ return
+ ineligibleWhenNotAvailableOrContractOrder(
+ order,
+ orderIndex,
+ context
+ );
+ }
+
+ function ineligibleForBadFraction_noFill(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is
+ // fulfillAvailableAdvancedOrders, which would just skip the order.
+ // Otherwise the eligibility is the same as ineligibleForBadFraction.
+ bytes4 action = context.action();
+ if (action == context.seaport.fulfillAvailableAdvancedOrders.selector) {
+ return true;
+ }
+
+ return ineligibleForBadFraction(order, orderIndex, context);
+ }
+
+ function ineligibleForCannotCancelOrder(
+ AdvancedOrder memory /* order */,
+ uint256 /* orderIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is
+ // cancelOrder. Note that the testing framework doesn't currently call
+ // cancelOrder.
+ bytes4 action = context.action();
+
+ if (action != context.seaport.cancel.selector) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForOrderIsCancelled(
+ AdvancedOrder memory order,
+ uint256 /* orderIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached if the function call is one of
+ // the fulfillAvailable functions, because the order will be skipped
+ // without triggering the failure.
+ if (ineligibleWhenFulfillAvailable(context)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order is a contract
+ // order because all instances where the target failure can be hit are
+ // on non-contract order paths.
+ if (ineligibleWhenContractOrder(order)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForOrderAlreadyFilled(
+ AdvancedOrder memory order,
+ uint256 /* orderIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached if the function call is one of
+ // the fulfillAvailable functions, because the order will be skipped
+ // without triggering the failure.
+ //
+ // It might be possible to remove ineligibleWhenBasic and instead
+ // differentiate between partially filled and non-filled orders, but it
+ // is probably a heavy lift in the test framework as it currently is. As
+ // of right now, it's not possible to consistently hit the target
+ // failure on a partially filled order when calling a basic function.
+ if (
+ ineligibleWhenFulfillAvailable(context) ||
+ ineligibleWhenBasic(context)
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order is a contract
+ // order because all instances where the target failure can be hit are
+ // on non-contract order paths.
+ if (ineligibleWhenContractOrder(order)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForBadFractionPartialContractOrder(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the order is a contract
+ // order. The target failure here is the one in the
+ // `_validateOrderAndUpdateStatus` function, inside the `if
+ // (orderParameters.orderType == OrderType.CONTRACT) {` block.
+ if (order.parameters.orderType != OrderType.CONTRACT) {
+ return true;
+ }
+
+ if (ineligibleWhenUnavailableOrNotAdvanced(context, orderIndex)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForInvalidFulfillmentComponentData(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // TODO: This filter can be relaxed and probably the others below that
+ // are similar. These failures should be triggerable with both
+ // functions that accept Fulfillment[] and functions that accept
+ // FulfillmentComponent[]. All of these fulfillment failure tests
+ // should be revisited.
+ //
+ // The target failure can't be reached unless the function call is
+ // a type that accepts fulfillment arguments.
+ if (ineligibleWhenNotFulfillmentIngestingFunction(context)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForMissingFulfillmentComponentOnAggregation(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is
+ // a type that accepts fulfillment arguments.
+ if (ineligibleWhenNotFulfillmentIngestingFunction(context)) {
+ return true;
+ }
+
+ if (ineligibleWhenMatch(context)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForOfferAndConsiderationRequiredOnFulfillment(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be reached unless the function call is
+ // a type that accepts fulfillment arguments.
+ if (ineligibleWhenNotFulfillmentIngestingFunction(context)) {
+ return true;
+ }
+
+ if (ineligibleWhenNotMatch(context)) {
+ return true;
+ }
+
+ if (context.executionState.fulfillments.length == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForMismatchedFulfillmentOfferAndConsiderationComponents_Modified(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // This revert lies on a path in `_applyFulfillment`, which is only
+ // called by `_matchAdvancedOrders`, which is only called by the match*
+ // functions.
+ if (ineligibleWhenNotMatch(context)) {
+ return true;
+ }
+
+ // The context needs to have at least one existing fulfillment, because
+ // the failure test checks the case where a fulfillment is modified.
+ if (context.executionState.fulfillments.length < 1) {
+ return true;
+ }
+
+ // Grab the offer components from the first fulfillment. The first isn't
+ // special, but it's the only one that needs to be checked, because it's
+ // the only one that will be modified in the mutation. This is just a
+ // simplification/convenience.
+ FulfillmentComponent[] memory firstOfferComponents = (
+ context.executionState.fulfillments[0].offerComponents
+ );
+
+ // Iterate over the offer components and check if any of them have an
+ // item index that is out of bounds for the order. The mutation modifies
+ // the token of the offer item at the given index, so the index needs to
+ // be within range. This can happen when contract orders modify their
+ // offer or consideration lengths, or in the case of erroneous input for
+ // fulfillments.
+ for (uint256 i = 0; i < firstOfferComponents.length; ++i) {
+ FulfillmentComponent memory component = (firstOfferComponents[i]);
+ if (
+ context
+ .executionState
+ .orders[component.orderIndex]
+ .parameters
+ .offer
+ .length <= component.itemIndex
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function ineligibleForMismatchedFulfillmentOfferAndConsiderationComponents_Swapped(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // This revert lies on a path in `_applyFulfillment`, which is only
+ // called by `_matchAdvancedOrders`, which is only called by the match*
+ // functions.
+ if (ineligibleWhenNotMatch(context)) {
+ return true;
+ }
+
+ // The context needs to have at least two existing fulfillments, because
+ // this failure test checks the case where fulfillments are swapped.
+ if (context.executionState.fulfillments.length < 2) {
+ return true;
+ }
+
+ // Grab the first offer components from the first fulfillment. There's
+ // nothing special about the first fulfillment or the first offer
+ // components, but they're the only ones that need to be checked,
+ // because they're the only ones that will be modified in the mutation.
+ FulfillmentComponent memory firstOfferComponent = (
+ context.executionState.fulfillments[0].offerComponents[0]
+ );
+
+ // Get the item pointed to by the first offer component.
+ SpentItem memory item = context
+ .executionState
+ .orderDetails[firstOfferComponent.orderIndex]
+ .offer[firstOfferComponent.itemIndex];
+
+ // Iterate over the remaining fulfillments and check that the offer item
+ // can be paired with a consideration item that's incompatible with it
+ // in such a way that the target failure can be triggered.
+ for (
+ uint256 i = 1;
+ i < context.executionState.fulfillments.length;
+ ++i
+ ) {
+ FulfillmentComponent memory considerationComponent = (
+ context.executionState.fulfillments[i].considerationComponents[
+ 0
+ ]
+ );
+
+ ReceivedItem memory compareItem = context
+ .executionState
+ .orderDetails[considerationComponent.orderIndex]
+ .consideration[considerationComponent.itemIndex];
+ if (
+ item.itemType != compareItem.itemType ||
+ item.token != compareItem.token ||
+ item.identifier != compareItem.identifier
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function ineligibleForMissingItemAmount_OfferItem_FulfillAvailable(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // There are three flavors of this mutation. This one is for the
+ // fulfillAvailable functions.
+ bytes4 action = context.action();
+ if (
+ action != context.seaport.fulfillAvailableAdvancedOrders.selector &&
+ action != context.seaport.fulfillAvailableOrders.selector
+ ) {
+ return true;
+ }
+
+ // Iterate over offer fulfillments.
+ for (
+ uint256 i;
+ i < context.executionState.offerFulfillments.length;
+ i++
+ ) {
+ // Get the first fulfillment component from the current offer
+ // fulfillment.
+ FulfillmentComponent memory fulfillmentComponent = context
+ .executionState
+ .offerFulfillments[i][0];
+
+ // If the item index is out of bounds, then the mutation can't be
+ // applied.
+ if (
+ context
+ .executionState
+ .orders[fulfillmentComponent.orderIndex]
+ .parameters
+ .offer
+ .length <= fulfillmentComponent.itemIndex
+ ) {
+ return true;
+ }
+
+ // If the fulfillmentComponent's item type is not ERC721 and the
+ // order is available, then the mutation can be applied. 721s are
+ // ruled out because the mutation needs to change the start and end
+ // amounts to 0, which triggers a different revert for 721s. The
+ // order being unavailable is ruled out because the order needs to
+ // be processed for the target failure to be hit.
+ if (
+ context
+ .executionState
+ .orderDetails[fulfillmentComponent.orderIndex]
+ .offer[fulfillmentComponent.itemIndex]
+ .itemType != ItemType.ERC721
+ ) {
+ if (
+ context
+ .executionState
+ .orderDetails[fulfillmentComponent.orderIndex]
+ .unavailableReason == UnavailableReason.AVAILABLE
+ ) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ function ineligibleForMissingItemAmount_OfferItem_Match(
+ FuzzTestContext memory /* context */
+ ) internal pure returns (bool) {
+ // TODO: finish this filter and write a corresponding mutation.
+ return true;
+ }
+
+ function ineligibleForMissingItemAmount_OfferItem(
+ AdvancedOrder memory order,
+ uint256 /* orderIndex */,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The fulfillAvailable functions are ruled out because they're handled
+ // separately. Match functions are ruled out because they need to be
+ // handled separately, too (but are not yet).
+ if (
+ ineligibleWhenFulfillAvailable(context) ||
+ ineligibleWhenMatch(context)
+ ) {
+ return true;
+ }
+
+ // Only a subset of basic orders are eligible for this mutation. This
+ // portion of the filter prevents an Arithmetic over/underflow — as bids
+ // are paid from the offerer, setting the offer amount to zero will
+ // result in an underflow when attempting to reduce that offer amount as
+ // part of paying out additional recipient items.
+ if (
+ ineligibleWhenBasic(context) &&
+ order.parameters.consideration.length > 1 && (
+ order.parameters.consideration[0].itemType == ItemType.ERC721 ||
+ order.parameters.consideration[0].itemType == ItemType.ERC1155
+ )
+ ) {
+ return true;
+ }
+
+ // There needs to be one or more offer items to tamper with.
+ if (order.parameters.offer.length == 0) {
+ return true;
+ }
+
+ // At least one offer item must be native, ERC20, or ERC1155. 721s
+ // with amounts of 0 trigger a different revert.
+ bool hasValidItem;
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (
+ item.itemType != ItemType.ERC721 &&
+ item.itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ hasValidItem = true;
+ break;
+ }
+ }
+ if (!hasValidItem) {
+ return true;
+ }
+
+ // Offerer must not also be consideration recipient for all items,
+ // otherwise the check that triggers the target failure will not be hit
+ // and the function call will not revert.
+ bool offererIsNotRecipient;
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[i];
+ if (item.recipient != order.parameters.offerer) {
+ offererIsNotRecipient = true;
+ break;
+ }
+ }
+ if (!offererIsNotRecipient) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForMissingItemAmount_ConsiderationItem(
+ AdvancedOrder memory /* order */,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal pure returns (bool) {
+ // This filter works basically the same as the OfferItem bookend to it.
+ // Order must be available.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // Order must have at least one offer item
+ if (
+ context
+ .executionState
+ .previewedOrders[orderIndex]
+ .parameters
+ .offer
+ .length < 1
+ ) {
+ return true;
+ }
+
+ // At least one consideration item must be native, ERC20, or ERC1155
+ bool hasValidItem;
+ for (
+ uint256 i;
+ i <
+ context
+ .executionState
+ .previewedOrders[orderIndex]
+ .parameters
+ .consideration
+ .length;
+ i++
+ ) {
+ ConsiderationItem memory item = context
+ .executionState
+ .previewedOrders[orderIndex]
+ .parameters
+ .consideration[i];
+ if (
+ item.itemType != ItemType.ERC721 &&
+ item.itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ hasValidItem = true;
+ break;
+ }
+ }
+ if (!hasValidItem) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForNoContract(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // Can't be one of the fulfillAvailable actions, or else the orders will
+ // just be skipped and the target failure will not be hit. It'll pass or
+ // revert with NoSpecifiedOrdersAvailable or something instead.
+ if (ineligibleWhenFulfillAvailable(context)) {
+ return true;
+ }
+
+ // One non-native execution is necessary to trigger the target failure.
+ // Seaport will only check for a contract if there the context results
+ // in an execution that is not native.
+ for (
+ uint256 i;
+ i < context.expectations.expectedExplicitExecutions.length;
+ i++
+ ) {
+ if (
+ context.expectations.expectedExplicitExecutions[i].item.token !=
+ address(0)
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function ineligibleForUnusedItemParameters_Token(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure cannot be triggered in the fulfillAvailable cases
+ // because it gets skipped instead. And the match cases cause a
+ // MismatchedFulfillmentOfferAndConsiderationComponents(uint256)
+ // instead.
+ if (
+ ineligibleWhenFulfillAvailable(context) ||
+ ineligibleWhenMatch(context)
+ ) {
+ return true;
+ }
+
+ // The target failure is not eligible when rebates are present that may
+ // strip out the unused item parameters. TODO: take a more granular and
+ // precise approach here; only reduced total offer items is actually a
+ // problem, and even those only if all eligible offer items are removed.
+ if (ineligibleWhenOrderHasRebates(order, orderIndex, context)) {
+ return true;
+ }
+
+ // The order must have at least one native item to tamper with. It can't
+ // be a 20, 721, or 1155, because only native items get checked for the
+ // existence of an unused contract address parameter.
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (item.itemType == ItemType.NATIVE) {
+ return false;
+ }
+ }
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[i];
+ if (item.itemType == ItemType.NATIVE) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function ineligibleForUnusedItemParameters_Identifier(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure cannot be triggered in the fulfillAvailable cases
+ // because it gets skipped instead. And the match cases cause a
+ // MismatchedFulfillmentOfferAndConsiderationComponents(uint256)
+ // instead.
+ if (
+ ineligibleWhenFulfillAvailable(context) ||
+ ineligibleWhenMatch(context)
+ ) {
+ return true;
+ }
+
+ // The target failure is not eligible when rebates are present that may
+ // strip out the unused item parameters. TODO: take a more granular and
+ // precise approach here; only reduced total offer items is actually a
+ // problem, and even those only if all eligible offer items are removed.
+ if (ineligibleWhenOrderHasRebates(order, orderIndex, context)) {
+ return true;
+ }
+
+ // The order must have at least one native or ERC20 consideration
+ // item to tamper with. It can't be a 721 or 1155, because only native
+ // and ERC20 items get checked for the existence of an unused
+ // identifier parameter.
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[i];
+ if (
+ item.itemType == ItemType.ERC20 ||
+ item.itemType == ItemType.NATIVE
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function ineligibleForInvalidERC721TransferAmount(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // TODO: this is so the item is not filtered; add test case where
+ // executions are checked. Also deals with partial fills
+ bytes4 action = context.action();
+ if (
+ action == context.seaport.fulfillAdvancedOrder.selector ||
+ ineligibleWhenFulfillAvailable(context) ||
+ ineligibleWhenMatch(context)
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order isn't available.
+ // Seaport only checks for an invalid 721 transfer amount if the
+ // item is actually about to be transferred, which means it needs to be
+ // on an available order.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // The target failure is not eligible when rebates are present that may
+ // strip out the unused item parameters. TODO: take a more granular and
+ // precise approach here; only modified item amounts are actually a
+ // problem, and even those only if there is only one eligible item.
+ if (ineligibleWhenOrderHasRebates(order, orderIndex, context)) {
+ return true;
+ }
+
+ // The order must have at least one 721 item to tamper with.
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return false;
+ }
+ }
+
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[i];
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function ineligibleForConsiderationNotMet(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered on paths other than those
+ // enumerated below, because the revert lies on code paths that are
+ // only reached by those top level function calls.
+ bytes4 action = context.action();
+ if (
+ action != context.seaport.fulfillAvailableAdvancedOrders.selector &&
+ action != context.seaport.fulfillAvailableOrders.selector &&
+ action != context.seaport.matchAdvancedOrders.selector &&
+ action != context.seaport.matchOrders.selector
+ ) {
+ return true;
+ }
+
+ // TODO: This check is overly restrictive and is here as a simplifying
+ // assumption. Explore removing it.
+ if (order.numerator != order.denominator) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order is a contract
+ // order because this check lies on a branch taken only by non-contract
+ // orders.
+ if (ineligibleWhenContractOrder(order)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order isn't available.
+ // Seaport only checks for proper consideration if the order is not
+ // skipped.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order doesn't require
+ // any consideration.
+ if (ineligibleWhenNoConsiderationLength(order)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForPartialFillsNotEnabledForOrder(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // Exclude methods that don't support partial fills.
+ if (ineligibleWhenNotAdvanced(context)) {
+ return true;
+ }
+
+ // Exclude partial and contract orders. It's not possible to trigger
+ // the target failure on an order that supports partial fills. Contract
+ // orders give a different revert.
+ if (
+ order.parameters.orderType == OrderType.PARTIAL_OPEN ||
+ order.parameters.orderType == OrderType.PARTIAL_RESTRICTED ||
+ ineligibleWhenContractOrder(order)
+ ) {
+ return true;
+ }
+
+ // The target failure can't be triggered if the order isn't available.
+ // Seaport only checks whether partial fills are enabled if the order
+ // is not skipped.
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function ineligibleForPanic_PartialFillOverflow(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ bytes4 action = context.action();
+ if (
+ action != context.seaport.fulfillAvailableAdvancedOrders.selector &&
+ action != context.seaport.matchAdvancedOrders.selector &&
+ action != context.seaport.fulfillAdvancedOrder.selector
+ ) {
+ return true;
+ }
+
+ // TODO: this overfits a bit, instead use time + max fulfilled
+ if (ineligibleWhenUnavailable(context, orderIndex)) {
+ return true;
+ }
+
+ return (order.parameters.orderType != OrderType.PARTIAL_OPEN &&
+ order.parameters.orderType != OrderType.PARTIAL_RESTRICTED);
+ }
+
+ function ineligibleForInexactFraction(
+ AdvancedOrder memory order,
+ uint256 orderIndex,
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ if (
+ ineligibleForPanic_PartialFillOverflow(order, orderIndex, context)
+ ) {
+ return true;
+ }
+
+ if (
+ order.parameters.offer.length +
+ order.parameters.consideration.length ==
+ 0
+ ) {
+ return true;
+ }
+
+ uint256 itemAmount = order.parameters.offer.length == 0
+ ? order.parameters.consideration[0].startAmount
+ : order.parameters.offer[0].startAmount;
+
+ if (itemAmount == 0) {
+ itemAmount = order.parameters.offer.length == 0
+ ? order.parameters.consideration[0].endAmount
+ : order.parameters.offer[0].endAmount;
+ }
+
+ // This isn't perfect, but odds of hitting it are slim to none
+ if (itemAmount > type(uint120).max - 1) {
+ itemAmount = 664613997892457936451903530140172392;
+ }
+
+ (, , uint256 totalFilled, uint256 totalSize) = (
+ context.seaport.getOrderStatus(
+ context.executionState.orderDetails[orderIndex].orderHash
+ )
+ );
+
+ return (FractionUtil
+ .getPartialFillResults(
+ uint120(totalFilled),
+ uint120(totalSize),
+ 1,
+ uint120(itemAmount + 1)
+ )
+ .status == FractionStatus.INVALID);
+ }
+
+ function ineligibleForNoSpecifiedOrdersAvailable(
+ FuzzTestContext memory context
+ ) internal view returns (bool) {
+ // The target failure can't be triggered by top level function calls
+ // other than those enumerated below because it lies on
+ // fulfillAvaialable*-specific code paths.
+ bytes4 action = context.action();
+ if (
+ action != context.seaport.fulfillAvailableAdvancedOrders.selector &&
+ action != context.seaport.fulfillAvailableOrders.selector
+ ) {
+ return true;
+ }
+
+ // Exclude orders with criteria resolvers
+ // TODO: Overfilter? Without this check, this test reverts with
+ // ConsiderationCriteriaResolverOutOfRange()
+ if (context.executionState.criteriaResolvers.length > 0) {
+ return true;
+ }
+
+ return false;
+ }
+}
+
+contract FuzzMutations is Test, FuzzExecutor {
+ using AdvancedOrderLib for AdvancedOrder;
+ using CheckHelpers for FuzzTestContext;
+ using ConsiderationItemLib for ConsiderationItem;
+ using FuzzDerivers for FuzzTestContext;
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for AdvancedOrder;
+ using FuzzInscribers for AdvancedOrder;
+ using MutationEligibilityLib for FuzzTestContext;
+ using MutationFilters for FuzzTestContext;
+ using MutationHelpersLib for FuzzTestContext;
+ using OrderParametersLib for OrderParameters;
+
+ function mutation_invalidContractOrderGenerateReverts(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer.
+ HashCalldataContractOfferer(payable(order.parameters.offerer))
+ .setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_generateReverts
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderGenerateReturnsInvalidEncoding(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer.
+ HashCalldataContractOfferer(payable(order.parameters.offerer))
+ .setFailureReason(
+ orderHash,
+ OffererZoneFailureReason
+ .ContractOfferer_generateReturnsInvalidEncoding
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderRatifyReverts(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer.
+ HashCalldataContractOfferer(payable(order.parameters.offerer))
+ .setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_ratifyReverts
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderInvalidMagicValue(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer.
+ HashCalldataContractOfferer(payable(order.parameters.offerer))
+ .setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_InvalidMagicValue
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidRestrictedOrderReverts(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer.
+ HashValidationZoneOfferer(payable(order.parameters.zone))
+ .setFailureReason(orderHash, OffererZoneFailureReason.Zone_reverts);
+
+ exec(context);
+ }
+
+ function mutation_invalidRestrictedOrderInvalidMagicValue(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer.
+ HashValidationZoneOfferer(payable(order.parameters.zone))
+ .setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.Zone_InvalidMagicValue
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderInsufficientMinimumReceived(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ HashCalldataContractOfferer offerer = HashCalldataContractOfferer(
+ payable(order.parameters.offerer)
+ );
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer and by mutating the amount
+ // of the first item in the offer.
+ offerer.setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_InsufficientMinimumReceived
+ );
+
+ // TODO: operate on a fuzzed item (this is always the first item)
+ offerer.addItemAmountMutation(
+ Side.OFFER,
+ 0,
+ order.parameters.offer[0].startAmount - 1,
+ orderHash
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderOfferAmountMismatch(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by mutating the amount of the first
+ // item in the offer. It triggers an `InvalidContractOrder` revert
+ // because the start amount of a contract order offer item must be equal
+ // to the end amount.
+ order.parameters.offer[0].startAmount = 1;
+ order.parameters.offer[0].endAmount = 2;
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderIncorrectMinimumReceived(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ HashCalldataContractOfferer offerer = HashCalldataContractOfferer(
+ payable(order.parameters.offerer)
+ );
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer and by calling a function
+ // that stores a value in the contract offerer that causes the contract
+ // offerer to change the length of the offer in its `generate` function.
+ offerer.setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_IncorrectMinimumReceived
+ );
+
+ // TODO: operate on a fuzzed item (this always operates on last item)
+ offerer.addDropItemMutation(
+ Side.OFFER,
+ order.parameters.offer.length - 1,
+ orderHash
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderConsiderationAmountMismatch(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This triggers an `InvalidContractOrder` revert because the start
+ // amount of a contract order consideration item must be equal to the
+ // end amount.
+ order.parameters.consideration[0].startAmount =
+ order.parameters.consideration[0].endAmount +
+ 1;
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderExcessMaximumSpent(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ HashCalldataContractOfferer offerer = HashCalldataContractOfferer(
+ payable(order.parameters.offerer)
+ );
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer and by calling a function
+ // that stores a value in the contract offerer that causes the contract
+ // offerer to add an extra item to the consideration in its `generate`
+ // function.
+ offerer.setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_ExcessMaximumSpent
+ );
+
+ offerer.addExtraItemMutation(
+ Side.CONSIDERATION,
+ ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: 1,
+ recipient: payable(order.parameters.offerer)
+ }),
+ orderHash
+ );
+
+ exec(context);
+ }
+
+ function mutation_invalidContractOrderIncorrectMaximumSpent(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ HashCalldataContractOfferer offerer = HashCalldataContractOfferer(
+ payable(order.parameters.offerer)
+ );
+
+ // This mutation triggers a revert by setting a failure reason that gets
+ // stored in the HashCalldataContractOfferer and by calling a function
+ // that stores a value in the contract offerer that causes the contract
+ // offerer to change the amount of a consideration item in its
+ // `generate` function.
+ offerer.setFailureReason(
+ orderHash,
+ OffererZoneFailureReason.ContractOfferer_IncorrectMaximumSpent
+ );
+
+ // TODO: operate on a fuzzed item (this is always the first item)
+ offerer.addItemAmountMutation(
+ Side.CONSIDERATION,
+ 0,
+ order.parameters.consideration[0].startAmount + 1,
+ orderHash
+ );
+
+ exec(context);
+ }
+
+ function mutation_offerItemMissingApproval(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+
+ // This mutation triggers a revert by pranking an approval revocation on
+ // a non-filtered, non-native item in the offer. The offerer needs to
+ // have approved items that will be transferred.
+
+ // TODO: pick a random item (this always picks the first one)
+ OfferItem memory item;
+ for (uint256 i = 0; i < order.parameters.offer.length; ++i) {
+ item = order.parameters.offer[i];
+ if (
+ !context.isFilteredOrNative(
+ item,
+ order.parameters.offerer,
+ order.parameters.conduitKey
+ )
+ ) {
+ break;
+ }
+ }
+
+ address approveTo = context.getApproveTo(order.parameters);
+ vm.prank(order.parameters.offerer);
+ if (item.itemType == ItemType.ERC20) {
+ TestERC20(item.token).approve(approveTo, 0);
+ } else {
+ TestNFT(item.token).setApprovalForAll(approveTo, false);
+ }
+
+ exec(context);
+ }
+
+ function mutation_callerMissingApproval(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+
+ // This mutation triggers a revert by pranking an approval revocation on
+ // a non-filtered, non-native item in the consideration. The caller
+ // needs to have approved items that will be transferred.
+
+ // TODO: pick a random item (this always picks the first one)
+ ConsiderationItem memory item;
+ for (uint256 i = 0; i < order.parameters.consideration.length; ++i) {
+ item = order.parameters.consideration[i];
+ if (!context.isFilteredOrNative(item)) {
+ break;
+ }
+ }
+
+ address approveTo = context.getApproveTo();
+ vm.prank(context.executionState.caller);
+ if (item.itemType == ItemType.ERC20) {
+ TestERC20(item.token).approve(approveTo, 0);
+ } else {
+ TestNFT(item.token).setApprovalForAll(approveTo, false);
+ }
+
+ exec(context);
+ }
+
+ function mutation_invalidMsgValue(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */
+ ) external {
+ AdvancedOrder memory order = context.executionState.orders[0];
+
+ BasicOrderType orderType = order.getBasicOrderType();
+
+ // This mutation triggers a revert by setting the msg.value to an
+ // incorrect value. The msg.value must be equal to or greater than the
+ // amount of native tokens required for payable routes and must be
+ // 0 for nonpayable routes.
+
+ // BasicOrderType 0-7 are payable Native-Token routes
+ if (uint8(orderType) < 8) {
+ context.executionState.value = 0;
+ // BasicOrderType 8 and above are nonpayable Token-Token routes
+ } else {
+ vm.deal(context.executionState.caller, 1);
+ context.executionState.value = 1;
+ }
+
+ exec(context);
+ }
+
+ function mutation_insufficientNativeTokensSupplied(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */
+ ) external {
+ uint256 minimumRequired = context.expectations.minimumValue;
+
+ // This mutation triggers a revert by setting the msg.value to one less
+ // than the minimum required value. In this test framework, the minimum
+ // required value is calculated to be the lowest possible value that
+ // will not trigger a revert. Lowering it by one will trigger a revert
+ // because the caller isn't putting enough money in to cover everything.
+
+ context.executionState.value = minimumRequired - 1;
+
+ exec(context);
+ }
+
+ function mutation_criteriaNotEnabledForItem(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */
+ ) external {
+ // This mutation triggers a revert by adding a criteria resolver for an
+ // item that does not have the correct item type. It's not permitted to
+ // add a criteria resolver for an item that is not a *WithCriteria type.
+
+ // Grab the old resolvers.
+ CriteriaResolver[] memory oldResolvers = context
+ .executionState
+ .criteriaResolvers;
+ // Make a new array with one more slot.
+ CriteriaResolver[] memory newResolvers = new CriteriaResolver[](
+ oldResolvers.length + 1
+ );
+ // Copy the old resolvers into the new array.
+ for (uint256 i = 0; i < oldResolvers.length; ++i) {
+ newResolvers[i] = oldResolvers[i];
+ }
+
+ uint256 orderIndex;
+ Side side;
+
+ // Iterate over orders.
+ for (
+ ;
+ orderIndex < context.executionState.orders.length;
+ ++orderIndex
+ ) {
+ // Skip unavailable orders.
+ if (context.ineligibleWhenUnavailable(orderIndex)) {
+ continue;
+ }
+
+ // Grab the order at the current index.
+ AdvancedOrder memory order = context.executionState.previewedOrders[
+ orderIndex
+ ];
+
+ // If it has an offer, set the side to offer and break, otherwise
+ // if it has a consideration, set the side to consideration and
+ // break.
+ if (order.parameters.offer.length > 0) {
+ side = Side.OFFER;
+ break;
+ } else if (order.parameters.consideration.length > 0) {
+ side = Side.CONSIDERATION;
+ break;
+ }
+ }
+
+ // Add a new resolver to the end of the array with the correct index and
+ // side, but with empty values otherwise.
+ newResolvers[oldResolvers.length] = CriteriaResolver({
+ orderIndex: orderIndex,
+ side: side,
+ index: 0,
+ identifier: 0,
+ criteriaProof: new bytes32[](0)
+ });
+
+ // Set the new resolvers to the execution state.
+ context.executionState.criteriaResolvers = newResolvers;
+
+ exec(context);
+ }
+
+ function mutation_invalidSignature(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the signature to a
+ // signature with an invalid length. Seaport rejects signatures that
+ // are not one of the valid lengths.
+
+ // TODO: fuzz on size of invalid signature
+ order.signature = "";
+
+ exec(context);
+ }
+
+ function mutation_invalidSigner_BadSignature(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the signature to a
+ // signature that recovers to an invalid address. Seaport rejects
+ // signatures that do not recover to the correct signer address.
+
+ order.signature[0] = bytes1(uint8(order.signature[0]) ^ 0x01);
+
+ exec(context);
+ }
+
+ function mutation_invalidSigner_ModifiedOrder(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by changing the order so that the
+ // otherwise-valid signature is for a different order. Seaport rejects
+ // signatures that do not recover to the correct signer address.
+
+ order.parameters.salt ^= 0x01;
+
+ exec(context);
+ }
+
+ function mutation_badSignatureV(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the byte at the end of the
+ // signature to a value that is not 27 or 28. Seaport rejects
+ // signatures that do not have a valid V value.
+
+ order.signature[64] = 0xff;
+
+ exec(context);
+ }
+
+ function mutation_badContractSignature_BadSignature(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the signature to a
+ // signature that recovers to an invalid address, but only in cases
+ // where a 1271 contract is the signer. Seaport rejects signatures that
+ // do not recover to the correct signer address.
+
+ if (order.signature.length == 0) {
+ order.signature = new bytes(1);
+ }
+
+ order.signature[0] ^= 0x01;
+
+ exec(context);
+ }
+
+ function mutation_badContractSignature_ModifiedOrder(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by changing the order so that the
+ // otherwise-valid signature is for a different order, but only in cases
+ // where a 1271 contract is the signer. Seaport rejects signatures that
+ // do not recover to the correct signer address.
+
+ order.parameters.salt ^= 0x01;
+
+ exec(context);
+ }
+
+ function mutation_badContractSignature_MissingMagic(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by calling a function on the 1271
+ // offerer that causes it to not return the magic value. Seaport
+ // requires that 1271 contracts return the magic value.
+
+ EIP1271Offerer(payable(order.parameters.offerer)).returnEmpty();
+
+ exec(context);
+ }
+
+ function mutation_considerationLengthNotEqualToTotalOriginal_ExtraItems(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the total original
+ // consideration items value to a value that is less than the length of
+ // the consideration array of a contract order (tips on a contract
+ // order). The total original consideration items value must be equal to
+ // the length of the consideration array.
+
+ order.parameters.totalOriginalConsiderationItems =
+ order.parameters.consideration.length -
+ 1;
+
+ exec(context);
+ }
+
+ function mutation_considerationLengthNotEqualToTotalOriginal_MissingItems(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the total original
+ // consideration items value to a value that is greater than the length
+ // of the consideration array of a contract order (tips on a contract
+ // order). The total original consideration items value must be equal to
+ // the length of the consideration array.
+
+ order.parameters.totalOriginalConsiderationItems =
+ order.parameters.consideration.length +
+ 1;
+
+ exec(context);
+ }
+
+ function mutation_missingOriginalConsiderationItems(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the total original
+ // consideration items value to a value that is greater than the length
+ // of the consideration array of a non-contract order. The total
+ // original consideration items value must be equal to the length of the
+ // consideration array.
+
+ order.parameters.totalOriginalConsiderationItems =
+ order.parameters.consideration.length +
+ 1;
+
+ exec(context);
+ }
+
+ function mutation_invalidTime_NotStarted(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the start time to a value
+ // that is in the future. The start time must be in the past.
+
+ order.parameters.startTime = block.timestamp + 1;
+ order.parameters.endTime = block.timestamp + 2;
+
+ exec(context);
+ }
+
+ function mutation_invalidTime_Expired(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the end time to a value
+ // that is not in the future. The end time must be in the future.
+
+ order.parameters.startTime = block.timestamp - 1;
+ order.parameters.endTime = block.timestamp;
+
+ exec(context);
+ }
+
+ function mutation_invalidConduit(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation triggers a revert by setting the conduit key to an
+ // invalid value. The conduit key must correspond to a real, valid
+ // conduit.
+
+ // Note: We should also adjust approvals for any items approved on the
+ // old conduit, but the error here will be thrown before transfers
+ // begin to occur.
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ order.parameters.conduitKey = keccak256("invalid conduit");
+
+ _signOrValidateMutatedOrder(context, orderIndex);
+
+ context
+ .executionState
+ .previewedOrders[orderIndex]
+ .parameters
+ .conduitKey = keccak256("invalid conduit");
+
+ context = context.withDerivedOrderDetails().withDerivedFulfillments();
+ if (
+ context.advancedOrdersSpace.orders[orderIndex].signatureMethod ==
+ SignatureMethod.VALIDATE
+ ) {
+ order.inscribeOrderStatusValidated(true, context.seaport);
+ }
+
+ exec(context);
+ }
+
+ function mutation_badFraction_NoFill(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the numerator to 0. The
+ // numerator must be greater than 0.
+
+ order.numerator = 0;
+
+ exec(context);
+ }
+
+ function mutation_badFraction_Overfill(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the numerator to a value
+ // that is greater than the denominator. The numerator must be less than
+ // or equal to the denominator.
+
+ // TODO: fuzz on a range of potential overfill amounts
+ order.numerator = 2;
+ order.denominator = 1;
+
+ exec(context);
+ }
+
+ function mutation_orderIsCancelled(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ bytes32 orderHash = mutationState.selectedOrderHash;
+
+ // This mutation triggers a revert by using cheatcodes to mark the order
+ // as cancelled in the Seaport internal mapping. A cancelled order
+ // cannot be filled.
+
+ FuzzInscribers.inscribeOrderStatusCancelled(
+ orderHash,
+ true,
+ context.seaport
+ );
+
+ exec(context);
+ }
+
+ function mutation_orderAlreadyFilled(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+
+ // This mutation triggers a revert by using cheatcodes to mark the order
+ // as filled in the Seaport internal mapping. An order that has already
+ // been filled cannot be filled again.
+
+ order.inscribeOrderStatusNumeratorAndDenominator(1, 1, context.seaport);
+
+ exec(context);
+ }
+
+ function mutation_cannotCancelOrder(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ AdvancedOrder memory order = mutationState.selectedOrder;
+
+ // This mutation triggers a revert by setting the caller as an address
+ // that is not the offerer. Only the offerer can cancel an order.
+
+ context.executionState.caller = address(
+ uint160(order.parameters.offerer) - 1
+ );
+
+ exec(context);
+ }
+
+ function mutation_badFraction_partialContractOrder(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation triggers a revert by setting the numerator to a value
+ // that is less than the denominator. Contract orders can't have a
+ // partial fill.
+
+ order.numerator = 6;
+ order.denominator = 9;
+
+ exec(context);
+ }
+
+ function mutation_invalidFulfillmentComponentData(
+ FuzzTestContext memory context
+ ) external {
+ // This mutation triggers a revert by modifying or creating a
+ // fulfillment component that uses an order index that is out of bounds.
+ // The order index must be within bounds.
+
+ if (context.executionState.fulfillments.length != 0) {
+ // If there's already one or more fulfillments, just set the order index
+ // for the first fulfillment's consideration component to an invalid
+ // value.
+ context
+ .executionState
+ .fulfillments[0]
+ .considerationComponents[0]
+ .orderIndex = context.executionState.orders.length;
+ } else {
+ // Otherwise, create a new, empty fulfillment.
+ context.executionState.fulfillments = new Fulfillment[](1);
+
+ context
+ .executionState
+ .fulfillments[0]
+ .offerComponents = new FulfillmentComponent[](1);
+
+ context
+ .executionState
+ .fulfillments[0]
+ .considerationComponents = new FulfillmentComponent[](1);
+
+ context
+ .executionState
+ .fulfillments[0]
+ .considerationComponents[0]
+ .orderIndex = context.executionState.orders.length;
+ }
+
+ // Do the same sort of thing for offer fulfillments and consideration
+ // fulfillments.
+ if (context.executionState.offerFulfillments.length != 0) {
+ context.executionState.offerFulfillments[0][0].orderIndex = context
+ .executionState
+ .orders
+ .length;
+ } else if (
+ context.executionState.considerationFulfillments.length != 0
+ ) {
+ context
+ .executionState
+ .considerationFulfillments[0][0].orderIndex = context
+ .executionState
+ .orders
+ .length;
+ } else {
+ context.executionState.considerationFulfillments = (
+ new FulfillmentComponent[][](1)
+ );
+
+ context.executionState.considerationFulfillments[0] = (
+ new FulfillmentComponent[](1)
+ );
+
+ context
+ .executionState
+ .considerationFulfillments[0][0].orderIndex = context
+ .executionState
+ .orders
+ .length;
+ }
+
+ exec(context);
+ }
+
+ function mutation_missingFulfillmentComponentOnAggregation(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation triggers a revert by creating or swapping in an empty
+ // fulfillment component array. At least one fulfillment component must
+ // be supplied.
+
+ // If the mutation side is OFFER and there are no offer fulfillments,
+ // create a new, empty offer fulfillment. Otherwise, reset the first
+ // offer fulfillment to an empty FulfillmentComponent array. If the
+ // mutation side is CONSIDERATION, reset the first consideration
+ // fulfillment to an empty FulfillmentComponent array.
+ if (mutationState.side == Side.OFFER) {
+ if (context.executionState.offerFulfillments.length == 0) {
+ context
+ .executionState
+ .offerFulfillments = new FulfillmentComponent[][](1);
+ } else {
+ context.executionState.offerFulfillments[
+ 0
+ ] = new FulfillmentComponent[](0);
+ }
+ } else if (mutationState.side == Side.CONSIDERATION) {
+ context.executionState.considerationFulfillments[
+ 0
+ ] = new FulfillmentComponent[](0);
+ }
+
+ exec(context);
+ }
+
+ function mutation_offerAndConsiderationRequiredOnFulfillment(
+ FuzzTestContext memory context
+ ) external {
+ // This mutation triggers a revert by setting the offerComponents and
+ // considerationComponents arrays to empty FulfillmentComponent arrays.
+ // At least one offer component and one consideration component must be
+ // supplied.
+
+ context.executionState.fulfillments[0] = Fulfillment({
+ offerComponents: new FulfillmentComponent[](0),
+ considerationComponents: new FulfillmentComponent[](0)
+ });
+
+ exec(context);
+ }
+
+ function mutation_mismatchedFulfillmentOfferAndConsiderationComponents_Modified(
+ FuzzTestContext memory context
+ ) external {
+ // This mutation triggers a revert by modifying the token addresses of
+ // all of the offer items referenced in the first fulfillment's offer
+ // components. Corresponding offer and consideration components must
+ // each target the same item.
+
+ // Get the first fulfillment's offer components.
+ FulfillmentComponent[] memory firstOfferComponents = (
+ context.executionState.fulfillments[0].offerComponents
+ );
+
+ // Iterate over the offer components and modify the token address of
+ // each corresponding offer item. This preserves the intended
+ // aggregation and filtering patterns, but causes the offer and
+ // consideration components to have mismatched token addresses.
+ for (uint256 i = 0; i < firstOfferComponents.length; ++i) {
+ FulfillmentComponent memory component = (firstOfferComponents[i]);
+ address token = context
+ .executionState
+ .orders[component.orderIndex]
+ .parameters
+ .offer[component.itemIndex]
+ .token;
+ address modifiedToken = address(uint160(token) ^ 1);
+ context
+ .executionState
+ .orders[component.orderIndex]
+ .parameters
+ .offer[component.itemIndex]
+ .token = modifiedToken;
+ }
+
+ // "Resign" the orders.
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ _signOrValidateMutatedOrder(context, i);
+ }
+
+ exec(context);
+ }
+
+ function mutation_mismatchedFulfillmentOfferAndConsiderationComponents_Swapped(
+ FuzzTestContext memory context
+ ) external {
+ // This mutation triggers a revert by shuffling around the offer
+ // components in such a way that an item with an incorrect type,
+ // address, or identifier is attempted to be paired up with a
+ // consideration component that expects the pre-suhffle type, address,
+ // or identifier. A fulfillment's offer and condieration components must
+ // harmonize.
+
+ // Store a reference to the first fulfillment's offer components for
+ // later use.
+ FulfillmentComponent[] memory firstOfferComponents = (
+ context.executionState.fulfillments[0].offerComponents
+ );
+
+ // Get the first fulfillment's first offer component.
+ FulfillmentComponent memory firstOfferComponent = (
+ firstOfferComponents[0]
+ );
+
+ // Use the indexes in the first offer component to get the item.
+ SpentItem memory item = context
+ .executionState
+ .orderDetails[firstOfferComponent.orderIndex]
+ .offer[firstOfferComponent.itemIndex];
+
+ // Start iterating at the second fulfillment, since the first is the one
+ // that gets mutated.
+ uint256 i = 1;
+ for (; i < context.executionState.fulfillments.length; ++i) {
+ // Get the first consideration component of the current fulfillment.
+ FulfillmentComponent memory considerationComponent = (
+ context.executionState.fulfillments[i].considerationComponents[
+ 0
+ ]
+ );
+
+ // Use the indexes in the first consideration component to get the
+ // item that needs to be compared against.
+ ReceivedItem memory compareItem = context
+ .executionState
+ .orderDetails[considerationComponent.orderIndex]
+ .consideration[considerationComponent.itemIndex];
+
+ // If it's not a match, then it works for the mutation, so break.
+ if (
+ item.itemType != compareItem.itemType ||
+ item.token != compareItem.token ||
+ item.identifier != compareItem.identifier
+ ) {
+ break;
+ }
+ }
+
+ // Swap offer components of the first and current fulfillments.
+ FulfillmentComponent[] memory swappedOfferComponents = (
+ context.executionState.fulfillments[i].offerComponents
+ );
+
+ // Set up a pointer that will be used temporarily in the shuffle.
+ bytes32 swappedPointer;
+
+ assembly {
+ // Store the pointer to the swapped offer components.
+ swappedPointer := swappedOfferComponents
+ // Set the swapped offer components to the first offer components.
+ swappedOfferComponents := firstOfferComponents
+ // Set the first offer components to non-compatible offer
+ // components.
+ firstOfferComponents := swappedPointer
+ }
+
+ // Set the offer components of the first fulfillment to the mutated
+ // firstOfferComponents.
+ context
+ .executionState
+ .fulfillments[0]
+ .offerComponents = firstOfferComponents;
+
+ // Set the offer components of the current fulfillment to the offer
+ // components that were originally in the first fulfillment.
+ context
+ .executionState
+ .fulfillments[i]
+ .offerComponents = swappedOfferComponents;
+
+ exec(context);
+ }
+
+ function mutation_invalidWildcardProof(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 criteriaResolverIndex = mutationState
+ .selectedCriteriaResolverIndex;
+ CriteriaResolver memory resolver = context
+ .executionState
+ .criteriaResolvers[criteriaResolverIndex];
+
+ // This mutation works by jamming in a proof for the selected criteria
+ // resolver, but only operates on criteria resolvers that aren't
+ // expected to have proofs at all, see
+ // ineligibleForInvalidProof_Wildcard.
+
+ bytes32[] memory criteriaProof = new bytes32[](1);
+ resolver.criteriaProof = criteriaProof;
+
+ exec(context);
+ }
+
+ function mutation_invalidMerkleProof(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 criteriaResolverIndex = mutationState
+ .selectedCriteriaResolverIndex;
+ CriteriaResolver memory resolver = context
+ .executionState
+ .criteriaResolvers[criteriaResolverIndex];
+
+ // This mutation triggers a revert by modifying the first proof element
+ // in a criteria resolver's proof array. Seaport will reject a criteria
+ // resolver if the the identifiers, criteria, and proof do not
+ // harmonize.
+
+ bytes32 firstProofElement = resolver.criteriaProof[0];
+ resolver.criteriaProof[0] = bytes32(uint256(firstProofElement) ^ 1);
+
+ exec(context);
+ }
+
+ function mutation_orderCriteriaResolverOutOfRange(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */
+ ) external {
+ // This mutation works by adding an extra criteria resolver with an
+ // order index that's out of range. The order index on a criteria
+ // resolver must be within the range of the orders array.
+
+ CriteriaResolver[] memory oldResolvers = context
+ .executionState
+ .criteriaResolvers;
+ CriteriaResolver[] memory newResolvers = new CriteriaResolver[](
+ oldResolvers.length + 1
+ );
+ for (uint256 i = 0; i < oldResolvers.length; ++i) {
+ newResolvers[i] = oldResolvers[i];
+ }
+
+ newResolvers[oldResolvers.length] = CriteriaResolver({
+ orderIndex: context.executionState.orders.length,
+ side: Side.OFFER,
+ index: 0,
+ identifier: 0,
+ criteriaProof: new bytes32[](0)
+ });
+
+ context.executionState.criteriaResolvers = newResolvers;
+
+ exec(context);
+ }
+
+ function mutation_offerCriteriaResolverOutOfRange(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by mutating an existing criteria resolver to have
+ // an item index that's out of range. The item index on a criteria
+ // resolver must be within the range of the order's offer array if the
+ // criteria resolver's side is OFFER, as is the case for this mutation.
+
+ uint256 criteriaResolverIndex = mutationState
+ .selectedCriteriaResolverIndex;
+ CriteriaResolver memory resolver = context
+ .executionState
+ .criteriaResolvers[criteriaResolverIndex];
+
+ OrderDetails memory order = context.executionState.orderDetails[
+ resolver.orderIndex
+ ];
+ resolver.index = order.offer.length;
+
+ exec(context);
+ }
+
+ function mutation_considerationCriteriaResolverOutOfRange(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by mutating an existing criteria resolver to have
+ // an item index that's out of range. The item index on a criteria
+ // resolver must be within the range of the order's consideration
+ // array if the criteria resolver's side is CONSIDERATION, as is the
+ // case for this mutation.
+
+ uint256 criteriaResolverIndex = mutationState
+ .selectedCriteriaResolverIndex;
+ CriteriaResolver memory resolver = context
+ .executionState
+ .criteriaResolvers[criteriaResolverIndex];
+
+ OrderDetails memory order = context.executionState.orderDetails[
+ resolver.orderIndex
+ ];
+ resolver.index = order.consideration.length;
+
+ exec(context);
+ }
+
+ function mutation_unresolvedCriteria(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by copying over all the criteria resolvers except
+ // for the selected one, which is left empty. Without a criteria
+ // resolver, the item with a *WITH_CRITERIA type will be left unresolved
+ // by the end of the _applyCriteriaResolvers* functions, which is not
+ // permitted.
+
+ uint256 criteriaResolverIndex = mutationState
+ .selectedCriteriaResolverIndex;
+
+ CriteriaResolver[] memory oldResolvers = context
+ .executionState
+ .criteriaResolvers;
+ CriteriaResolver[] memory newResolvers = new CriteriaResolver[](
+ oldResolvers.length - 1
+ );
+
+ // Iterate from 0 to the selected criteria resolver index and copy
+ // resolvers.
+ for (uint256 i = 0; i < criteriaResolverIndex; ++i) {
+ newResolvers[i] = oldResolvers[i];
+ }
+
+ // Iterate from the selected criteria resolver index + 1 to the end and
+ // copy resolvers.
+ for (
+ uint256 i = criteriaResolverIndex + 1;
+ i < oldResolvers.length;
+ ++i
+ ) {
+ newResolvers[i - 1] = oldResolvers[i];
+ }
+
+ context.executionState.criteriaResolvers = newResolvers;
+
+ exec(context);
+ }
+
+ function mutation_missingItemAmount_OfferItem_FulfillAvailable(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */
+ ) external {
+ // This mutation works by setting the amount of the first eligible item
+ // in an offer to 0. Items cannot have 0 amounts.
+
+ // Iterate over all the offer fulfillments. This mutation needs to be
+ // applied to a an item that won't be filtered. The presence of a
+ // fulfillment that points to the item serves as a proxy that the item's
+ // transfer won't be filtered.
+ for (
+ uint256 i;
+ i < context.executionState.offerFulfillments.length;
+ i++
+ ) {
+ FulfillmentComponent memory fulfillmentComponent = context
+ .executionState
+ .offerFulfillments[i][0];
+
+ AdvancedOrder memory order = context.executionState.orders[
+ fulfillmentComponent.orderIndex
+ ];
+
+ // The item cannot be a 721, because setting the amount of a 721 to
+ // 0 triggers a different revert.
+ if (
+ context
+ .executionState
+ .orderDetails[fulfillmentComponent.orderIndex]
+ .offer[fulfillmentComponent.itemIndex]
+ .itemType != ItemType.ERC721
+ ) {
+ // The order must be available.
+ if (
+ context
+ .executionState
+ .orderDetails[fulfillmentComponent.orderIndex]
+ .unavailableReason == UnavailableReason.AVAILABLE
+ ) {
+ // For all orders, set the start and end amounts to 0.
+ order
+ .parameters
+ .offer[fulfillmentComponent.itemIndex]
+ .startAmount = 0;
+ order
+ .parameters
+ .offer[fulfillmentComponent.itemIndex]
+ .endAmount = 0;
+
+ // For contract orders, tell the test contract about the
+ // mutation so that it knows to give back bad amounts.
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ HashCalldataContractOfferer(
+ payable(order.parameters.offerer)
+ ).addItemAmountMutation(
+ Side.OFFER,
+ fulfillmentComponent.itemIndex,
+ 0,
+ context
+ .executionState
+ .orderDetails[
+ fulfillmentComponent.orderIndex
+ ]
+ .orderHash
+ );
+ }
+
+ _signOrValidateMutatedOrder(
+ context,
+ fulfillmentComponent.orderIndex
+ );
+
+ break;
+ }
+ }
+ }
+
+ exec(context);
+ }
+
+ function mutation_missingItemAmount_OfferItem(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by setting the amount of the first eligible item
+ // in an offer to 0. Items cannot have 0 amounts.
+
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ uint256 firstNon721OfferItem;
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (
+ item.itemType != ItemType.ERC721 &&
+ item.itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ firstNon721OfferItem = i;
+ break;
+ }
+ }
+
+ order.parameters.offer[firstNon721OfferItem].startAmount = 0;
+ order.parameters.offer[firstNon721OfferItem].endAmount = 0;
+
+ _signOrValidateMutatedOrder(context, orderIndex);
+
+ exec(context);
+ }
+
+ function mutation_missingItemAmount_ConsiderationItem(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works in the same way as
+ // mutation_missingItemAmount_OfferItem_FulfillAvailable aboce, except
+ // that it targets consideration items instead of offer items. Items
+ // cannot have 0 amounts.
+
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ uint256 firstNon721ConsiderationItem;
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[i];
+ if (
+ item.itemType != ItemType.ERC721 &&
+ item.itemType != ItemType.ERC721_WITH_CRITERIA
+ ) {
+ firstNon721ConsiderationItem = i;
+ break;
+ }
+ }
+
+ order
+ .parameters
+ .consideration[firstNon721ConsiderationItem]
+ .startAmount = 0;
+ order
+ .parameters
+ .consideration[firstNon721ConsiderationItem]
+ .endAmount = 0;
+
+ if (order.parameters.orderType == OrderType.CONTRACT) {
+ HashCalldataContractOfferer(payable(order.parameters.offerer))
+ .addItemAmountMutation(
+ Side.CONSIDERATION,
+ firstNon721ConsiderationItem,
+ 0,
+ mutationState.selectedOrderHash
+ );
+ }
+
+ _signOrValidateMutatedOrder(context, orderIndex);
+
+ exec(context);
+ }
+
+ function mutation_noContract(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by changing the contract address of token from
+ // an actual token address to a non-contract address. The token address
+ // has to be swapped throughout all orders to avoid hitting earlier
+ // reverts, such as aggregation reverts or mismatch reverts. Seaport
+ // rejects calls to non-contract addresses.
+
+ address targetContract;
+
+ // Iterate over expectedExplicitExecutions to find a token address.
+ for (
+ uint256 i;
+ i < context.expectations.expectedExplicitExecutions.length;
+ i++
+ ) {
+ address candidate = context
+ .expectations
+ .expectedExplicitExecutions[i]
+ .item
+ .token;
+
+ if (candidate != address(0)) {
+ targetContract = candidate;
+ break;
+ }
+ }
+
+ // Iterate over orders and replace all instances of the target contract
+ // address with the selected arbitrary address.
+ for (uint256 i; i < context.executionState.orders.length; i++) {
+ AdvancedOrder memory order = context.executionState.orders[i];
+
+ for (uint256 j; j < order.parameters.consideration.length; j++) {
+ ConsiderationItem memory item = order.parameters.consideration[
+ j
+ ];
+ if (item.token == targetContract) {
+ item.token = mutationState.selectedArbitraryAddress;
+ }
+ }
+
+ for (uint256 j; j < order.parameters.offer.length; j++) {
+ OfferItem memory item = order.parameters.offer[j];
+ if (item.token == targetContract) {
+ item.token = mutationState.selectedArbitraryAddress;
+ }
+ }
+
+ _signOrValidateMutatedOrder(context, i);
+ }
+
+ exec(context);
+ }
+
+ function mutation_unusedItemParameters_Token(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by setting the token address of the first
+ // eligible item in an offer to a nonzero address. An item with
+ // ItemType.NATIVE cannot have a nonzero token address.
+
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // Add nonzero token address to first native item
+ bool nativeItemFound;
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (item.itemType == ItemType.NATIVE) {
+ item.token = address(1);
+ nativeItemFound = true;
+ break;
+ }
+ }
+
+ if (!nativeItemFound) {
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[
+ i
+ ];
+
+ if (item.itemType == ItemType.NATIVE) {
+ item.token = address(1);
+ nativeItemFound = true;
+ break;
+ }
+ }
+ }
+
+ _signOrValidateMutatedOrder(context, orderIndex);
+
+ exec(context);
+ }
+
+ function mutation_unusedItemParameters_Identifier(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by setting the identifier of the first eligible
+ // item in an offer to a nonzero value. An item with ItemType.NATIVE
+ // or ItemType.ERC20 cannot have a nonzero identifier.
+
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // Add nonzero identifierOrCriteria to first valid item
+ bool validItemFound;
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (
+ item.itemType == ItemType.ERC20 ||
+ item.itemType == ItemType.NATIVE
+ ) {
+ item.identifierOrCriteria = 1;
+ validItemFound = true;
+ break;
+ }
+ }
+
+ if (!validItemFound) {
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[
+ i
+ ];
+ if (
+ item.itemType == ItemType.ERC20 ||
+ item.itemType == ItemType.NATIVE
+ ) {
+ item.identifierOrCriteria = 1;
+ validItemFound = true;
+ break;
+ }
+ }
+ }
+
+ _signOrValidateMutatedOrder(context, orderIndex);
+
+ exec(context);
+ }
+
+ function mutation_invalidERC721TransferAmount(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ // This mutation works by setting the amount of the first eligible
+ // item in an offer to an invalid value. An item with ItemType.ERC721
+ // or ItemType.ERC721_WITH_CRITERIA must have an amount of 1.
+
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // Add invalid amount to first valid item
+ bool validItemFound;
+ for (uint256 i; i < order.parameters.offer.length; i++) {
+ OfferItem memory item = order.parameters.offer[i];
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ item.startAmount = 2;
+ item.endAmount = 2;
+ validItemFound = true;
+ break;
+ }
+ }
+
+ if (!validItemFound) {
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ ConsiderationItem memory item = order.parameters.consideration[
+ i
+ ];
+ if (
+ item.itemType == ItemType.ERC721 ||
+ item.itemType == ItemType.ERC721_WITH_CRITERIA
+ ) {
+ item.startAmount = 2;
+ item.endAmount = 2;
+ validItemFound = true;
+ break;
+ }
+ }
+ }
+
+ _signOrValidateMutatedOrder(context, orderIndex);
+
+ exec(context);
+ }
+
+ function mutation_considerationNotMet(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation works by adding a new consideration item to the order.
+ // An attempt to fill an order must include all the consideration items
+ // that the order specifies. Since the test framework supplies exactly
+ // the right number of native tokens to fill the order (before this
+ // mutation), adding a new consideration item will cause the order to
+ // fail.
+
+ ConsiderationItem[] memory newConsideration = new ConsiderationItem[](
+ order.parameters.consideration.length + 1
+ );
+ for (uint256 i; i < order.parameters.consideration.length; i++) {
+ newConsideration[i] = order.parameters.consideration[i];
+ }
+ newConsideration[
+ order.parameters.consideration.length
+ ] = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.NATIVE)
+ .withAmount(100);
+ order.parameters.consideration = newConsideration;
+
+ exec(context);
+ }
+
+ function mutation_inexactFraction(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation works by setting the demoninator of the fraction to a
+ // value that does not nicely divide. Remainders are not permissible.
+
+ // Get an item's start amount.
+ uint256 itemAmount = order.parameters.offer.length == 0
+ ? order.parameters.consideration[0].startAmount
+ : order.parameters.offer[0].startAmount;
+
+ // If the item's start amount is 0, get an item's end amount.
+ if (itemAmount == 0) {
+ itemAmount = order.parameters.offer.length == 0
+ ? order.parameters.consideration[0].endAmount
+ : order.parameters.offer[0].endAmount;
+ }
+
+ // If the item amount is huge, set it to a value that's very large but
+ // less than type(uint120).max.
+ // This isn't perfect, but odds of hitting it are slim to none.
+ // type(uint120).max is 1329227995784915872903807060280344575.
+ // The hardcoded value below is just above type(uint120).max / 2.
+ // 664613997892457936451903530140172392 - (type(uint120).max / 2) = 0x69
+ if (itemAmount > type(uint120).max - 1) {
+ itemAmount = 664613997892457936451903530140172392;
+ }
+
+ // Set the order's numerator to 1 and denominator to the item amount
+ // plus 1. This will result in a division that produces a remainder.
+ order.numerator = 1;
+ order.denominator = uint120(itemAmount) + 1;
+
+ exec(context);
+ }
+
+ function mutation_partialFillOverflow(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation works by setting the demoninator of the order to a
+ // value and the on chain denominator to values that, when summed
+ // together, trigger a panic. The EVM doesn't like overflows.
+
+ // (664613997892457936451903530140172393 +
+ // 664613997892457936451903530140172297) > type(uint120).max
+
+ order.numerator = 1;
+ order.denominator = 664613997892457936451903530140172393;
+
+ order.inscribeOrderStatusNumeratorAndDenominator(
+ 1,
+ 664613997892457936451903530140172297,
+ context.seaport
+ );
+
+ exec(context);
+ }
+
+ function mutation_partialFillsNotEnabledForOrder(
+ FuzzTestContext memory context,
+ MutationState memory mutationState
+ ) external {
+ uint256 orderIndex = mutationState.selectedOrderIndex;
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // This mutation works by mutating the order to ask for a partial fill
+ // on functions that don't support partial fills. Seaport will reject a
+ // denominator that is not 1 for functions that don't support partial
+ // fills.
+
+ order.numerator = 1;
+ order.denominator = 10;
+
+ exec(context);
+ }
+
+ function mutation_noSpecifiedOrdersAvailable(
+ FuzzTestContext memory context,
+ MutationState memory /* mutationState */
+ ) external {
+ // This mutation works by wiping out all the orders. Seaport reverts if
+ // `_executeAvailableFulfillments` finishes its loop and produces no
+ // executions.
+
+ for (uint256 i; i < context.executionState.orders.length; i++) {
+ AdvancedOrder memory order = context.executionState.orders[i];
+ order.parameters.consideration = new ConsiderationItem[](0);
+ order.parameters.totalOriginalConsiderationItems = 0;
+
+ _signOrValidateMutatedOrder(context, i);
+ }
+ context.executionState.offerFulfillments = new FulfillmentComponent[][](
+ 0
+ );
+ context
+ .executionState
+ .considerationFulfillments = new FulfillmentComponent[][](0);
+
+ exec(context);
+ }
+
+ /**
+ * @dev Helper function to sign or validate a mutated order, depending on
+ * which is necessary.
+ *
+ * @param context The fuzz test context.
+ * @param orderIndex The index of the order to sign or validate.
+ */
+ function _signOrValidateMutatedOrder(
+ FuzzTestContext memory context,
+ uint256 orderIndex
+ ) private {
+ AdvancedOrder memory order = context.executionState.orders[orderIndex];
+
+ // If an order has been validated, then the mutated order should be
+ // validated too so that we're conforming the failure paths as closely
+ // as possible to the success paths.
+ if (
+ context.advancedOrdersSpace.orders[orderIndex].signatureMethod ==
+ SignatureMethod.VALIDATE
+ ) {
+ order.inscribeOrderStatusValidated(true, context.seaport);
+ } else if (context.executionState.caller != order.parameters.offerer) {
+ // It's not necessary to sign an order if the caller is the offerer.
+ // But if the caller is not the offerer, then sign the order using
+ // the function from the order generator.
+ AdvancedOrdersSpaceGenerator._signOrders(
+ context.advancedOrdersSpace,
+ context.executionState.orders,
+ context.generatorContext
+ );
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzSetup.sol b/test/foundry/new/helpers/FuzzSetup.sol
new file mode 100644
index 000000000..b52f51fe5
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzSetup.sol
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import { ExecutionLib, ZoneParametersLib } from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ Execution,
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { OrderDetails } from "seaport-sol/fulfillments/lib/Structs.sol";
+
+import { ItemType, OrderType } from "seaport-sol/SeaportEnums.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+import { CriteriaResolverHelper } from "./CriteriaResolverHelper.sol";
+
+import {
+ AmountDeriverHelper
+} from "seaport-sol/lib/fulfillment/AmountDeriverHelper.sol";
+
+import { ExpectedEventsUtil } from "./event-utils/ExpectedEventsUtil.sol";
+
+import { ExecutionsFlattener } from "./event-utils/ExecutionsFlattener.sol";
+
+import { ExpectedBalances } from "./ExpectedBalances.sol";
+
+import { dumpExecutions } from "./DebugUtil.sol";
+
+import { FuzzChecks } from "./FuzzChecks.sol";
+
+import { FuzzEngineLib } from "./FuzzEngineLib.sol";
+
+import { FuzzHelpers } from "./FuzzHelpers.sol";
+
+import { FuzzTestContext } from "./FuzzTestContextLib.sol";
+
+interface TestERC20 {
+ function mint(address to, uint256 amount) external;
+
+ function increaseAllowance(address spender, uint256 amount) external;
+}
+
+interface TestERC721 {
+ function mint(address to, uint256 tokenId) external;
+
+ function setApprovalForAll(address operator, bool approved) external;
+}
+
+interface TestERC1155 {
+ function mint(address to, uint256 tokenId, uint256 amount) external;
+
+ function setApprovalForAll(address operator, bool approved) external;
+}
+
+/**
+ * @dev Stateless helpers related to fuzz checks.
+ */
+library CheckHelpers {
+ /**
+ * @dev Register a check to be run after the test is executed.
+ *
+ * @param context The test context.
+ * @param check The check to register.
+ *
+ * @return The updated test context.
+ */
+ function registerCheck(
+ FuzzTestContext memory context,
+ bytes4 check
+ ) internal pure returns (FuzzTestContext memory) {
+ bytes4[] memory checks = context.checks;
+ bytes4[] memory newChecks = new bytes4[](checks.length + 1);
+ for (uint256 i; i < checks.length; ++i) {
+ newChecks[i] = checks[i];
+ }
+ newChecks[checks.length] = check;
+ context.checks = newChecks;
+ return context;
+ }
+
+ /**
+ * @dev Get the address to approve to for a given test context.
+ *
+ * @param context The test context.
+ */
+ function getApproveTo(
+ FuzzTestContext memory context
+ ) internal view returns (address) {
+ if (context.executionState.fulfillerConduitKey == bytes32(0)) {
+ return address(context.seaport);
+ } else {
+ (address conduit, bool exists) = context
+ .conduitController
+ .getConduit(context.executionState.fulfillerConduitKey);
+ if (exists) {
+ return conduit;
+ } else {
+ revert("CheckHelpers: Conduit not found");
+ }
+ }
+ }
+
+ /**
+ * @dev Get the address to approve to for a given test context and order.
+ *
+ * @param context The test context.
+ * @param orderParams The order parameters.
+ */
+ function getApproveTo(
+ FuzzTestContext memory context,
+ OrderParameters memory orderParams
+ ) internal view returns (address) {
+ if (orderParams.conduitKey == bytes32(0)) {
+ return address(context.seaport);
+ } else {
+ (address conduit, bool exists) = context
+ .conduitController
+ .getConduit(orderParams.conduitKey);
+ if (exists) {
+ return conduit;
+ } else {
+ revert("CheckHelpers: Conduit not found");
+ }
+ }
+ }
+
+ /**
+ * @dev Get the address to approve to for a given test context and order.
+ *
+ * @param context The test context.
+ * @param orderDetails The order details.
+ */
+ function getApproveTo(
+ FuzzTestContext memory context,
+ OrderDetails memory orderDetails
+ ) internal view returns (address) {
+ if (orderDetails.conduitKey == bytes32(0)) {
+ return address(context.seaport);
+ } else {
+ (address conduit, bool exists) = context
+ .conduitController
+ .getConduit(orderDetails.conduitKey);
+ if (exists) {
+ return conduit;
+ } else {
+ revert("CheckHelpers: Conduit not found");
+ }
+ }
+ }
+}
+
+/**
+ * @dev Setup functions perform the stateful setup steps necessary to run a
+ * FuzzEngine test, like minting test tokens and setting approvals.
+ * Currently, we also register checks in the setup step, but we might
+ * want to move this to a separate step. Setup happens after generation,
+ * amendment, and derivation, but before execution.
+ */
+abstract contract FuzzSetup is Test, AmountDeriverHelper {
+ using CheckHelpers for FuzzTestContext;
+ using FuzzEngineLib for FuzzTestContext;
+ using FuzzHelpers for FuzzTestContext;
+
+ using FuzzHelpers for AdvancedOrder[];
+ using ZoneParametersLib for AdvancedOrder[];
+
+ using ExecutionLib for Execution;
+
+ using ExpectedEventsUtil for FuzzTestContext;
+
+ /**
+ * @dev Set up the zone params on a test context.
+ *
+ * @param context The test context.
+ */
+ function setUpZoneParameters(FuzzTestContext memory context) public view {
+ UnavailableReason[] memory unavailableReasons = new UnavailableReason[](
+ context.advancedOrdersSpace.orders.length
+ );
+
+ for (uint256 i; i < context.executionState.orderDetails.length; ++i) {
+ unavailableReasons[i] = context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason;
+ }
+
+ // Get the expected zone calldata hashes for each order.
+ bytes32[] memory calldataHashes = context
+ .executionState
+ .orders
+ .getExpectedZoneCalldataHash(
+ address(context.seaport),
+ context.executionState.caller,
+ context.executionState.criteriaResolvers,
+ context.executionState.maximumFulfilled,
+ unavailableReasons
+ );
+
+ // Provision the expected zone calldata hash array.
+ bytes32[] memory expectedZoneCalldataHash = new bytes32[](
+ context.executionState.orders.length
+ );
+
+ bool registerChecks;
+
+ // Iterate over the orders and for each restricted order, set up the
+ // expected zone calldata hash. If any of the orders is restricted,
+ // flip the flag to register the hash validation check.
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ OrderParameters memory order = context
+ .executionState
+ .orders[i]
+ .parameters;
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE &&
+ (order.orderType == OrderType.FULL_RESTRICTED ||
+ order.orderType == OrderType.PARTIAL_RESTRICTED)
+ ) {
+ registerChecks = true;
+ expectedZoneCalldataHash[i] = calldataHashes[i];
+ }
+ }
+
+ context
+ .expectations
+ .expectedZoneCalldataHash = expectedZoneCalldataHash;
+
+ if (registerChecks) {
+ context.registerCheck(
+ FuzzChecks.check_validateOrderExpectedDataHash.selector
+ );
+ }
+ }
+
+ function setUpContractOfferers(FuzzTestContext memory context) public pure {
+ bytes32[2][] memory contractOrderCalldataHashes = context
+ .getExpectedContractOffererCalldataHashes();
+
+ bytes32[2][]
+ memory expectedContractOrderCalldataHashes = new bytes32[2][](
+ context.executionState.orders.length
+ );
+
+ bool registerChecks;
+
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ OrderParameters memory order = context
+ .executionState
+ .orders[i]
+ .parameters;
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE &&
+ order.orderType == OrderType.CONTRACT
+ ) {
+ registerChecks = true;
+ expectedContractOrderCalldataHashes[i][
+ 0
+ ] = contractOrderCalldataHashes[i][0];
+ expectedContractOrderCalldataHashes[i][
+ 1
+ ] = contractOrderCalldataHashes[i][1];
+ }
+ }
+
+ context
+ .expectations
+ .expectedContractOrderCalldataHashes = expectedContractOrderCalldataHashes;
+
+ if (registerChecks) {
+ context.registerCheck(
+ FuzzChecks.check_contractOrderExpectedDataHashes.selector
+ );
+ }
+ }
+
+ /**
+ * @dev Set up the offer items on a test context. Mints test tokens and
+ * sets necessary approvals.
+ *
+ * @param context The test context.
+ */
+ function setUpOfferItems(FuzzTestContext memory context) public {
+ bool isMatchable = context.action() ==
+ context.seaport.matchAdvancedOrders.selector ||
+ context.action() == context.seaport.matchOrders.selector;
+
+ // Iterate over orders and mint/approve as necessary.
+ for (uint256 i; i < context.executionState.orderDetails.length; ++i) {
+ OrderDetails memory order = context.executionState.orderDetails[i];
+
+ if (order.unavailableReason != UnavailableReason.AVAILABLE) {
+ continue;
+ }
+
+ SpentItem[] memory items = order.offer;
+ address offerer = order.offerer;
+ address approveTo = context.getApproveTo(order);
+ for (uint256 j = 0; j < items.length; j++) {
+ SpentItem memory item = items[j];
+
+ if (item.itemType == ItemType.NATIVE) {
+ if (
+ context.executionState.orders[i].parameters.orderType ==
+ OrderType.CONTRACT
+ ) {
+ vm.deal(offerer, offerer.balance + item.amount);
+ } else if (isMatchable) {
+ vm.deal(
+ context.executionState.caller,
+ context.executionState.caller.balance + item.amount
+ );
+ }
+ }
+
+ if (item.itemType == ItemType.ERC20) {
+ TestERC20(item.token).mint(offerer, item.amount);
+ vm.prank(offerer);
+ TestERC20(item.token).increaseAllowance(
+ approveTo,
+ item.amount
+ );
+ }
+
+ if (item.itemType == ItemType.ERC721) {
+ TestERC721(item.token).mint(offerer, item.identifier);
+ vm.prank(offerer);
+ TestERC721(item.token).setApprovalForAll(approveTo, true);
+ }
+
+ if (item.itemType == ItemType.ERC1155) {
+ TestERC1155(item.token).mint(
+ offerer,
+ item.identifier,
+ item.amount
+ );
+ vm.prank(offerer);
+ TestERC1155(item.token).setApprovalForAll(approveTo, true);
+ }
+ }
+ }
+ }
+
+ /**
+ * @dev Set up the consideration items on a test context. Mints test tokens
+ * and sets necessary approvals.
+ *
+ * @param context The test context.
+ */
+ function setUpConsiderationItems(FuzzTestContext memory context) public {
+ // Skip creating consideration items if we're calling a match function
+ if (
+ context.action() == context.seaport.matchAdvancedOrders.selector ||
+ context.action() == context.seaport.matchOrders.selector
+ ) return;
+
+ // In all cases, deal balance to caller if consideration item is native
+ for (uint256 i; i < context.executionState.orderDetails.length; ++i) {
+ OrderDetails memory order = context.executionState.orderDetails[i];
+ ReceivedItem[] memory items = order.consideration;
+
+ for (uint256 j = 0; j < items.length; j++) {
+ if (items[j].itemType == ItemType.NATIVE) {
+ vm.deal(
+ context.executionState.caller,
+ context.executionState.caller.balance + items[j].amount
+ );
+ }
+ }
+ }
+
+ // Special handling for basic orders that are bids; only first item
+ // needs to be approved
+ if (
+ (context.action() == context.seaport.fulfillBasicOrder.selector ||
+ context.action() ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector) &&
+ context.executionState.orders[0].parameters.offer[0].itemType ==
+ ItemType.ERC20
+ ) {
+ ConsiderationItem memory item = context
+ .executionState
+ .orders[0]
+ .parameters
+ .consideration[0];
+
+ address approveTo = context.getApproveTo();
+
+ if (item.itemType == ItemType.ERC721) {
+ TestERC721(item.token).mint(
+ context.executionState.caller,
+ item.identifierOrCriteria
+ );
+ vm.prank(context.executionState.caller);
+ TestERC721(item.token).setApprovalForAll(approveTo, true);
+ } else {
+ TestERC1155(item.token).mint(
+ context.executionState.caller,
+ item.identifierOrCriteria,
+ item.startAmount
+ );
+ vm.prank(context.executionState.caller);
+ TestERC1155(item.token).setApprovalForAll(approveTo, true);
+ }
+
+ return;
+ }
+
+ // Iterate over orders and mint/approve as necessary.
+ for (uint256 i; i < context.executionState.orderDetails.length; ++i) {
+ OrderDetails memory order = context.executionState.orderDetails[i];
+ ReceivedItem[] memory items = order.consideration;
+
+ if (order.unavailableReason != UnavailableReason.AVAILABLE) {
+ continue;
+ }
+
+ address owner = context.executionState.caller;
+ address approveTo = context.getApproveTo();
+
+ for (uint256 j = 0; j < items.length; j++) {
+ ReceivedItem memory item = items[j];
+
+ if (item.itemType == ItemType.ERC20) {
+ TestERC20(item.token).mint(owner, item.amount);
+ vm.prank(owner);
+ TestERC20(item.token).increaseAllowance(
+ approveTo,
+ item.amount
+ );
+ }
+
+ if (item.itemType == ItemType.ERC721) {
+ bool shouldMint = true;
+ if (
+ context.executionState.caller ==
+ context.executionState.recipient ||
+ context.executionState.recipient == address(0)
+ ) {
+ for (
+ uint256 k;
+ k < context.executionState.orderDetails.length;
+ ++k
+ ) {
+ if (
+ context
+ .executionState
+ .orderDetails[k]
+ .unavailableReason !=
+ UnavailableReason.AVAILABLE
+ ) {
+ continue;
+ }
+
+ SpentItem[] memory spentItems = context
+ .executionState
+ .orderDetails[k]
+ .offer;
+ for (uint256 l; l < spentItems.length; ++l) {
+ if (
+ spentItems[l].itemType == ItemType.ERC721 &&
+ spentItems[l].token == item.token &&
+ spentItems[l].identifier == item.identifier
+ ) {
+ shouldMint = false;
+ break;
+ }
+ }
+ if (!shouldMint) break;
+ }
+ }
+ if (shouldMint) {
+ TestERC721(item.token).mint(owner, item.identifier);
+ }
+ vm.prank(owner);
+ TestERC721(item.token).setApprovalForAll(approveTo, true);
+ }
+
+ if (item.itemType == ItemType.ERC1155) {
+ TestERC1155(item.token).mint(
+ owner,
+ item.identifier,
+ item.amount
+ );
+ vm.prank(owner);
+ TestERC1155(item.token).setApprovalForAll(approveTo, true);
+ }
+ }
+ }
+ }
+
+ function registerExpectedEventsAndBalances(
+ FuzzTestContext memory context
+ ) public {
+ ExecutionsFlattener.flattenExecutions(context);
+ context.registerCheck(FuzzChecks.check_expectedBalances.selector);
+ ExpectedBalances balanceChecker = context.testHelpers.balanceChecker();
+
+ Execution[] memory _executions = context
+ .expectations
+ .allExpectedExecutions;
+ Execution[] memory executions = _executions;
+
+ try balanceChecker.addTransfers(executions) {} catch (
+ bytes memory reason
+ ) {
+ context.expectations.allExpectedExecutions = executions;
+ dumpExecutions(context);
+ assembly {
+ revert(add(reason, 32), mload(reason))
+ }
+ }
+ context.registerCheck(FuzzChecks.check_executions.selector);
+ context.setExpectedTransferEventHashes();
+ context.registerCheck(
+ FuzzChecks.check_expectedTransferEventsEmitted.selector
+ );
+ ExpectedEventsUtil.startRecordingLogs();
+ }
+
+ /**
+ * @dev Set up the checks that will always be run. Note that this must be
+ * run after registerExpectedEventsAndBalances at the moment.
+ *
+ * @param context The test context.
+ */
+ function registerCommonChecks(FuzzTestContext memory context) public {
+ context.setExpectedSeaportEventHashes();
+ context.registerCheck(
+ FuzzChecks.check_expectedSeaportEventsEmitted.selector
+ );
+ context.registerCheck(FuzzChecks.check_orderStatusFullyFilled.selector);
+ }
+
+ /**
+ * @dev Set up the function-specific checks.
+ *
+ * @param context The test context.
+ */
+ function registerFunctionSpecificChecks(
+ FuzzTestContext memory context
+ ) public view {
+ bytes4 _action = context.action();
+ if (_action == context.seaport.fulfillOrder.selector) {
+ context.registerCheck(FuzzChecks.check_orderFulfilled.selector);
+ } else if (_action == context.seaport.fulfillAdvancedOrder.selector) {
+ context.registerCheck(FuzzChecks.check_orderFulfilled.selector);
+ } else if (_action == context.seaport.fulfillBasicOrder.selector) {
+ context.registerCheck(FuzzChecks.check_orderFulfilled.selector);
+ } else if (
+ _action ==
+ context.seaport.fulfillBasicOrder_efficient_6GL6yc.selector
+ ) {
+ context.registerCheck(FuzzChecks.check_orderFulfilled.selector);
+ } else if (_action == context.seaport.fulfillAvailableOrders.selector) {
+ context.registerCheck(FuzzChecks.check_allOrdersFilled.selector);
+ } else if (
+ _action == context.seaport.fulfillAvailableAdvancedOrders.selector
+ ) {
+ context.registerCheck(FuzzChecks.check_allOrdersFilled.selector);
+ } else if (_action == context.seaport.matchOrders.selector) {
+ // Add match-specific checks
+ } else if (_action == context.seaport.matchAdvancedOrders.selector) {
+ // Add match-specific checks
+ } else if (_action == context.seaport.cancel.selector) {
+ context.registerCheck(FuzzChecks.check_orderCancelled.selector);
+ } else if (_action == context.seaport.validate.selector) {
+ context.registerCheck(FuzzChecks.check_orderValidated.selector);
+ } else {
+ revert("FuzzEngine: Action not implemented");
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/FuzzTestContextLib.sol b/test/foundry/new/helpers/FuzzTestContextLib.sol
new file mode 100644
index 000000000..d42f98a6e
--- /dev/null
+++ b/test/foundry/new/helpers/FuzzTestContextLib.sol
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Vm } from "forge-std/Vm.sol";
+
+import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
+
+import {
+ AdvancedOrderLib,
+ BasicOrderParametersLib,
+ MatchComponent
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdvancedOrder,
+ BasicOrderParameters,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ FulfillmentComponent,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType, OrderType, Side } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ BroadOrderType,
+ OrderStatusEnum,
+ SignatureMethod,
+ UnavailableReason
+} from "seaport-sol/SpaceEnums.sol";
+
+import { AdvancedOrdersSpace } from "seaport-sol/StructSpace.sol";
+
+import { OrderDetails } from "seaport-sol/fulfillments/lib/Structs.sol";
+
+import {
+ AmountDeriverHelper
+} from "seaport-sol/lib/fulfillment/AmountDeriverHelper.sol";
+
+import {
+ ConduitControllerInterface
+} from "seaport-sol/ConduitControllerInterface.sol";
+
+import { SeaportInterface } from "seaport-sol/SeaportInterface.sol";
+
+import { Account } from "../BaseOrderTest.sol";
+
+import { Result } from "./FuzzHelpers.sol";
+
+import { ExpectedBalances } from "./ExpectedBalances.sol";
+
+import { CriteriaResolverHelper } from "./CriteriaResolverHelper.sol";
+
+import {
+ FuzzGeneratorContext,
+ FuzzGeneratorContextLib
+} from "./FuzzGeneratorContextLib.sol";
+
+import { TestStateGenerator } from "./FuzzGenerators.sol";
+
+import { Failure } from "./FuzzMutationSelectorLib.sol";
+
+import { FractionResults } from "./FractionUtil.sol";
+
+import {
+ ErrorsAndWarnings,
+ SeaportValidatorInterface
+} from "../../../../contracts/helpers/order-validator/SeaportValidator.sol";
+
+interface TestHelpers {
+ function balanceChecker() external view returns (ExpectedBalances);
+
+ function amountDeriverHelper() external view returns (AmountDeriverHelper);
+
+ function criteriaResolverHelper()
+ external
+ view
+ returns (CriteriaResolverHelper);
+
+ function makeAccount(
+ string memory name
+ ) external view returns (Account memory);
+
+ function getNaiveFulfillmentComponents(
+ OrderDetails[] memory orderDetails
+ )
+ external
+ returns (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ );
+
+ function getMatchedFulfillments(
+ AdvancedOrder[] memory orders,
+ CriteriaResolver[] memory resolvers
+ )
+ external
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents,
+ MatchComponent[] memory remainingConsiderationComponents
+ );
+
+ function getMatchedFulfillments(
+ OrderDetails[] memory orders
+ )
+ external
+ returns (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory remainingOfferComponents,
+ MatchComponent[] memory remainingConsiderationComponents
+ );
+
+ function allocateTokensAndApprovals(address _to, uint128 _amount) external;
+}
+
+struct FuzzParams {
+ uint256 seed;
+ uint256 totalOrders;
+ uint256 maxOfferItems;
+ uint256 maxConsiderationItems;
+ bytes seedInput;
+}
+
+struct ReturnValues {
+ bool fulfilled;
+ bool cancelled;
+ bool validated;
+ bool[] availableOrders;
+ Execution[] executions;
+}
+
+/**
+ * @dev Context data related to post-execution expectations.
+ */
+struct Expectations {
+ /**
+ * @dev Expected zone calldata hashes.
+ */
+ bytes32[] expectedZoneCalldataHash;
+ /**
+ * @dev Expected contract order calldata hashes. Index 0 of the outer array
+ * corresponds to the generateOrder hash, while index 1 corresponds to
+ * the ratifyOrder hash.
+ */
+ bytes32[2][] expectedContractOrderCalldataHashes;
+ /**
+ * @dev Expected Result state for each order. Indexes correspond to the
+ * indexes of the orders in the orders array.
+ */
+ Result[] expectedResults;
+ /**
+ * @dev Expected executions. Implicit means it doesn't correspond directly
+ * with a fulfillment that was passed in.
+ */
+ Execution[] expectedImplicitPreExecutions;
+ Execution[] expectedImplicitPostExecutions;
+ Execution[] expectedExplicitExecutions;
+ Execution[] allExpectedExecutions;
+ // /**
+ // * @dev Whether an order is available and will be fulfilled. Indexes
+ // * correspond to order indexes in the orders array.
+ // */
+ // bool[] expectedAvailableOrders;
+ /**
+ * @dev Expected event hashes. Encompasses all events that match watched
+ * topic0s.
+ */
+ bytes32[] expectedTransferEventHashes;
+ /**
+ * @dev Expected event hashes. Encompasses all events that match watched
+ * topic0s.
+ */
+ bytes32[] expectedSeaportEventHashes;
+ bool[] ineligibleOrders;
+ bool[] ineligibleFailures;
+ /**
+ * @dev Number of expected implicit native executions.
+ */
+ uint256 expectedImpliedNativeExecutions;
+ /**
+ * @dev Amount of native tokens we expect to be returned to the caller.
+ */
+ uint256 expectedNativeTokensReturned;
+ /**
+ * @dev Minimum msg.value that must be provided by caller.
+ */
+ uint256 minimumValue;
+ FractionResults[] expectedFillFractions;
+}
+
+/**
+ * @dev Context data related to test execution
+ */
+struct ExecutionState {
+ /**
+ * @dev A caller address. If this is nonzero, the FuzzEngine will prank this
+ * address before calling exec.
+ */
+ address caller;
+ uint256 contractOffererNonce;
+ /**
+ * @dev A recipient address to be passed into fulfillAdvancedOrder,
+ * fulfillAvailableAdvancedOrders, or matchAdvancedOrders. Speciying a
+ * recipient on the fulfill functions will set that address as the
+ * recipient for all received items. Specifying a recipient on the
+ * match function will set that address as the recipient for all
+ * unspent offer item amounts.
+ */
+ address recipient;
+ /**
+ * @dev A counter that can be incremented to cancel all orders made with
+ * the same counter value.
+ */
+ uint256 counter;
+ /**
+ * @dev Indicates what conduit, if any, to check for token approvals. A zero
+ * value means no conduit, look to seaport itself.
+ */
+ bytes32 fulfillerConduitKey;
+ /**
+ * @dev A struct containing basic order parameters that are used in the
+ * fulfillBasic functions.
+ */
+ BasicOrderParameters basicOrderParameters;
+ /**
+ * @dev An array of AdvancedOrders
+ */
+ AdvancedOrder[] orders;
+ OrderDetails[] orderDetails;
+ /**
+ * @dev A copy of the original orders array. Modify this when calling
+ * previewOrder on contract orders and use it to derive order
+ * details (which is used to derive fulfillments and executions).
+ */
+ AdvancedOrder[] previewedOrders;
+ /**
+ * @dev An array of CriteriaResolvers. These allow specification of an
+ * order, offer or consideration, an identifier, and a proof. They
+ * enable trait offer and collection offers, etc.
+ */
+ CriteriaResolver[] criteriaResolvers;
+ /**
+ * @dev An array of Fulfillments. These are used in the match functions to
+ * point offers and considerations to one another.
+ */
+ Fulfillment[] fulfillments;
+ /**
+ * @dev offer components not explicitly supplied in match fulfillments.
+ */
+ FulfillmentComponent[] remainingOfferComponents;
+ bool hasRemainders;
+ /**
+ * @dev An array of FulfillmentComponents. These are used in the
+ * fulfillAvailable functions to set up aggregations.
+ */
+ FulfillmentComponent[][] offerFulfillments;
+ FulfillmentComponent[][] considerationFulfillments;
+ /**
+ * @dev The maximum number of fulfillments to attempt in the
+ * fulfillAvailable functions.
+ */
+ uint256 maximumFulfilled;
+ /**
+ * @dev Status of each order before execution.
+ */
+ OrderStatusEnum[] preExecOrderStatuses;
+ uint256 value;
+ /**
+ * @dev ErrorsAndWarnings returned from SeaportValidator.
+ */
+ ErrorsAndWarnings[] validationErrors;
+}
+
+/**
+ * @dev Context data related to failure mutations.
+ */
+struct MutationState {
+ /**
+ * @dev Copy of the order selected for mutation.
+ */
+ AdvancedOrder selectedOrder;
+ /**
+ * @dev Index of the selected order in the orders array.
+ */
+ uint256 selectedOrderIndex;
+ bytes32 selectedOrderHash;
+ Side side;
+ CriteriaResolver selectedCriteriaResolver;
+ uint256 selectedCriteriaResolverIndex;
+ address selectedArbitraryAddress;
+}
+
+struct FuzzTestContext {
+ /**
+ * @dev Cached selector of the chosen Seaport action.
+ */
+ bytes4 _action;
+ /**
+ * @dev Whether a Seaport action has been selected. This boolean is used as
+ * a workaround to detect when the cached action is set, since the
+ * empty selector is a valid Seaport action (fulfill basic efficient).
+ */
+ bool actionSelected;
+ /**
+ * @dev A Seaport interface, either the reference or optimized version.
+ */
+ SeaportInterface seaport;
+ /**
+ * @dev A ConduitController interface.
+ */
+ ConduitControllerInterface conduitController;
+ /**
+ * @dev A SeaportValidator interface.
+ */
+ SeaportValidatorInterface seaportValidator;
+ /**
+ * @dev A TestHelpers interface. These helper functions are used to generate
+ * accounts and fulfillments.
+ */
+ TestHelpers testHelpers;
+ /**
+ * @dev A struct containing fuzzed params generated by the Foundry fuzzer.
+ */
+ FuzzParams fuzzParams;
+ /**
+ * @dev A struct containing the state for the execution phase.
+ */
+ ExecutionState executionState;
+ /**
+ * @dev Return values from the last call to exec. Superset of return values
+ * from all Seaport functions.
+ */
+ ReturnValues returnValues;
+ /**
+ * @dev Actual events emitted.
+ */
+ Vm.Log[] actualEvents;
+ /**
+ * @dev A struct containing expectations for the test. These are used to
+ * make assertions about the resulting test state.
+ */
+ Expectations expectations;
+ /**
+ * @dev An array of function selectors for "checks". The FuzzEngine will
+ * call these functions after calling exec to make assertions about
+ * the resulting test state.
+ */
+ bytes4[] checks;
+ /**
+ * @dev A struct containing the context for the FuzzGenerator. This is used
+ * upstream to generate the order state and is included here for use
+ * and reference throughout the rest of the lifecycle.
+ */
+ FuzzGeneratorContext generatorContext;
+ /**
+ * @dev The AdvancedOrdersSpace used to generate the orders. A nested struct
+ * of enums defining the selected permutation of orders.
+ */
+ AdvancedOrdersSpace advancedOrdersSpace;
+}
+
+/**
+ * @notice Builder library for FuzzTestContext.
+ */
+library FuzzTestContextLib {
+ using AdvancedOrderLib for AdvancedOrder;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using BasicOrderParametersLib for BasicOrderParameters;
+ using FuzzTestContextLib for FuzzTestContext;
+ using LibPRNG for LibPRNG.PRNG;
+ using FuzzGeneratorContextLib for FuzzGeneratorContext;
+
+ /**
+ * @dev Create an empty FuzzTestContext.
+ *
+ * @custom:return emptyContext the empty FuzzTestContext
+ */
+ function empty() internal returns (FuzzTestContext memory) {
+ AdvancedOrder[] memory orders;
+ CriteriaResolver[] memory resolvers;
+ Fulfillment[] memory fulfillments;
+ FulfillmentComponent[] memory components;
+ FulfillmentComponent[][] memory componentsArray;
+ Result[] memory results;
+ bool[] memory available;
+ Execution[] memory executions;
+ bytes32[] memory hashes;
+ bytes32[] memory expectedTransferEventHashes;
+ bytes32[] memory expectedSeaportEventHashes;
+ Vm.Log[] memory actualEvents;
+
+ return
+ FuzzTestContext({
+ _action: bytes4(0),
+ actionSelected: false,
+ seaport: SeaportInterface(address(0)),
+ conduitController: ConduitControllerInterface(address(0)),
+ seaportValidator: SeaportValidatorInterface(address(0)),
+ fuzzParams: FuzzParams({
+ seed: 0,
+ totalOrders: 0,
+ maxOfferItems: 0,
+ maxConsiderationItems: 0,
+ seedInput: ""
+ }),
+ checks: new bytes4[](0),
+ returnValues: ReturnValues({
+ fulfilled: false,
+ cancelled: false,
+ validated: false,
+ availableOrders: available,
+ executions: executions
+ }),
+ expectations: Expectations({
+ expectedZoneCalldataHash: hashes,
+ expectedContractOrderCalldataHashes: new bytes32[2][](0),
+ expectedImplicitPreExecutions: new Execution[](0),
+ expectedImplicitPostExecutions: new Execution[](0),
+ expectedExplicitExecutions: new Execution[](0),
+ allExpectedExecutions: new Execution[](0),
+ expectedResults: results,
+ // expectedAvailableOrders: new bool[](0),
+ expectedTransferEventHashes: expectedTransferEventHashes,
+ expectedSeaportEventHashes: expectedSeaportEventHashes,
+ ineligibleOrders: new bool[](orders.length),
+ ineligibleFailures: new bool[](uint256(Failure.length)),
+ expectedImpliedNativeExecutions: 0,
+ expectedNativeTokensReturned: 0,
+ minimumValue: 0,
+ expectedFillFractions: new FractionResults[](orders.length)
+ }),
+ executionState: ExecutionState({
+ caller: address(0),
+ contractOffererNonce: 0,
+ recipient: address(0),
+ counter: 0,
+ fulfillerConduitKey: bytes32(0),
+ basicOrderParameters: BasicOrderParametersLib.empty(),
+ preExecOrderStatuses: new OrderStatusEnum[](0),
+ previewedOrders: orders,
+ orders: orders,
+ orderDetails: new OrderDetails[](0),
+ criteriaResolvers: resolvers,
+ fulfillments: fulfillments,
+ remainingOfferComponents: components,
+ hasRemainders: false,
+ offerFulfillments: componentsArray,
+ considerationFulfillments: componentsArray,
+ maximumFulfilled: 0,
+ value: 0,
+ validationErrors: new ErrorsAndWarnings[](orders.length)
+ }),
+ actualEvents: actualEvents,
+ testHelpers: TestHelpers(address(this)),
+ generatorContext: FuzzGeneratorContextLib.empty(),
+ advancedOrdersSpace: TestStateGenerator.empty()
+ });
+ }
+
+ /**
+ * @dev Create a FuzzTestContext from the given partial arguments.
+ *
+ * @param orders the AdvancedOrder[] to set
+ * @param seaport the SeaportInterface to set
+ * @param caller the caller address to set
+ * @custom:return _context the FuzzTestContext
+ */
+ function from(
+ AdvancedOrder[] memory orders,
+ SeaportInterface seaport,
+ address caller
+ ) internal returns (FuzzTestContext memory) {
+ return
+ empty()
+ .withOrders(orders)
+ .withSeaport(seaport)
+ .withOrderHashes()
+ .withCaller(caller)
+ .withPreviewedOrders(orders.copy())
+ .withProvisionedIneligbleOrdersArray();
+ }
+
+ /**
+ * @dev Create a FuzzTestContext from the given partial arguments.
+ *
+ * @param orders the AdvancedOrder[] to set
+ * @param seaport the SeaportInterface to set
+ * @custom:return _context the FuzzTestContext
+ */
+ function from(
+ AdvancedOrder[] memory orders,
+ SeaportInterface seaport
+ ) internal returns (FuzzTestContext memory) {
+ return
+ empty()
+ .withOrders(orders)
+ .withSeaport(seaport)
+ .withOrderHashes()
+ .withPreviewedOrders(orders.copy())
+ .withProvisionedIneligbleOrdersArray();
+ }
+
+ /**
+ * @dev Sets the orders on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the orders of
+ * @param orders the AdvancedOrder[] to set
+ *
+ * @return _context the FuzzTestContext with the orders set
+ */
+ function withOrders(
+ FuzzTestContext memory context,
+ AdvancedOrder[] memory orders
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.orders = orders.copy();
+
+ // Bootstrap with all available to ease direct testing.
+ if (context.executionState.orderDetails.length == 0) {
+ context.executionState.orderDetails = new OrderDetails[](
+ orders.length
+ );
+ context.executionState.validationErrors = new ErrorsAndWarnings[](
+ orders.length
+ );
+ for (uint256 i = 0; i < orders.length; ++i) {
+ context
+ .executionState
+ .orderDetails[i]
+ .unavailableReason = UnavailableReason.AVAILABLE;
+ }
+ }
+
+ context.expectations.expectedFillFractions = (
+ new FractionResults[](orders.length)
+ );
+
+ return context;
+ }
+
+ // NOTE: expects context.executionState.orders and context.seaport to
+ // already be set.
+ function withOrderHashes(
+ FuzzTestContext memory context
+ ) internal view returns (FuzzTestContext memory) {
+ bytes32[] memory orderHashes = context
+ .executionState
+ .orders
+ .getOrderHashes(address(context.seaport));
+
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ context.executionState.orderDetails[i].orderHash = orderHashes[i];
+ }
+
+ return context;
+ }
+
+ function withPreviewedOrders(
+ FuzzTestContext memory context,
+ AdvancedOrder[] memory orders
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.previewedOrders = orders.copy();
+ return context;
+ }
+
+ function withProvisionedIneligbleOrdersArray(
+ FuzzTestContext memory context
+ ) internal pure returns (FuzzTestContext memory) {
+ context.expectations.ineligibleOrders = new bool[](
+ context.executionState.orders.length
+ );
+ return context;
+ }
+
+ /**
+ * @dev Sets the SeaportInterface on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the SeaportInterface of
+ * @param seaport the SeaportInterface to set
+ *
+ * @return _context the FuzzTestContext with the SeaportInterface set
+ */
+ function withSeaport(
+ FuzzTestContext memory context,
+ SeaportInterface seaport
+ ) internal pure returns (FuzzTestContext memory) {
+ context.seaport = seaport;
+ return context;
+ }
+
+ /**
+ * @dev Sets the ConduitControllerInterface on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the
+ * ConduitControllerInterface of
+ * @param conduitController the ConduitControllerInterface to set
+ *
+ * @return _context the FuzzTestContext with the ConduitControllerInterface
+ * set
+ */
+ function withConduitController(
+ FuzzTestContext memory context,
+ ConduitControllerInterface conduitController
+ ) internal pure returns (FuzzTestContext memory) {
+ context.conduitController = conduitController;
+ return context;
+ }
+
+ /**
+ * @dev Sets the SeaportValidatorInterface on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the
+ * SeaportValidatorInterface of
+ * @param seaportValidator the SeaportValidatorInterface to set
+ *
+ * @return _context the FuzzTestContext with the SeaportValidatorInterface
+ * set
+ */
+ function withSeaportValidator(
+ FuzzTestContext memory context,
+ SeaportValidatorInterface seaportValidator
+ ) internal pure returns (FuzzTestContext memory) {
+ context.seaportValidator = seaportValidator;
+ return context;
+ }
+
+ /**
+ * @dev Sets the caller on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the caller of
+ * @param caller the caller address to set
+ *
+ * @return _context the FuzzTestContext with the caller set
+ */
+ function withCaller(
+ FuzzTestContext memory context,
+ address caller
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.caller = caller;
+ return context;
+ }
+
+ /**
+ * @dev Sets the fuzzParams on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the fuzzParams of
+ * @param fuzzParams the fuzzParams struct to set
+ *
+ * @return _context the FuzzTestContext with the fuzzParams set
+ */
+ function withFuzzParams(
+ FuzzTestContext memory context,
+ FuzzParams memory fuzzParams
+ ) internal pure returns (FuzzTestContext memory) {
+ context.fuzzParams = _copyFuzzParams(fuzzParams);
+ return context;
+ }
+
+ /**
+ * @dev Sets the checks on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the checks of
+ * @param checks the checks array to set
+ *
+ * @return _context the FuzzTestContext with the checks set
+ */
+ function withChecks(
+ FuzzTestContext memory context,
+ bytes4[] memory checks
+ ) internal pure returns (FuzzTestContext memory) {
+ context.checks = _copyBytes4(checks);
+ return context;
+ }
+
+ /**
+ * @dev Sets the counter on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the counter of
+ * @param counter the counter value to set
+ *
+ * @return _context the FuzzTestContext with the counter set
+ */
+ function withCounter(
+ FuzzTestContext memory context,
+ uint256 counter
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.counter = counter;
+ return context;
+ }
+
+ /**
+ * @dev Sets the counter on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the counter of
+ * @param contractOffererNonce the cocontractOffererNonceunter value to set
+ *
+ * @return _context the FuzzTestContext with the counter set
+ */
+ function withContractOffererNonce(
+ FuzzTestContext memory context,
+ uint256 contractOffererNonce
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.contractOffererNonce = contractOffererNonce;
+ return context;
+ }
+
+ function withGeneratorContext(
+ FuzzTestContext memory context,
+ FuzzGeneratorContext memory generatorContext
+ ) internal pure returns (FuzzTestContext memory) {
+ context.generatorContext = generatorContext;
+ return context;
+ }
+
+ function withSpace(
+ FuzzTestContext memory context,
+ AdvancedOrdersSpace memory space
+ ) internal pure returns (FuzzTestContext memory) {
+ context.advancedOrdersSpace = space;
+ return context;
+ }
+
+ /**
+ * @dev Sets the fulfillerConduitKey on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the fulfillerConduitKey of
+ * @param fulfillerConduitKey the fulfillerConduitKey value to set
+ *
+ * @return _context the FuzzTestContext with the fulfillerConduitKey set
+ */
+ function withFulfillerConduitKey(
+ FuzzTestContext memory context,
+ bytes32 fulfillerConduitKey
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.fulfillerConduitKey = fulfillerConduitKey;
+ return context;
+ }
+
+ /**
+ * @dev Sets the criteriaResolvers on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the criteriaResolvers of
+ * @param criteriaResolvers the criteriaResolvers array to set
+ *
+ * @return _context the FuzzTestContext with the criteriaResolvers set
+ */
+ function withCriteriaResolvers(
+ FuzzTestContext memory context,
+ CriteriaResolver[] memory criteriaResolvers
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.criteriaResolvers = _copyCriteriaResolvers(
+ criteriaResolvers
+ );
+ return context;
+ }
+
+ /**
+ * @dev Sets the recipient on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the recipient of
+ * @param recipient the recipient value to set
+ *
+ * @return _context the FuzzTestContext with the recipient set
+ */
+ function withRecipient(
+ FuzzTestContext memory context,
+ address recipient
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.recipient = recipient;
+ return context;
+ }
+
+ /**
+ * @dev Sets the fulfillments on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the fulfillments of
+ * @param fulfillments the offerFulfillments value to set
+ *
+ * @return _context the FuzzTestContext with the fulfillments set
+ */
+ function withFulfillments(
+ FuzzTestContext memory context,
+ Fulfillment[] memory fulfillments
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.fulfillments = fulfillments;
+ return context;
+ }
+
+ /**
+ * @dev Sets the offerFulfillments on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the offerFulfillments of
+ * @param offerFulfillments the offerFulfillments value to set
+ *
+ * @return _context the FuzzTestContext with the offerFulfillments set
+ */
+ function withOfferFulfillments(
+ FuzzTestContext memory context,
+ FulfillmentComponent[][] memory offerFulfillments
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.offerFulfillments = _copyFulfillmentComponents(
+ offerFulfillments
+ );
+ return context;
+ }
+
+ /**
+ * @dev Sets the considerationFulfillments on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the
+ * considerationFulfillments of
+ * @param considerationFulfillments the considerationFulfillments value to
+ * set
+ *
+ * @return _context the FuzzTestContext with the considerationFulfillments
+ * set
+ */
+ function withConsiderationFulfillments(
+ FuzzTestContext memory context,
+ FulfillmentComponent[][] memory considerationFulfillments
+ ) internal pure returns (FuzzTestContext memory) {
+ context
+ .executionState
+ .considerationFulfillments = _copyFulfillmentComponents(
+ considerationFulfillments
+ );
+ return context;
+ }
+
+ /**
+ * @dev Sets the maximumFulfilled on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the maximumFulfilled of
+ * @param maximumFulfilled the maximumFulfilled value to set
+ *
+ * @return _context the FuzzTestContext with maximumFulfilled set
+ */
+ function withMaximumFulfilled(
+ FuzzTestContext memory context,
+ uint256 maximumFulfilled
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.maximumFulfilled = maximumFulfilled;
+ return context;
+ }
+
+ /**
+ * @dev Sets the basicOrderParameters on a FuzzTestContext
+ *
+ * @param context the FuzzTestContext to set the fulfillments of
+ * @param basicOrderParameters the offerFulfillments value to set
+ *
+ * @return _context the FuzzTestContext with the fulfillments set
+ */
+ function withBasicOrderParameters(
+ FuzzTestContext memory context,
+ BasicOrderParameters memory basicOrderParameters
+ ) internal pure returns (FuzzTestContext memory) {
+ context.executionState.basicOrderParameters = basicOrderParameters;
+ return context;
+ }
+
+ /**
+ * @dev Sets a pseudorandom OrderStatus for each order on a FuzzTestContext.
+ * The preExecOrderStatuses are indexed to orders.
+ *
+ *
+ * @param context the FuzzTestContext to set the preExecOrderStatuses of
+ *
+ * @return _context the FuzzTestContext with the preExecOrderStatuses set
+ */
+ function withPreExecOrderStatuses(
+ FuzzTestContext memory context,
+ AdvancedOrdersSpace memory space
+ ) internal pure returns (FuzzTestContext memory) {
+ LibPRNG.PRNG memory prng = LibPRNG.PRNG(context.fuzzParams.seed);
+
+ context.executionState.preExecOrderStatuses = new OrderStatusEnum[](
+ context.executionState.orders.length
+ );
+
+ for (uint256 i = 0; i < context.executionState.orders.length; i++) {
+ if (space.orders[i].orderType == BroadOrderType.CONTRACT) {
+ if (
+ space.orders[i].unavailableReason ==
+ UnavailableReason.GENERATE_ORDER_FAILURE
+ ) {
+ context.executionState.preExecOrderStatuses[
+ i
+ ] = OrderStatusEnum.REVERT;
+ } else {
+ context.executionState.preExecOrderStatuses[
+ i
+ ] = OrderStatusEnum.AVAILABLE;
+ }
+ } else if (
+ space.orders[i].unavailableReason == UnavailableReason.CANCELLED
+ ) {
+ // TODO: support cases where order is both cancelled and has
+ // been partially fulfilled.
+ context.executionState.preExecOrderStatuses[i] = OrderStatusEnum
+ .CANCELLED_EXPLICIT;
+ } else if (
+ space.orders[i].unavailableReason ==
+ UnavailableReason.ALREADY_FULFILLED
+ ) {
+ context.executionState.preExecOrderStatuses[i] = OrderStatusEnum
+ .FULFILLED;
+ } else if (
+ space.orders[i].signatureMethod == SignatureMethod.VALIDATE
+ ) {
+ // NOTE: this assumes that the order has not been partially
+ // filled (partially filled orders are de-facto validated).
+ context.executionState.preExecOrderStatuses[i] = OrderStatusEnum
+ .VALIDATED;
+ } else {
+ if (
+ space.orders[i].unavailableReason ==
+ UnavailableReason.GENERATE_ORDER_FAILURE
+ ) {
+ revert(
+ "FuzzTestContextLib: bad location for generate order failure"
+ );
+ }
+
+ OrderType orderType = (
+ context.executionState.orders[i].parameters.orderType
+ );
+
+ // TODO: figure out a way to do this for orders with 721 items
+ OrderParameters memory orderParams = (
+ context.executionState.orders[i].parameters
+ );
+
+ bool has721 = false;
+ for (uint256 j = 0; j < orderParams.offer.length; ++j) {
+ if (
+ orderParams.offer[j].itemType == ItemType.ERC721 ||
+ orderParams.offer[j].itemType ==
+ ItemType.ERC721_WITH_CRITERIA
+ ) {
+ has721 = true;
+ break;
+ }
+ }
+
+ if (!has721) {
+ for (
+ uint256 j = 0;
+ j < orderParams.consideration.length;
+ ++j
+ ) {
+ if (
+ orderParams.consideration[j].itemType ==
+ ItemType.ERC721 ||
+ orderParams.consideration[j].itemType ==
+ ItemType.ERC721_WITH_CRITERIA
+ ) {
+ has721 = true;
+ break;
+ }
+ }
+ }
+
+ uint256 upperBound = (!has721 &&
+ (orderType == OrderType.PARTIAL_OPEN ||
+ orderType == OrderType.PARTIAL_RESTRICTED))
+ ? 2
+ : 1;
+
+ context.executionState.preExecOrderStatuses[
+ i
+ ] = OrderStatusEnum(uint8(bound(prng.next(), 0, upperBound)));
+ }
+ }
+
+ return context;
+ }
+
+ function _copyBytes4(
+ bytes4[] memory selectors
+ ) private pure returns (bytes4[] memory) {
+ bytes4[] memory copy = new bytes4[](selectors.length);
+ for (uint256 i = 0; i < selectors.length; i++) {
+ copy[i] = selectors[i];
+ }
+ return copy;
+ }
+
+ function _copyFulfillmentComponents(
+ FulfillmentComponent[][] memory fulfillmentComponents
+ ) private pure returns (FulfillmentComponent[][] memory) {
+ FulfillmentComponent[][]
+ memory outerCopy = new FulfillmentComponent[][](
+ fulfillmentComponents.length
+ );
+ for (uint256 i = 0; i < fulfillmentComponents.length; i++) {
+ FulfillmentComponent[]
+ memory innerCopy = new FulfillmentComponent[](
+ fulfillmentComponents[i].length
+ );
+ for (uint256 j = 0; j < fulfillmentComponents[i].length; j++) {
+ innerCopy[j] = fulfillmentComponents[i][j];
+ }
+ outerCopy[i] = innerCopy;
+ }
+ return outerCopy;
+ }
+
+ function _copyCriteriaResolvers(
+ CriteriaResolver[] memory criteriaResolvers
+ ) private pure returns (CriteriaResolver[] memory) {
+ CriteriaResolver[] memory copy = new CriteriaResolver[](
+ criteriaResolvers.length
+ );
+ for (uint256 i = 0; i < criteriaResolvers.length; i++) {
+ copy[i] = criteriaResolvers[i];
+ }
+ return copy;
+ }
+
+ function _copyFuzzParams(
+ FuzzParams memory params
+ ) private pure returns (FuzzParams memory) {
+ return
+ FuzzParams({
+ seed: params.seed,
+ totalOrders: params.totalOrders,
+ maxOfferItems: params.maxOfferItems,
+ maxConsiderationItems: params.maxConsiderationItems,
+ seedInput: bytes.concat(params.seedInput)
+ });
+ }
+}
+
+// @dev Implementation cribbed from forge-std bound
+function bound(
+ uint256 x,
+ uint256 min,
+ uint256 max
+) pure returns (uint256 result) {
+ require(min <= max, "Max is less than min.");
+ // If x is between min and max, return x directly. This is to ensure that
+ // dictionary values do not get shifted if the min is nonzero.
+ if (x >= min && x <= max) return x;
+
+ uint256 size = max - min + 1;
+
+ // If the value is 0, 1, 2, 3, warp that to min, min+1, min+2, min+3.
+ // Similarly for the UINT256_MAX side. This helps ensure coverage of the
+ // min/max values.
+ if (x <= 3 && size > x) return min + x;
+ if (x >= type(uint256).max - 3 && size > type(uint256).max - x)
+ return max - (type(uint256).max - x);
+
+ // Otherwise, wrap x into the range [min, max], i.e. the range is inclusive.
+ if (x > max) {
+ uint256 diff = x - max;
+ uint256 rem = diff % size;
+ if (rem == 0) return max;
+ result = min + rem - 1;
+ } else if (x < min) {
+ uint256 diff = min - x;
+ uint256 rem = diff % size;
+ if (rem == 0) return min;
+ result = max - rem + 1;
+ }
+}
diff --git a/test/foundry/new/helpers/Labeler.sol b/test/foundry/new/helpers/Labeler.sol
new file mode 100644
index 000000000..89b22b246
--- /dev/null
+++ b/test/foundry/new/helpers/Labeler.sol
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { vm } from "./VmUtils.sol";
+import { LibString } from "solady/src/utils/LibString.sol";
+
+address constant LABELER_ADDRESS = address(
+ uint160(uint256(keccak256(".labeler")))
+);
+
+function setLabel(address account, string memory _label) {
+ vm.store(
+ LABELER_ADDRESS,
+ bytes32(uint256(uint160(account))),
+ LibString.packOne(_label)
+ );
+}
+
+function withLabel(address account) pure returns (string memory out) {
+ out = LibString.toHexString(account);
+ string memory label = pureGetLabel()(account);
+ uint256 length;
+ assembly {
+ length := mload(label)
+ }
+ if (length > 0) {
+ out = string.concat(out, " (", label, ")");
+ }
+}
+
+function getLabel(address account) pure returns (string memory) {
+ return pureGetLabel()(account);
+}
+
+function getLabelView(address account) view returns (string memory _label) {
+ bytes32 storedLabel = vm.load(
+ LABELER_ADDRESS,
+ bytes32(uint256(uint160(account)))
+ );
+ if (storedLabel != bytes32(0)) {
+ return LibString.unpackOne(storedLabel);
+ }
+}
+
+function withLabel(address[] memory accounts) pure returns (string[] memory) {
+ uint256 length = accounts.length;
+ string[] memory out = new string[](length);
+ for (uint256 i; i < length; i++) {
+ out[i] = withLabel(accounts[i]);
+ }
+ return out;
+}
+
+function pureGetLabel()
+ pure
+ returns (function(address) internal pure returns (string memory) pureFn)
+{
+ function(address)
+ internal
+ view
+ returns (string memory) viewFn = getLabelView;
+ assembly {
+ pureFn := viewFn
+ }
+}
diff --git a/test/foundry/new/helpers/Metrics.sol b/test/foundry/new/helpers/Metrics.sol
new file mode 100644
index 000000000..969ef188e
--- /dev/null
+++ b/test/foundry/new/helpers/Metrics.sol
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { vm } from "./VmUtils.sol";
+
+function logCall(string memory name) {
+ logCall(name, true);
+}
+
+/**
+ * @dev Log a call to "call-metrics.txt"
+ */
+function logCall(string memory name, bool enabled) {
+ logCounter("call", name, enabled);
+}
+
+/**
+ * @dev Log a mutation to "mutation-metrics.txt"
+ */
+function logMutation(string memory name) {
+ logCounter("mutation", name, true);
+}
+
+/**
+ * @dev Log a vm.assume to "assume-metrics.txt"
+ */
+function logAssume(string memory name) {
+ logCounter("assume", name, true);
+}
+
+/**
+ * @dev Log a counter to a metrics file if the SEAPORT_COLLECT_FUZZ_METRICS env
+ * var is set. Named metrics are written as statsd counters, e.g.
+ * "metric:1|c". To write to a new file, it must be allowlisted under
+ * `fs_permissions` in `foundry.toml`.
+ *
+ * @param file name of the metrics file to write to. "-metrics.txt" will be
+ * appended to the name.
+ * @param metric name of the metric to increment.
+ * @param enabled flag to enable/disable metrics collection
+ */
+function logCounter(string memory file, string memory metric, bool enabled) {
+ if (enabled && vm.envOr("SEAPORT_COLLECT_FUZZ_METRICS", false)) {
+ string memory counter = string.concat(metric, ":1|c");
+ vm.writeLine(string.concat(file, "-metrics.txt"), counter);
+ }
+}
diff --git a/test/foundry/new/helpers/PreapprovedERC721.sol b/test/foundry/new/helpers/PreapprovedERC721.sol
new file mode 100644
index 000000000..04cc6de1a
--- /dev/null
+++ b/test/foundry/new/helpers/PreapprovedERC721.sol
@@ -0,0 +1,31 @@
+// SPDX-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { CustomERC721 } from "../../token/CustomERC721.sol";
+
+contract PreapprovedERC721 is CustomERC721 {
+ mapping(address => bool) public preapprovals;
+
+ constructor(address[] memory preapproved) CustomERC721("", "") {
+ for (uint256 i = 0; i < preapproved.length; i++) {
+ preapprovals[preapproved[i]] = true;
+ }
+ }
+
+ function mint(address to, uint256 amount) external returns (bool) {
+ _mint(to, amount);
+ return true;
+ }
+
+ function isApprovedForAll(
+ address owner,
+ address operator
+ ) public view override returns (bool) {
+ return
+ preapprovals[operator] || super.isApprovedForAll(owner, operator);
+ }
+
+ function tokenURI(uint256) public pure override returns (string memory) {
+ return "";
+ }
+}
diff --git a/test/foundry/new/helpers/Searializer.sol b/test/foundry/new/helpers/Searializer.sol
new file mode 100644
index 000000000..453284ae3
--- /dev/null
+++ b/test/foundry/new/helpers/Searializer.sol
@@ -0,0 +1,1022 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Vm, vm } from "./VmUtils.sol";
+
+import {
+ AdditionalRecipient,
+ AdvancedOrder,
+ BasicOrderParameters,
+ ConsiderationItem,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ OrderParameters,
+ ReceivedItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import {
+ BasicOrderType,
+ ItemType,
+ OrderType,
+ Side
+} from "seaport-sol/SeaportEnums.sol";
+
+import { Result } from "./FuzzHelpers.sol";
+
+import {
+ FuzzParams,
+ FuzzTestContext,
+ ReturnValues
+} from "./FuzzTestContextLib.sol";
+
+import {
+ ERC1155AccountDump,
+ ERC1155TokenDump,
+ ERC20TokenDump,
+ ERC721TokenDump,
+ ExpectedBalancesDump,
+ NativeAccountDump
+} from "./ExpectedBalances.sol";
+
+import { withLabel } from "./Labeler.sol";
+
+import {
+ ErrorsAndWarnings
+} from "../../../../contracts/helpers/order-validator/SeaportValidator.sol";
+
+import {
+ IssueStringHelpers
+} from "../../../../contracts/helpers/order-validator/lib/SeaportValidatorTypes.sol";
+
+/**
+ * @notice A helper library to seralize test data as JSON.
+ */
+library Searializer {
+ function tojsonBytes32(
+ string memory objectKey,
+ string memory valueKey,
+ bytes32 value
+ ) internal returns (string memory) {
+ return vm.serializeBytes32(objectKey, valueKey, value);
+ }
+
+ function tojsonAddress(
+ string memory objectKey,
+ string memory valueKey,
+ address value
+ ) internal returns (string memory) {
+ return vm.serializeString(objectKey, valueKey, withLabel(value));
+ }
+
+ function tojsonUint256(
+ string memory objectKey,
+ string memory valueKey,
+ uint256 value
+ ) internal returns (string memory) {
+ return vm.serializeUint(objectKey, valueKey, value);
+ }
+
+ function tojsonFuzzParams(
+ string memory objectKey,
+ string memory valueKey,
+ FuzzParams memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonUint256(obj, "seed", value.seed);
+ tojsonUint256(obj, "totalOrders", value.totalOrders);
+ tojsonUint256(obj, "maxOfferItems", value.maxOfferItems);
+ string memory finalJson = tojsonUint256(
+ obj,
+ "maxConsiderationItems",
+ value.maxConsiderationItems
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonItemType(
+ string memory objectKey,
+ string memory valueKey,
+ ItemType value
+ ) internal returns (string memory) {
+ string[6] memory members = [
+ "NATIVE",
+ "ERC20",
+ "ERC721",
+ "ERC1155",
+ "ERC721_WITH_CRITERIA",
+ "ERC1155_WITH_CRITERIA"
+ ];
+ uint256 index = uint256(value);
+ return vm.serializeString(objectKey, valueKey, members[index]);
+ }
+
+ function tojsonOfferItem(
+ string memory objectKey,
+ string memory valueKey,
+ OfferItem memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonItemType(obj, "itemType", value.itemType);
+ tojsonAddress(obj, "token", value.token);
+ tojsonUint256(obj, "identifierOrCriteria", value.identifierOrCriteria);
+ tojsonUint256(obj, "startAmount", value.startAmount);
+ string memory finalJson = tojsonUint256(
+ obj,
+ "endAmount",
+ value.endAmount
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayOfferItem(
+ string memory objectKey,
+ string memory valueKey,
+ OfferItem[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonOfferItem(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonConsiderationItem(
+ string memory objectKey,
+ string memory valueKey,
+ ConsiderationItem memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonItemType(obj, "itemType", value.itemType);
+ tojsonAddress(obj, "token", value.token);
+ tojsonUint256(obj, "identifierOrCriteria", value.identifierOrCriteria);
+ tojsonUint256(obj, "startAmount", value.startAmount);
+ tojsonUint256(obj, "endAmount", value.endAmount);
+ string memory finalJson = tojsonAddress(
+ obj,
+ "recipient",
+ value.recipient
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayConsiderationItem(
+ string memory objectKey,
+ string memory valueKey,
+ ConsiderationItem[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonConsiderationItem(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonOrderType(
+ string memory objectKey,
+ string memory valueKey,
+ OrderType value
+ ) internal returns (string memory) {
+ string[5] memory members = [
+ "FULL_OPEN",
+ "PARTIAL_OPEN",
+ "FULL_RESTRICTED",
+ "PARTIAL_RESTRICTED",
+ "CONTRACT"
+ ];
+ uint256 index = uint256(value);
+ return vm.serializeString(objectKey, valueKey, members[index]);
+ }
+
+ function tojsonOrderParameters(
+ string memory objectKey,
+ string memory valueKey,
+ OrderParameters memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "offerer", value.offerer);
+ tojsonAddress(obj, "zone", value.zone);
+ tojsonDynArrayOfferItem(obj, "offer", value.offer);
+ tojsonDynArrayConsiderationItem(
+ obj,
+ "consideration",
+ value.consideration
+ );
+ tojsonOrderType(obj, "orderType", value.orderType);
+ tojsonUint256(obj, "startTime", value.startTime);
+ tojsonUint256(obj, "endTime", value.endTime);
+ tojsonBytes32(obj, "zoneHash", value.zoneHash);
+ tojsonUint256(obj, "salt", value.salt);
+ tojsonBytes32(obj, "conduitKey", value.conduitKey);
+ string memory finalJson = tojsonUint256(
+ obj,
+ "totalOriginalConsiderationItems",
+ value.totalOriginalConsiderationItems
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonBytes(
+ string memory objectKey,
+ string memory valueKey,
+ bytes memory value
+ ) internal returns (string memory) {
+ return vm.serializeBytes(objectKey, valueKey, value);
+ }
+
+ function tojsonAdvancedOrder(
+ string memory objectKey,
+ string memory valueKey,
+ AdvancedOrder memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonOrderParameters(obj, "parameters", value.parameters);
+ tojsonUint256(obj, "numerator", value.numerator);
+ tojsonUint256(obj, "denominator", value.denominator);
+ tojsonBytes(obj, "signature", value.signature);
+ string memory finalJson = tojsonBytes(
+ obj,
+ "extraData",
+ value.extraData
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayAdvancedOrder(
+ string memory objectKey,
+ string memory valueKey,
+ AdvancedOrder[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonAdvancedOrder(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonSide(
+ string memory objectKey,
+ string memory valueKey,
+ Side value
+ ) internal returns (string memory) {
+ string[2] memory members = ["OFFER", "CONSIDERATION"];
+ uint256 index = uint256(value);
+ return vm.serializeString(objectKey, valueKey, members[index]);
+ }
+
+ function tojsonDynArrayBytes32(
+ string memory objectKey,
+ string memory valueKey,
+ bytes32[] memory value
+ ) internal returns (string memory) {
+ return vm.serializeBytes32(objectKey, valueKey, value);
+ }
+
+ function tojsonCriteriaResolver(
+ string memory objectKey,
+ string memory valueKey,
+ CriteriaResolver memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonUint256(obj, "orderIndex", value.orderIndex);
+ tojsonSide(obj, "side", value.side);
+ tojsonUint256(obj, "index", value.index);
+ tojsonUint256(obj, "identifier", value.identifier);
+ string memory finalJson = tojsonDynArrayBytes32(
+ obj,
+ "criteriaProof",
+ value.criteriaProof
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayCriteriaResolver(
+ string memory objectKey,
+ string memory valueKey,
+ CriteriaResolver[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonCriteriaResolver(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonFulfillmentComponent(
+ string memory objectKey,
+ string memory valueKey,
+ FulfillmentComponent memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonUint256(obj, "orderIndex", value.orderIndex);
+ string memory finalJson = tojsonUint256(
+ obj,
+ "itemIndex",
+ value.itemIndex
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayFulfillmentComponent(
+ string memory objectKey,
+ string memory valueKey,
+ FulfillmentComponent[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonFulfillmentComponent(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonFulfillment(
+ string memory objectKey,
+ string memory valueKey,
+ Fulfillment memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonDynArrayFulfillmentComponent(
+ obj,
+ "offerComponents",
+ value.offerComponents
+ );
+ string memory finalJson = tojsonDynArrayFulfillmentComponent(
+ obj,
+ "considerationComponents",
+ value.considerationComponents
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayFulfillment(
+ string memory objectKey,
+ string memory valueKey,
+ Fulfillment[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonFulfillment(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonDynArrayDynArrayFulfillmentComponent(
+ string memory objectKey,
+ string memory valueKey,
+ FulfillmentComponent[][] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonDynArrayFulfillmentComponent(
+ obj,
+ vm.toString(i),
+ value[i]
+ );
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonBasicOrderType(
+ string memory objectKey,
+ string memory valueKey,
+ BasicOrderType value
+ ) internal returns (string memory) {
+ string[24] memory members = [
+ "ETH_TO_ERC721_FULL_OPEN",
+ "ETH_TO_ERC721_PARTIAL_OPEN",
+ "ETH_TO_ERC721_FULL_RESTRICTED",
+ "ETH_TO_ERC721_PARTIAL_RESTRICTED",
+ "ETH_TO_ERC1155_FULL_OPEN",
+ "ETH_TO_ERC1155_PARTIAL_OPEN",
+ "ETH_TO_ERC1155_FULL_RESTRICTED",
+ "ETH_TO_ERC1155_PARTIAL_RESTRICTED",
+ "ERC20_TO_ERC721_FULL_OPEN",
+ "ERC20_TO_ERC721_PARTIAL_OPEN",
+ "ERC20_TO_ERC721_FULL_RESTRICTED",
+ "ERC20_TO_ERC721_PARTIAL_RESTRICTED",
+ "ERC20_TO_ERC1155_FULL_OPEN",
+ "ERC20_TO_ERC1155_PARTIAL_OPEN",
+ "ERC20_TO_ERC1155_FULL_RESTRICTED",
+ "ERC20_TO_ERC1155_PARTIAL_RESTRICTED",
+ "ERC721_TO_ERC20_FULL_OPEN",
+ "ERC721_TO_ERC20_PARTIAL_OPEN",
+ "ERC721_TO_ERC20_FULL_RESTRICTED",
+ "ERC721_TO_ERC20_PARTIAL_RESTRICTED",
+ "ERC1155_TO_ERC20_FULL_OPEN",
+ "ERC1155_TO_ERC20_PARTIAL_OPEN",
+ "ERC1155_TO_ERC20_FULL_RESTRICTED",
+ "ERC1155_TO_ERC20_PARTIAL_RESTRICTED"
+ ];
+ uint256 index = uint256(value);
+ return vm.serializeString(objectKey, valueKey, members[index]);
+ }
+
+ function tojsonAdditionalRecipient(
+ string memory objectKey,
+ string memory valueKey,
+ AdditionalRecipient memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonUint256(obj, "amount", value.amount);
+ string memory finalJson = tojsonAddress(
+ obj,
+ "recipient",
+ value.recipient
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayAdditionalRecipient(
+ string memory objectKey,
+ string memory valueKey,
+ AdditionalRecipient[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonAdditionalRecipient(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonBasicOrderParameters(
+ string memory objectKey,
+ string memory valueKey,
+ BasicOrderParameters memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "considerationToken", value.considerationToken);
+ tojsonUint256(
+ obj,
+ "considerationIdentifier",
+ value.considerationIdentifier
+ );
+ tojsonUint256(obj, "considerationAmount", value.considerationAmount);
+ tojsonAddress(obj, "offerer", value.offerer);
+ tojsonAddress(obj, "zone", value.zone);
+ tojsonAddress(obj, "offerToken", value.offerToken);
+ tojsonUint256(obj, "offerIdentifier", value.offerIdentifier);
+ tojsonUint256(obj, "offerAmount", value.offerAmount);
+ tojsonBasicOrderType(obj, "basicOrderType", value.basicOrderType);
+ tojsonUint256(obj, "startTime", value.startTime);
+ tojsonUint256(obj, "endTime", value.endTime);
+ tojsonBytes32(obj, "zoneHash", value.zoneHash);
+ tojsonUint256(obj, "salt", value.salt);
+ tojsonBytes32(obj, "offererConduitKey", value.offererConduitKey);
+ tojsonBytes32(obj, "fulfillerConduitKey", value.fulfillerConduitKey);
+ tojsonUint256(
+ obj,
+ "totalOriginalAdditionalRecipients",
+ value.totalOriginalAdditionalRecipients
+ );
+ tojsonDynArrayAdditionalRecipient(
+ obj,
+ "additionalRecipients",
+ value.additionalRecipients
+ );
+ string memory finalJson = tojsonBytes(
+ obj,
+ "signature",
+ value.signature
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayBytes4(
+ string memory objectKey,
+ string memory valueKey,
+ bytes4[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonBytes32(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonArray2Bytes32(
+ string memory objectKey,
+ string memory valueKey,
+ bytes32[2] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonBytes32(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonDynArrayArray2Bytes32(
+ string memory objectKey,
+ string memory valueKey,
+ bytes32[2][] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonArray2Bytes32(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonResult(
+ string memory objectKey,
+ string memory valueKey,
+ Result value
+ ) internal returns (string memory) {
+ string[4] memory members = [
+ "FULFILLMENT",
+ "UNAVAILABLE",
+ "VALIDATE",
+ "CANCEL"
+ ];
+ uint256 index = uint256(value);
+ return vm.serializeString(objectKey, valueKey, members[index]);
+ }
+
+ function tojsonDynArrayResult(
+ string memory objectKey,
+ string memory valueKey,
+ Result[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonResult(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonReceivedItem(
+ string memory objectKey,
+ string memory valueKey,
+ ReceivedItem memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonItemType(obj, "itemType", value.itemType);
+ tojsonAddress(obj, "token", value.token);
+ tojsonUint256(obj, "identifier", value.identifier);
+ tojsonUint256(obj, "amount", value.amount);
+ string memory finalJson = tojsonAddress(
+ obj,
+ "recipient",
+ value.recipient
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonExecution(
+ string memory objectKey,
+ string memory valueKey,
+ Execution memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonReceivedItem(obj, "item", value.item);
+ tojsonAddress(obj, "offerer", value.offerer);
+ string memory finalJson = tojsonBytes32(
+ obj,
+ "conduitKey",
+ value.conduitKey
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayExecution(
+ string memory objectKey,
+ string memory valueKey,
+ Execution[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonExecution(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonLog(
+ string memory objectKey,
+ string memory valueKey,
+ Vm.Log memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonDynArrayBytes32(obj, "topics", value.topics);
+ tojsonBytes(obj, "data", value.data);
+ string memory finalJson = tojsonAddress(obj, "emitter", value.emitter);
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayLog(
+ string memory objectKey,
+ string memory valueKey,
+ Vm.Log[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonLog(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonBool(
+ string memory objectKey,
+ string memory valueKey,
+ bool value
+ ) internal returns (string memory) {
+ return vm.serializeBool(objectKey, valueKey, value);
+ }
+
+ function tojsonDynArrayBool(
+ string memory objectKey,
+ string memory valueKey,
+ bool[] memory value
+ ) internal returns (string memory) {
+ return vm.serializeBool(objectKey, valueKey, value);
+ }
+
+ function tojsonReturnValues(
+ string memory objectKey,
+ string memory valueKey,
+ ReturnValues memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonBool(obj, "fulfilled", value.fulfilled);
+ tojsonBool(obj, "cancelled", value.cancelled);
+ tojsonBool(obj, "validated", value.validated);
+ tojsonDynArrayBool(obj, "availableOrders", value.availableOrders);
+ string memory finalJson = tojsonDynArrayExecution(
+ obj,
+ "executions",
+ value.executions
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonFuzzTestContext(
+ string memory objectKey,
+ string memory valueKey,
+ FuzzTestContext memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonBytes32(obj, "_action", value._action);
+ tojsonAddress(obj, "seaport", address(value.seaport));
+ tojsonAddress(
+ obj,
+ "conduitController",
+ address(value.conduitController)
+ );
+ tojsonAddress(obj, "caller", value.executionState.caller);
+ tojsonAddress(obj, "recipient", value.executionState.recipient);
+ tojsonFuzzParams(obj, "fuzzParams", value.fuzzParams);
+ tojsonDynArrayAdvancedOrder(obj, "orders", value.executionState.orders);
+ tojsonDynArrayAdvancedOrder(
+ obj,
+ "previewedOrders",
+ value.executionState.previewedOrders
+ );
+ tojsonUint256(obj, "counter", value.executionState.counter);
+ tojsonBytes32(
+ obj,
+ "fulfillerConduitKey",
+ value.executionState.fulfillerConduitKey
+ );
+ tojsonDynArrayCriteriaResolver(
+ obj,
+ "criteriaResolvers",
+ value.executionState.criteriaResolvers
+ );
+ tojsonDynArrayFulfillment(
+ obj,
+ "fulfillments",
+ value.executionState.fulfillments
+ );
+ tojsonDynArrayFulfillmentComponent(
+ obj,
+ "remainingOfferComponents",
+ value.executionState.remainingOfferComponents
+ );
+ tojsonDynArrayDynArrayFulfillmentComponent(
+ obj,
+ "offerFulfillments",
+ value.executionState.offerFulfillments
+ );
+ tojsonDynArrayDynArrayFulfillmentComponent(
+ obj,
+ "considerationFulfillments",
+ value.executionState.considerationFulfillments
+ );
+ tojsonUint256(
+ obj,
+ "maximumFulfilled",
+ value.executionState.maximumFulfilled
+ );
+ tojsonBasicOrderParameters(
+ obj,
+ "basicOrderParameters",
+ value.executionState.basicOrderParameters
+ );
+ tojsonAddress(obj, "testHelpers", address(value.testHelpers));
+ tojsonDynArrayBytes4(obj, "checks", value.checks);
+ tojsonDynArrayBytes32(
+ obj,
+ "expectedZoneCalldataHash",
+ value.expectations.expectedZoneCalldataHash
+ );
+ tojsonDynArrayArray2Bytes32(
+ obj,
+ "expectedContractOrderCalldataHashes",
+ value.expectations.expectedContractOrderCalldataHashes
+ );
+ tojsonDynArrayResult(
+ obj,
+ "expectedResults",
+ value.expectations.expectedResults
+ );
+ tojsonDynArrayExecution(
+ obj,
+ "expectedImplicitPreExecutions",
+ value.expectations.expectedImplicitPreExecutions
+ );
+ tojsonDynArrayExecution(
+ obj,
+ "expectedImplicitPostExecutions",
+ value.expectations.expectedImplicitPostExecutions
+ );
+ tojsonDynArrayExecution(
+ obj,
+ "expectedExplicitExecutions",
+ value.expectations.expectedExplicitExecutions
+ );
+ tojsonDynArrayExecution(
+ obj,
+ "allExpectedExecutions",
+ value.expectations.allExpectedExecutions
+ );
+ tojsonDynArrayBytes32(
+ obj,
+ "expectedTransferEventHashes",
+ value.expectations.expectedTransferEventHashes
+ );
+ tojsonDynArrayBytes32(
+ obj,
+ "expectedSeaportEventHashes",
+ value.expectations.expectedSeaportEventHashes
+ );
+ tojsonDynArrayLog(obj, "actualEvents", value.actualEvents);
+ string memory finalJson = tojsonReturnValues(
+ obj,
+ "returnValues",
+ value.returnValues
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonNativeAccountDump(
+ string memory objectKey,
+ string memory valueKey,
+ NativeAccountDump memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "account", value.account);
+ string memory finalJson = tojsonUint256(obj, "balance", value.balance);
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayNativeAccountDump(
+ string memory objectKey,
+ string memory valueKey,
+ NativeAccountDump[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonNativeAccountDump(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonDynArrayAddress(
+ string memory objectKey,
+ string memory valueKey,
+ address[] memory value
+ ) internal returns (string memory) {
+ return vm.serializeString(objectKey, valueKey, withLabel(value));
+ }
+
+ function tojsonDynArrayUint256(
+ string memory objectKey,
+ string memory valueKey,
+ uint256[] memory value
+ ) internal returns (string memory) {
+ return vm.serializeUint(objectKey, valueKey, value);
+ }
+
+ function tojsonERC20TokenDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC20TokenDump memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "token", value.token);
+ tojsonDynArrayAddress(obj, "accounts", value.accounts);
+ string memory finalJson = tojsonDynArrayUint256(
+ obj,
+ "balances",
+ value.balances
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayDynArrayUint256(
+ string memory objectKey,
+ string memory valueKey,
+ uint256[][] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonDynArrayUint256(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonERC721TokenDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC721TokenDump memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "token", value.token);
+ tojsonDynArrayAddress(obj, "accounts", value.accounts);
+ string memory finalJson = tojsonDynArrayDynArrayUint256(
+ obj,
+ "accountIdentifiers",
+ value.accountIdentifiers
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonERC1155AccountDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC1155AccountDump memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "account", value.account);
+ tojsonDynArrayUint256(obj, "identifiers", value.identifiers);
+ string memory finalJson = tojsonDynArrayUint256(
+ obj,
+ "balances",
+ value.balances
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayERC1155AccountDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC1155AccountDump[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonERC1155AccountDump(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonERC1155TokenDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC1155TokenDump memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonAddress(obj, "token", value.token);
+ string memory finalJson = tojsonDynArrayERC1155AccountDump(
+ obj,
+ "accounts",
+ value.accounts
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayERC20TokenDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC20TokenDump[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonERC20TokenDump(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonDynArrayERC721TokenDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC721TokenDump[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonERC721TokenDump(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonDynArrayERC1155TokenDump(
+ string memory objectKey,
+ string memory valueKey,
+ ERC1155TokenDump[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ out = tojsonERC1155TokenDump(obj, vm.toString(i), value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonExpectedBalancesDump(
+ string memory objectKey,
+ string memory valueKey,
+ ExpectedBalancesDump memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ tojsonDynArrayERC20TokenDump(obj, "erc20", value.erc20);
+ tojsonDynArrayERC721TokenDump(obj, "erc721", value.erc721);
+ string memory finalJson = tojsonDynArrayERC1155TokenDump(
+ obj,
+ "erc1155",
+ value.erc1155
+ );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function tojsonDynArrayValidationErrorsAndWarnings(
+ string memory objectKey,
+ string memory valueKey,
+ ErrorsAndWarnings[] memory value
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ if (value[i].errors.length > 0) {
+ out = tojsonDynArrayValidationErrorMessages(
+ obj,
+ vm.toString(i),
+ value[i].errors
+ );
+ }
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function tojsonDynArrayValidationErrorMessages(
+ string memory objectKey,
+ string memory valueKey,
+ uint16[] memory value
+ ) internal returns (string memory) {
+ uint256 length = value.length;
+ string[] memory out = new string[](length);
+ for (uint256 i; i < length; i++) {
+ out[i] = IssueStringHelpers.toIssueString(value[i]);
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+}
diff --git a/test/foundry/new/helpers/VmUtils.sol b/test/foundry/new/helpers/VmUtils.sol
new file mode 100644
index 000000000..fcf6ede93
--- /dev/null
+++ b/test/foundry/new/helpers/VmUtils.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Vm } from "forge-std/Vm.sol";
+import { logAssume } from "./Metrics.sol";
+
+address constant VM_ADDRESS = address(
+ uint160(uint256(keccak256("hevm cheat code")))
+);
+Vm constant vm = Vm(VM_ADDRESS);
+
+/**
+ * @dev A wrapper for Foundry vm.assume that logs rejected fuzz runs with a
+ * named reason. Use this instead of vm.assume in fuzz tests and give
+ * each assumption a unique name.
+ */
+function assume(bool condition, string memory name) {
+ if (!condition) {
+ logAssume(name);
+ }
+ vm.assume(condition);
+}
diff --git a/test/foundry/new/helpers/event-utils/EventHashes.sol b/test/foundry/new/helpers/event-utils/EventHashes.sol
new file mode 100644
index 000000000..84d05da81
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/EventHashes.sol
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+/**
+ * @dev Low level helpers. getTopicsHash and getEventHash are used to generate
+ * the hashes for topics and events respectively. getEventHashWithTopics is
+ * a convenience wrapper around the two.
+ */
+function getTopicsHash(
+ bytes32 topic0,
+ bytes32 topic1,
+ bytes32 topic2,
+ bytes32 topic3
+) pure returns (bytes32 topicsHash) {
+ topicsHash = keccak256(abi.encode(topic0, topic1, topic2, topic3));
+}
+
+function getTopicsHash(
+ bytes32 topic0,
+ bytes32 topic1,
+ bytes32 topic2
+) pure returns (bytes32 topicsHash) {
+ topicsHash = keccak256(abi.encode(topic0, topic1, topic2));
+}
+
+function getTopicsHash(
+ bytes32 topic0,
+ bytes32 topic1
+) pure returns (bytes32 topicsHash) {
+ topicsHash = keccak256(abi.encode(topic0, topic1));
+}
+
+function getTopicsHash(bytes32 topic0) pure returns (bytes32 topicsHash) {
+ topicsHash = keccak256(abi.encode(topic0));
+}
+
+function getTopicsHash() pure returns (bytes32 topicsHash) {
+ topicsHash = keccak256("");
+}
+
+function getEventHash(
+ address emitter,
+ bytes32 topicsHash,
+ bytes32 dataHash
+) pure returns (bytes32 eventHash) {
+ return keccak256(abi.encode(emitter, topicsHash, dataHash));
+}
+
+function getEventHashWithTopics(
+ address emitter,
+ bytes32 topic0,
+ bytes32 topic1,
+ bytes32 topic2,
+ bytes32 topic3,
+ bytes32 dataHash
+) pure returns (bytes32 eventHash) {
+ bytes32 topicsHash = getTopicsHash(topic0, topic1, topic2, topic3);
+ return getEventHash(emitter, topicsHash, dataHash);
+}
+
+function getEventHashWithTopics(
+ address emitter,
+ bytes32 topic0,
+ bytes32 topic1,
+ bytes32 topic2,
+ bytes32 dataHash
+) pure returns (bytes32 eventHash) {
+ bytes32 topicsHash = getTopicsHash(topic0, topic1, topic2);
+ return getEventHash(emitter, topicsHash, dataHash);
+}
+
+function getEventHashWithTopics(
+ address emitter,
+ bytes32 topic0,
+ bytes32 topic1,
+ bytes32 dataHash
+) pure returns (bytes32 eventHash) {
+ bytes32 topicsHash = getTopicsHash(topic0, topic1);
+ return getEventHash(emitter, topicsHash, dataHash);
+}
+
+function getEventHashWithTopics(
+ address emitter,
+ bytes32 topic0,
+ bytes32 dataHash
+) pure returns (bytes32 eventHash) {
+ bytes32 topicsHash = getTopicsHash(topic0);
+ return getEventHash(emitter, topicsHash, dataHash);
+}
+
+function getEventHashWithTopics(
+ address emitter,
+ bytes32 dataHash
+) pure returns (bytes32 eventHash) {
+ bytes32 topicsHash = getTopicsHash();
+ return getEventHash(emitter, topicsHash, dataHash);
+}
diff --git a/test/foundry/new/helpers/event-utils/EventSerializer.sol b/test/foundry/new/helpers/event-utils/EventSerializer.sol
new file mode 100644
index 000000000..5d86a3004
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/EventSerializer.sol
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { vm } from "../VmUtils.sol";
+
+import { SpentItem, ReceivedItem } from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+struct ERC20TransferEvent {
+ string kind;
+ address token;
+ address from;
+ address to;
+ uint256 amount;
+}
+
+struct ERC721TransferEvent {
+ string kind;
+ address token;
+ address from;
+ address to;
+ uint256 identifier;
+ // bytes32 topicHash;
+ // bytes32 dataHash;
+ // bytes32 eventHash;
+}
+
+struct ERC1155TransferEvent {
+ string kind;
+ address token;
+ address operator;
+ address from;
+ address to;
+ uint256 identifier;
+ uint256 amount;
+ // bytes32 topicHash;
+ // bytes32 dataHash;
+ // bytes32 eventHash;
+}
+
+struct OrderFulfilledEvent {
+ bytes32 orderHash;
+ address offerer;
+ address zone;
+ address recipient;
+ SpentItem[] offer;
+ ReceivedItem[] consideration;
+}
+
+library EventSerializer {
+ function serializeString(
+ string memory objectKey,
+ string memory valueKey,
+ string memory value
+ ) internal returns (string memory) {
+ return vm.serializeString(objectKey, valueKey, value);
+ }
+
+ function serializeAddress(
+ string memory objectKey,
+ string memory valueKey,
+ address value
+ ) internal returns (string memory) {
+ return vm.serializeAddress(objectKey, valueKey, value);
+ }
+
+ function serializeBytes32(
+ string memory objectKey,
+ string memory valueKey,
+ bytes32 value
+ ) internal returns (string memory) {
+ return vm.serializeBytes32(objectKey, valueKey, value);
+ }
+
+ function serializeUint256(
+ string memory objectKey,
+ string memory valueKey,
+ uint256 value
+ ) internal returns (string memory) {
+ return vm.serializeUint(objectKey, valueKey, value);
+ }
+
+ // function serializeSpentItem(
+ // SpentItem memory value,
+ // string memory objectKey,
+ // string memory valueKey
+ // ) internal returns (string memory) {
+ // string memory obj = string.concat(objectKey, valueKey);
+ // serializeUint256(obj, "itemType", uint256(value.itemType));
+ // serializeAddress(obj, "token", value.token);
+ // serializeUint256(obj, "identifier", value.identifier);
+ // string memory finalJson = serializeUint256(obj, "amount", value.amount);
+ // return vm.serializeString(objectKey, valueKey, finalJson);
+ // }
+
+ // function serializeReceivedItem(
+ // ReceivedItem memory value,
+ // string memory objectKey,
+ // string memory valueKey
+ // ) internal returns (string memory) {
+ // string memory obj = string.concat(objectKey, valueKey);
+ // serializeUint256(obj, "itemType", uint256(value.itemType));
+ // serializeAddress(obj, "token", value.token);
+ // serializeUint256(obj, "identifier", value.identifier);
+ // serializeUint256(obj, "amount", value.amount);
+ // string memory finalJson = serializeAddress(
+ // obj,
+ // "recipient",
+ // value.executionState.recipient
+ // );
+ // return vm.serializeString(objectKey, valueKey, finalJson);
+ // }
+
+ // function serializeSpentItemArray(
+ // SpentItem[] memory value,
+ // string memory objectKey,
+ // string memory valueKey
+ // ) internal returns (string memory) {
+ // string memory obj = string.concat(objectKey, valueKey);
+ // for (uint256 i = 0; i < value.length; i++) {
+ // serializeSpentItem(value[i], obj, string.concat("item", i));
+ // }
+ // return vm.serializeString(objectKey, valueKey, obj);
+ // }
+
+ // function serializeReceivedItemArray(
+ // ReceivedItem[] memory value,
+ // string memory objectKey,
+ // string memory valueKey
+ // ) internal returns (string memory) {
+ // string memory obj = string.concat(objectKey, valueKey);
+ // for (uint256 i = 0; i < value.length; i++) {
+ // serializeReceivedItem(value[i], obj, string.concat("item", i));
+ // }
+ // return vm.serializeString(objectKey, valueKey, obj);
+ // }
+
+ function serializeERC20TransferEvent(
+ ERC20TransferEvent memory value,
+ string memory objectKey,
+ string memory valueKey
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ serializeString(obj, "kind", value.kind);
+ serializeAddress(obj, "token", value.token);
+ serializeAddress(obj, "from", value.from);
+ serializeAddress(obj, "to", value.to);
+ string memory finalJson = serializeUint256(obj, "amount", value.amount);
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function serializeERC721TransferEvent(
+ ERC721TransferEvent memory value,
+ string memory objectKey,
+ string memory valueKey
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ serializeString(obj, "kind", value.kind);
+ serializeAddress(obj, "token", value.token);
+ serializeAddress(obj, "from", value.from);
+ serializeAddress(obj, "to", value.to);
+ string memory finalJson = serializeUint256(
+ obj,
+ "identifier",
+ value.identifier
+ );
+ // serializeUint256(obj, "identifier", value.identifier);
+ // serializeBytes32(obj, "topicHash", value.topicHash);
+ // serializeBytes32(obj, "dataHash", value.dataHash);
+ // string memory finalJson = serializeBytes32(
+ // obj,
+ // "eventHash",
+ // value.eventHash
+ // );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ function serializeERC1155TransferEvent(
+ ERC1155TransferEvent memory value,
+ string memory objectKey,
+ string memory valueKey
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ serializeString(obj, "kind", value.kind);
+ serializeAddress(obj, "operator", value.operator);
+ serializeAddress(obj, "token", value.token);
+ serializeAddress(obj, "from", value.from);
+ serializeAddress(obj, "to", value.to);
+ serializeUint256(obj, "identifier", value.identifier);
+ string memory finalJson = serializeUint256(obj, "amount", value.amount);
+ // serializeUint256(obj, "amount", value.amount);
+ // serializeBytes32(obj, "topicHash", value.topicHash);
+ // serializeBytes32(obj, "dataHash", value.dataHash);
+ // string memory finalJson = serializeBytes32(
+ // obj,
+ // "eventHash",
+ // value.eventHash
+ // );
+ return vm.serializeString(objectKey, valueKey, finalJson);
+ }
+
+ // function serializeOrderFulfilledEvent(
+ // OrderFulfilledEvent memory value,
+ // string memory objectKey,
+ // string memory valueKey
+ // ) internal returns (string memory) {
+ // string memory obj = string.concat(objectKey, valueKey);
+ // serializeBytes32(obj, "orderHash", value.orderHash);
+ // serializeAddress(obj, "offerer", value.offerer);
+ // serializeAddress(obj, "zone", value.zone);
+ // serializeAddress(obj, "recipient", value.executionState.recipient);
+ // serializeSpentItemArray(obj, "offer", value.offer);
+ // string memory finalJson = serializeReceivedItemArray(
+ // obj,
+ // "consideration",
+ // value.consideration
+ // );
+ // return vm.serializeString(objectKey, valueKey, finalJson);
+ // }
+}
diff --git a/test/foundry/new/helpers/event-utils/ExecutionsFlattener.sol b/test/foundry/new/helpers/event-utils/ExecutionsFlattener.sol
new file mode 100644
index 000000000..c5e425210
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/ExecutionsFlattener.sol
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.13;
+
+import { ArrayHelpers, MemoryPointer } from "seaport-sol/../ArrayHelpers.sol";
+
+import { Execution, ItemType } from "seaport-sol/SeaportStructs.sol";
+import { ExecutionLib } from "seaport-sol/lib/ExecutionLib.sol";
+
+import { FuzzTestContext } from "../FuzzTestContextLib.sol";
+
+library ExecutionsFlattener {
+ using ArrayHelpers for MemoryPointer;
+ using ExecutionsFlattener for *;
+ using ExecutionLib for Execution;
+
+ function flattenExecutions(FuzzTestContext memory context) internal pure {
+ context.expectations.allExpectedExecutions = ArrayHelpers
+ .flattenThree
+ .asExecutionsFlatten()(
+ context.expectations.expectedImplicitPreExecutions,
+ ArrayHelpers.mapWithArg.asMap()(
+ context.expectations.expectedExplicitExecutions,
+ fixExplicitExecution,
+ context
+ ),
+ context.expectations.expectedImplicitPostExecutions
+ );
+ require(
+ context.expectations.allExpectedExecutions.length ==
+ context.expectations.expectedImplicitPreExecutions.length +
+ context.expectations.expectedExplicitExecutions.length +
+ context.expectations.expectedImplicitPostExecutions.length,
+ "LENGTHS OF EXECUTIONS DO NOT MATCH"
+ );
+ uint256 e;
+ for (
+ uint256 i;
+ i < context.expectations.expectedImplicitPreExecutions.length;
+ i++
+ ) {
+ Execution memory execution1 = context
+ .expectations
+ .expectedImplicitPreExecutions[i];
+ Execution memory execution2 = context
+ .expectations
+ .allExpectedExecutions[e++];
+ require(
+ keccak256(abi.encode(execution1)) ==
+ keccak256(abi.encode(execution2)),
+ "IMPLICIT PRE EXECUTIONS DO NOT MATCH"
+ );
+ }
+ for (
+ uint256 i;
+ i < context.expectations.expectedExplicitExecutions.length;
+ i++
+ ) {
+ Execution memory execution1 = context
+ .expectations
+ .expectedExplicitExecutions[i];
+ Execution memory execution2 = context
+ .expectations
+ .allExpectedExecutions[e++];
+ if (execution1.item.itemType == ItemType.NATIVE) {
+ require(
+ execution2.offerer == address(context.seaport),
+ "SEAPORT NOT SET ON EXECUTION"
+ );
+ require(
+ execution1.conduitKey == execution2.conduitKey &&
+ keccak256(abi.encode(execution1.item)) ==
+ keccak256(abi.encode(execution2.item)),
+ "EXPLICIT EXECUTIONS DO NOT MATCH"
+ );
+ } else {
+ require(
+ keccak256(abi.encode(execution1)) ==
+ keccak256(abi.encode(execution2)),
+ "EXPLICIT EXECUTIONS DO NOT MATCH"
+ );
+ }
+ }
+ for (
+ uint256 i;
+ i < context.expectations.expectedImplicitPostExecutions.length;
+ i++
+ ) {
+ Execution memory execution1 = context
+ .expectations
+ .expectedImplicitPostExecutions[i];
+ Execution memory execution2 = context
+ .expectations
+ .allExpectedExecutions[e++];
+ require(
+ keccak256(abi.encode(execution1)) ==
+ keccak256(abi.encode(execution2)),
+ "IMPLICIT PRE EXECUTIONS DO NOT MATCH"
+ );
+ }
+ }
+
+ function fixExplicitExecution(
+ Execution memory execution,
+ FuzzTestContext memory context
+ ) internal pure returns (Execution memory) {
+ if (execution.item.itemType == ItemType.NATIVE) {
+ return execution.copy().withOfferer(address(context.seaport));
+ }
+ return execution;
+ }
+
+ function asMapCallback(
+ function(Execution memory, FuzzTestContext memory)
+ internal
+ pure
+ returns (Execution memory) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(MemoryPointer, MemoryPointer)
+ internal
+ pure
+ returns (MemoryPointer) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+
+ function asMap(
+ function(
+ MemoryPointer,
+ function(MemoryPointer, MemoryPointer)
+ internal
+ pure
+ returns (MemoryPointer),
+ MemoryPointer
+ ) internal pure returns (MemoryPointer) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(
+ Execution[] memory,
+ function(Execution memory, FuzzTestContext memory)
+ internal
+ pure
+ returns (Execution memory),
+ FuzzTestContext memory
+ ) internal pure returns (Execution[] memory) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+
+ function asExecutionsFlatten2(
+ function(MemoryPointer, MemoryPointer)
+ internal
+ view
+ returns (MemoryPointer) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(Execution[] memory, Execution[] memory)
+ internal
+ pure
+ returns (Execution[] memory) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+
+ function asExecutionsFlatten(
+ function(MemoryPointer, MemoryPointer, MemoryPointer)
+ internal
+ view
+ returns (MemoryPointer) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(Execution[] memory, Execution[] memory, Execution[] memory)
+ internal
+ pure
+ returns (Execution[] memory) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/event-utils/ExpectedEventsUtil.sol b/test/foundry/new/helpers/event-utils/ExpectedEventsUtil.sol
new file mode 100644
index 000000000..fa1005e87
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/ExpectedEventsUtil.sol
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { Vm } from "forge-std/Vm.sol";
+
+import {
+ ArrayHelpers,
+ MemoryPointer
+} from "../../../../../contracts/helpers/ArrayHelpers.sol";
+
+import { Execution } from "seaport-sol/SeaportStructs.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import { FuzzTestContext } from "../FuzzTestContextLib.sol";
+
+import { FuzzEngineLib } from "../FuzzEngineLib.sol";
+
+import { FuzzEngine } from "../FuzzEngine.sol";
+
+import { ForgeEventsLib } from "./ForgeEventsLib.sol";
+
+import { TransferEventsLib } from "./TransferEventsLib.sol";
+
+import { OrderFulfilledEventsLib } from "./OrderFulfilledEventsLib.sol";
+
+import { OrdersMatchedEventsLib } from "./OrdersMatchedEventsLib.sol";
+
+import { dumpTransfers } from "../DebugUtil.sol";
+
+bytes32 constant Topic0_ERC20_ERC721_Transfer = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
+bytes32 constant Topic0_ERC1155_TransferSingle = 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
+
+struct ReduceInput {
+ Vm.Log[] logsArray;
+ FuzzTestContext context;
+}
+
+struct Log {
+ bytes32[] topics;
+ bytes data;
+ address emitter;
+}
+
+/**
+ * @dev This library is used to check that the events emitted by tests match the
+ * expected events.
+ */
+library ExpectedEventsUtil {
+ using ArrayHelpers for MemoryPointer;
+ using Casts for *;
+ using ForgeEventsLib for Vm.Log;
+ using ForgeEventsLib for Vm.Log[];
+ using FuzzEngineLib for FuzzTestContext;
+ using OrderFulfilledEventsLib for FuzzTestContext;
+ using OrdersMatchedEventsLib for FuzzTestContext;
+
+ /**
+ * @dev Set up the Vm.
+ */
+
+ Vm private constant vm =
+ Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
+
+ event OrderFulfilled(
+ bytes32 orderHash,
+ address indexed offerer,
+ address indexed zone,
+ address recipient,
+ SpentItem[] offer,
+ ReceivedItem[] consideration
+ );
+
+ event OrdersMatched(bytes32[] orderHashes);
+
+ enum ItemType {
+ NATIVE,
+ ERC20,
+ ERC721,
+ ERC1155,
+ ERC721_WITH_CRITERIA,
+ ERC1155_WITH_CRITERIA
+ }
+
+ struct SpentItem {
+ ItemType itemType;
+ address token;
+ uint256 identifier;
+ uint256 amount;
+ }
+
+ struct ReceivedItem {
+ ItemType itemType;
+ address token;
+ uint256 identifier;
+ uint256 amount;
+ address payable recipient;
+ }
+
+ /**
+ * @dev Sets up the expected event hashes.
+ *
+ * @param context The test context
+ */
+ function setExpectedTransferEventHashes(
+ FuzzTestContext memory context
+ ) internal {
+ Execution[] memory executions = context
+ .expectations
+ .allExpectedExecutions;
+ require(
+ executions.length ==
+ context.expectations.expectedImplicitPreExecutions.length +
+ context.expectations.expectedExplicitExecutions.length +
+ context.expectations.expectedImplicitPostExecutions.length,
+ "ExpectedEventsUtil: executions length mismatch"
+ );
+
+ context.expectations.expectedTransferEventHashes = ArrayHelpers
+ .filterMapWithArg
+ .asExecutionsFilterMap()(
+ executions,
+ TransferEventsLib.getTransferEventHash,
+ context
+ );
+
+ vm.serializeBytes32(
+ "root",
+ "expectedTransferEventHashes",
+ context.expectations.expectedTransferEventHashes
+ );
+ }
+
+ function setExpectedSeaportEventHashes(
+ FuzzTestContext memory context
+ ) internal {
+ bool isMatch = context.action() ==
+ context.seaport.matchAdvancedOrders.selector ||
+ context.action() == context.seaport.matchOrders.selector;
+
+ uint256 totalExpectedEventHashes = isMatch ? 1 : 0;
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ ++totalExpectedEventHashes;
+ }
+ }
+
+ context.expectations.expectedSeaportEventHashes = new bytes32[](
+ totalExpectedEventHashes
+ );
+
+ totalExpectedEventHashes = 0;
+ for (uint256 i = 0; i < context.executionState.orders.length; ++i) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ context.expectations.expectedSeaportEventHashes[
+ totalExpectedEventHashes++
+ ] = context.getOrderFulfilledEventHash(i);
+ }
+ }
+
+ if (isMatch) {
+ context.expectations.expectedSeaportEventHashes[
+ totalExpectedEventHashes
+ ] = context.getOrdersMatchedEventHash();
+ }
+
+ vm.serializeBytes32(
+ "root",
+ "expectedSeaportEventHashes",
+ context.expectations.expectedSeaportEventHashes
+ );
+ }
+
+ /**
+ * @dev Starts recording logs.
+ */
+ function startRecordingLogs() internal {
+ vm.recordLogs();
+ }
+
+ /**
+ * @dev Checks that the events emitted by the test match the expected
+ * events.
+ *
+ * @param context The test context
+ */
+ function checkExpectedTransferEvents(
+ FuzzTestContext memory context
+ ) internal {
+ Vm.Log[] memory logs = vm.getRecordedLogs();
+ bytes memory callData = abi.encodeCall(FuzzEngine.setLogs, (logs));
+ (bool ok, ) = address(this).call(callData);
+ if (!ok) {
+ revert("ExpectedEventsUtil: log registration failed");
+ }
+
+ // MemoryPointer expectedEvents = toMemoryPointer(eventHashes);
+ bytes32[] memory expectedTransferEventHashes = context
+ .expectations
+ .expectedTransferEventHashes;
+
+ // For each expected event, verify that it matches the next log
+ // in `logs` that has a topic0 matching one of the watched events.
+ uint256 lastLogIndex = ArrayHelpers.reduceWithArg.asLogsReduce()(
+ expectedTransferEventHashes,
+ checkNextTransferEvent, // function called for each item in expectedEvents
+ 0, // initial value for the reduce call, index 0
+ ReduceInput(logs, context) // 3rd argument given to checkNextTransferEvent
+ );
+
+ // Verify that there are no other watched events in the array
+ int256 nextWatchedEventIndex = ArrayHelpers
+ .findIndexFrom
+ .asLogsFindIndex()(logs, isWatchedTransferEvent, lastLogIndex);
+
+ if (nextWatchedEventIndex != -1) {
+ dumpTransfers(context);
+ revert(
+ "ExpectedEvents: too many watched transfer events - info written to fuzz_debug.json"
+ );
+ }
+ }
+
+ function checkExpectedSeaportEvents(
+ FuzzTestContext memory context
+ ) internal {
+ // TODO: set these upstream (this expects checkExpectedTransferEvents to run first)
+ bytes memory callData = abi.encodeCall(FuzzEngine.getLogs, ());
+ (, bytes memory returnData) = address(this).call(callData);
+ Log[] memory rawLogs = abi.decode(returnData, (Log[]));
+
+ Vm.Log[] memory logs = new Vm.Log[](rawLogs.length);
+
+ for (uint256 i = 0; i < logs.length; ++i) {
+ Vm.Log memory log = logs[i];
+ Log memory rawLog = rawLogs[i];
+
+ log.topics = rawLog.topics;
+ log.data = rawLog.data;
+ log.emitter = rawLog.emitter;
+ }
+
+ // MemoryPointer expectedEvents = toMemoryPointer(eventHashes);
+ bytes32[] memory expectedSeaportEventHashes = context
+ .expectations
+ .expectedSeaportEventHashes;
+
+ // For each expected event, verify that it matches the next log
+ // in `logs` that has a topic0 matching one of the watched events.
+ uint256 lastLogIndex = ArrayHelpers.reduceWithArg.asLogsReduce()(
+ expectedSeaportEventHashes,
+ checkNextSeaportEvent, // function called for each item in expectedEvents
+ 0, // initial value for the reduce call, index 0
+ ReduceInput(logs, context) // 3rd argument given to checkNextSeaportEvent
+ );
+
+ // Verify that there are no other watched events in the array
+ int256 nextWatchedEventIndex = ArrayHelpers
+ .findIndexFrom
+ .asLogsFindIndex()(logs, isWatchedSeaportEvent, lastLogIndex);
+
+ if (nextWatchedEventIndex != -1) {
+ revert("ExpectedEvents: too many watched seaport events");
+ }
+ }
+
+ /**
+ * @dev This function defines which logs matter for the sake of the fuzz
+ * tests. This is called for every log emitted during a test run. If
+ * it returns true, `checkNextEvent` will assert that the log matches
+ * the next expected event recorded in the test context.
+ *
+ * @param log The log to check
+ *
+ * @return True if the log is a watched event, false otherwise
+ */
+ function isWatchedTransferEvent(
+ Vm.Log memory log
+ ) internal pure returns (bool) {
+ bytes32 topic0 = log.getTopic0();
+ return
+ topic0 == Topic0_ERC20_ERC721_Transfer ||
+ topic0 == Topic0_ERC1155_TransferSingle;
+ }
+
+ function isWatchedSeaportEvent(
+ Vm.Log memory log
+ ) internal pure returns (bool) {
+ bytes32 topic0 = log.getTopic0();
+ return
+ topic0 == OrderFulfilled.selector ||
+ topic0 == OrdersMatched.selector;
+ }
+
+ /**
+ * @dev Checks that the next log matches the next expected transfer event.
+ *
+ * @param lastLogIndex The index of the last log that was checked
+ * @param expectedEventHash The expected event hash
+ * @param input The input to the reduce function
+ *
+ * @return nextLogIndex The index of the next log to check
+ */
+ function checkNextTransferEvent(
+ uint256 lastLogIndex,
+ uint256 expectedEventHash,
+ ReduceInput memory input
+ ) internal returns (uint256 nextLogIndex) {
+ // Get the index of the next watched event in the logs array
+ int256 nextWatchedEventIndex = ArrayHelpers
+ .findIndexFrom
+ .asLogsFindIndex()(
+ input.logsArray,
+ isWatchedTransferEvent,
+ lastLogIndex
+ );
+
+ // Dump the events data and revert if there are no remaining transfer events
+ if (nextWatchedEventIndex == -1) {
+ vm.serializeUint("root", "failingIndex", lastLogIndex - 1);
+ vm.serializeBytes32(
+ "root",
+ "expectedEventHash",
+ bytes32(expectedEventHash)
+ );
+ dumpTransfers(input.context);
+ revert(
+ "ExpectedEvents: transfer event not found - info written to fuzz_debug.json"
+ );
+ }
+
+ require(
+ nextWatchedEventIndex != -1,
+ "ExpectedEvents: transfer event not found"
+ );
+
+ // Verify that the transfer event matches the expected event
+ uint256 i = uint256(nextWatchedEventIndex);
+ Vm.Log memory log = input.logsArray[i];
+ require(
+ log.getForgeEventHash() == bytes32(expectedEventHash),
+ "ExpectedEvents: transfer event hash does not match"
+ );
+
+ // Increment the log index for the next iteration
+ return i + 1;
+ }
+
+ /**
+ * @dev Checks that the next log matches the next expected event.
+ *
+ * @param lastLogIndex The index of the last log that was checked
+ * @param expectedEventHash The expected event hash
+ * @param input The input to the reduce function
+ *
+ * @return nextLogIndex The index of the next log to check
+ */
+ function checkNextSeaportEvent(
+ uint256 lastLogIndex,
+ uint256 expectedEventHash,
+ ReduceInput memory input
+ ) internal returns (uint256 nextLogIndex) {
+ // Get the index of the next watched event in the logs array
+ int256 nextWatchedEventIndex = ArrayHelpers
+ .findIndexFrom
+ .asLogsFindIndex()(
+ input.logsArray,
+ isWatchedSeaportEvent,
+ lastLogIndex
+ );
+
+ // Dump the events data and revert if there are no remaining transfer events
+ if (nextWatchedEventIndex == -1) {
+ vm.serializeUint("root", "failingIndex", lastLogIndex - 1);
+ vm.serializeBytes32(
+ "root",
+ "expectedEventHash",
+ bytes32(expectedEventHash)
+ );
+ revert(
+ "ExpectedEvents: seaport event not found - info written to fuzz_debug.json"
+ );
+ }
+
+ require(
+ nextWatchedEventIndex != -1,
+ "ExpectedEvents: seaport event not found"
+ );
+
+ // Verify that the transfer event matches the expected event
+ uint256 i = uint256(nextWatchedEventIndex);
+ Vm.Log memory log = input.logsArray[i];
+ require(
+ log.getForgeEventHash() == bytes32(expectedEventHash),
+ "ExpectedEvents: seaport event hash does not match"
+ );
+
+ // Increment the log index for the next iteration
+ return i + 1;
+ }
+}
+
+/**
+ * @dev Low level helpers.
+ */
+library Casts {
+ function asLogsFindIndex(
+ function(
+ MemoryPointer,
+ function(MemoryPointer) internal pure returns (bool),
+ uint256
+ ) internal pure returns (int256) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(
+ Vm.Log[] memory,
+ function(Vm.Log memory) internal pure returns (bool),
+ uint256
+ ) internal pure returns (int256) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+
+ function asLogsReduce(
+ function(
+ MemoryPointer,
+ function(uint256, uint256, MemoryPointer)
+ internal
+ returns (uint256),
+ uint256,
+ MemoryPointer
+ ) internal returns (uint256) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(
+ bytes32[] memory,
+ function(
+ uint256,
+ uint256,
+ ReduceInput memory //Vm.Log[] memory)
+ ) internal returns (uint256),
+ uint256,
+ ReduceInput memory //Vm.Log[] memory
+ ) internal returns (uint256) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+
+ function asExecutionsFilterMap(
+ function(
+ MemoryPointer,
+ function(MemoryPointer, MemoryPointer)
+ internal
+ pure
+ returns (MemoryPointer),
+ MemoryPointer
+ ) internal pure returns (MemoryPointer) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(
+ Execution[] memory,
+ function(Execution memory, FuzzTestContext memory)
+ internal
+ returns (bytes32),
+ FuzzTestContext memory
+ ) internal pure returns (bytes32[] memory) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+
+ function toMemoryPointer(
+ Execution[] memory arr
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := arr
+ }
+ }
+
+ function toMemoryPointer(
+ FuzzTestContext memory context
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := context
+ }
+ }
+
+ function toMemoryPointer(
+ Vm.Log[] memory arr
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := arr
+ }
+ }
+
+ function toMemoryPointer(
+ bytes32[] memory arr
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := arr
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/event-utils/ForgeEventsLib.sol b/test/foundry/new/helpers/event-utils/ForgeEventsLib.sol
new file mode 100644
index 000000000..48aa5e61f
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/ForgeEventsLib.sol
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol";
+
+import { Vm } from "forge-std/Vm.sol";
+
+import {
+ MemoryPointer
+} from "../../../../../contracts/helpers/PointerLibraries.sol";
+
+import { getEventHash, getTopicsHash } from "./EventHashes.sol";
+
+import {
+ ERC20TransferEvent,
+ ERC721TransferEvent,
+ ERC1155TransferEvent,
+ EventSerializer,
+ vm
+} from "./EventSerializer.sol";
+
+bytes32 constant Topic0_ERC20_ERC721_Transfer = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
+bytes32 constant Topic0_ERC1155_TransferSingle = 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
+
+library ForgeEventsLib {
+ using { ifTrue } for bytes32;
+ using EventSerializer for *;
+
+ /**
+ * @dev Returns the hash of the event emitted by Forge.
+ */
+ function getForgeEventHash(
+ Vm.Log memory log
+ ) internal pure returns (bytes32) {
+ bytes32 topicsHash = getForgeTopicsHash(log);
+ bytes32 dataHash = getDataHash(log);
+ return getEventHash(log.emitter, topicsHash, dataHash);
+ }
+
+ /**
+ * @dev Returns the memory pointer for a given log.
+ */
+ function toMemoryPointer(
+ Vm.Log memory log
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := log
+ }
+ }
+
+ /**
+ * @dev Returns the hash of the data on the event.
+ */
+ function getDataHash(Vm.Log memory log) internal pure returns (bytes32) {
+ return keccak256(log.data);
+ // MemoryPointer data = toMemoryPointer(log).pptr(32);
+ // return data.offset(32).hash(data.readUint256());
+ }
+
+ /**
+ * @dev Gets the first topic of the log.
+ */
+ function getTopic0(Vm.Log memory log) internal pure returns (bytes32) {
+ MemoryPointer topics = toMemoryPointer(log).pptr();
+ uint256 topicsCount = topics.readUint256();
+ return topics.offset(0x20).readBytes32().ifTrue(topicsCount > 0);
+ }
+
+ /**
+ * @dev Emits a log again.
+ */
+ function reEmit(Vm.Log memory log) internal {
+ MemoryPointer topics = toMemoryPointer(log).pptr();
+ uint256 topicsCount = topics.readUint256();
+ (
+ bytes32 topic0,
+ ,
+ bytes32 topic1,
+ ,
+ bytes32 topic2,
+ ,
+ bytes32 topic3,
+
+ ) = getTopics(log);
+ MemoryPointer data = toMemoryPointer(log).pptr(32);
+ assembly {
+ switch topicsCount
+ case 4 {
+ log4(data, mload(data), topic0, topic1, topic2, topic3)
+ }
+ case 3 {
+ log3(data, mload(data), topic0, topic1, topic2)
+ }
+ case 2 {
+ log2(data, mload(data), topic0, topic1)
+ }
+ case 1 {
+ log1(data, mload(data), topic0)
+ }
+ default {
+ log0(data, mload(data))
+ }
+ }
+ }
+
+ /**
+ * @dev Serializes a token transfer log for an ERC20, ERC721, or ERC1155.
+ */
+ function serializeTransferLog(
+ Vm.Log memory log,
+ string memory objectKey,
+ string memory valueKey
+ ) internal returns (string memory) {
+ (
+ bytes32 topic0,
+ ,
+ bytes32 topic1,
+ ,
+ bytes32 topic2,
+ ,
+ bytes32 topic3,
+ bool hasTopic3
+ ) = getTopics(log);
+ if (topic0 == Topic0_ERC20_ERC721_Transfer) {
+ if (hasTopic3) {
+ return
+ ERC721TransferEvent(
+ "ERC721",
+ log.emitter,
+ address(uint160(uint256(topic1))),
+ address(uint160(uint256(topic2))),
+ uint256(topic3)
+ // getForgeTopicsHash(log),
+ // getDataHash(log),
+ // getForgeEventHash(log)
+ ).serializeERC721TransferEvent(objectKey, valueKey);
+ } else {
+ ERC20TransferEvent memory eventData;
+ eventData.kind = "ERC20";
+ eventData.token = log.emitter;
+ eventData.from = address(uint160(uint256(topic1)));
+ eventData.to = address(uint160(uint256(topic2)));
+ eventData.amount = log.data.length >= 32
+ ? abi.decode(log.data, (uint256))
+ : 0;
+ if (log.data.length == 0) {
+ string memory obj = string.concat(objectKey, valueKey);
+ string memory finalJson = vm.serializeString(
+ obj,
+ "amount",
+ "No data provided in log for token amount"
+ );
+ vm.serializeString(objectKey, valueKey, finalJson);
+ }
+ return
+ eventData.serializeERC20TransferEvent(objectKey, valueKey);
+ }
+ } else if (topic0 == Topic0_ERC1155_TransferSingle) {
+ ERC1155TransferEvent memory eventData;
+ eventData.kind = "ERC1155";
+ eventData.token = log.emitter;
+ eventData.operator = address(uint160(uint256(topic1)));
+ eventData.from = address(uint160(uint256(topic2)));
+ eventData.to = address(uint160(uint256(topic3)));
+ (eventData.identifier, eventData.amount) = abi.decode(
+ log.data,
+ (uint256, uint256)
+ );
+ // eventData.topicHash = getForgeTopicsHash(log);
+ // eventData.dataHash = getDataHash(log);
+ // eventData.eventHash = getForgeEventHash(log);
+
+ return eventData.serializeERC1155TransferEvent(objectKey, valueKey);
+ }
+
+ revert("Invalid event log");
+ }
+
+ /**
+ * @dev Serializes an array of token transfer logs.
+ */
+ function serializeTransferLogs(
+ Vm.Log[] memory value,
+ string memory objectKey,
+ string memory valueKey
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ string memory _log = serializeTransferLog(
+ value[i],
+ obj,
+ string.concat("event", vm.toString(i))
+ );
+ uint256 len;
+ assembly {
+ len := mload(_log)
+ }
+ if (length > 0) {
+ out = _log;
+ }
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ /**
+ * @dev Gets the topics for a log.
+ */
+ function getTopics(
+ Vm.Log memory log
+ )
+ internal
+ pure
+ returns (
+ bytes32 topic0,
+ bool hasTopic0,
+ bytes32 topic1,
+ bool hasTopic1,
+ bytes32 topic2,
+ bool hasTopic2,
+ bytes32 topic3,
+ bool hasTopic3
+ )
+ {
+ MemoryPointer topics = toMemoryPointer(log).pptr();
+ uint256 topicsCount = topics.readUint256();
+ hasTopic0 = topicsCount > 0;
+ topic0 = topics.offset(0x20).readBytes32().ifTrue(hasTopic0);
+
+ hasTopic1 = topicsCount > 1;
+ topic1 = topics.offset(0x40).readBytes32().ifTrue(hasTopic1);
+
+ hasTopic2 = topicsCount > 2;
+ topic2 = topics.offset(0x60).readBytes32().ifTrue(hasTopic2);
+
+ hasTopic3 = topicsCount > 3;
+ topic3 = topics.offset(0x80).readBytes32().ifTrue(hasTopic3);
+ }
+
+ /**
+ * @dev Gets the hash for a log's topics.
+ */
+ function getForgeTopicsHash(
+ Vm.Log memory log
+ ) internal pure returns (bytes32 topicHash) {
+ // (
+ // bytes32 topic0,
+ // bool hasTopic0,
+ // bytes32 topic1,
+ // bool hasTopic1,
+ // bytes32 topic2,
+ // bool hasTopic2,
+ // bytes32 topic3,
+ // bool hasTopic3
+ // ) = getTopics(log);
+ return keccak256(abi.encodePacked(log.topics));
+ }
+}
+
+/**
+ * @dev Convenience helper.
+ */
+function ifTrue(bytes32 a, bool b) pure returns (bytes32 c) {
+ assembly {
+ c := mul(a, b)
+ }
+}
diff --git a/test/foundry/new/helpers/event-utils/OrderFulfilledEventsLib.sol b/test/foundry/new/helpers/event-utils/OrderFulfilledEventsLib.sol
new file mode 100644
index 000000000..2f0303b1f
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/OrderFulfilledEventsLib.sol
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ MemoryPointer
+} from "../../../../../contracts/helpers/ArrayHelpers.sol";
+
+import {
+ AdvancedOrder,
+ Execution,
+ ItemType,
+ OrderParameters,
+ SpentItem,
+ ReceivedItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import {
+ OrderDetails
+} from "../../../../../contracts/helpers/sol/fulfillments/lib/Structs.sol";
+
+import { FuzzTestContext } from "../FuzzTestContextLib.sol";
+
+import { getEventHashWithTopics, getTopicsHash } from "./EventHashes.sol";
+
+import {
+ AdvancedOrderLib
+} from "../../../../../contracts/helpers/sol/lib/AdvancedOrderLib.sol";
+
+import {
+ OrderParametersLib
+} from "../../../../../contracts/helpers/sol/lib/OrderParametersLib.sol";
+
+import {
+ OrderFulfilledEvent,
+ EventSerializer,
+ vm
+} from "./EventSerializer.sol";
+
+library OrderFulfilledEventsLib {
+ using { toBytes32 } for address;
+ using EventSerializer for *;
+ using AdvancedOrderLib for AdvancedOrder[];
+ using OrderParametersLib for OrderParameters;
+
+ event OrderFulfilled(
+ bytes32 orderHash,
+ address indexed offerer,
+ address indexed zone,
+ address recipient,
+ SpentItem[] offer,
+ ReceivedItem[] consideration
+ );
+
+ // check if order is available - expected available orders
+ // look up actions, if match
+ // create new lib for order fulfilled/match
+ // DON"T TOUCH anything related to transfer events
+ // function serializeOrderFulfilledLog(
+ // string memory objectKey,
+ // string memory valueKey,
+ // FuzzTestContext memory context
+ // ) internal returns (string memory) {
+ // OrderDetails[] memory orderDetails = (
+ // context.executionState.orders.getOrderDetails(context.executionState.criteriaResolvers)
+ // );
+
+ // for (uint256 i; i < orderDetails.length; i++) {
+ // OrderDetails memory detail = orderDetails[i];
+
+ // OrderFulfilledEvent memory eventData = OrderFulfilledEvent({
+ // orderHash: getOrderFulfilledEventHash(context),
+ // offerer: detail.offerer,
+ // zone: detail.zone,
+ // recipient: detail.recipient,
+ // offer: detail.offer,
+ // consideration: detail.consideration
+ // });
+
+ // return eventData.serializeOrderFulfilledEvent(objectKey, valueKey);
+ // }
+ // }
+
+ function getOrderFulfilledEventHash(
+ FuzzTestContext memory context,
+ uint256 orderIndex
+ ) internal pure returns (bytes32 eventHash) {
+ OrderParameters memory orderParams = context
+ .executionState
+ .orders[orderIndex]
+ .parameters;
+
+ OrderDetails memory details = (
+ context.executionState.orderDetails[orderIndex]
+ );
+
+ return
+ getEventHashWithTopics(
+ address(context.seaport), // emitter
+ OrderFulfilled.selector, // topic0
+ orderParams.offerer.toBytes32(), // topic1 - offerer
+ orderParams.zone.toBytes32(), // topic2 - zone
+ keccak256(
+ abi.encode(
+ details.orderHash,
+ context.executionState.recipient == address(0)
+ ? context.executionState.caller
+ : context.executionState.recipient,
+ details.offer,
+ details.consideration
+ )
+ ) // dataHash
+ );
+ }
+}
+
+/**
+ * @dev Low level helper to cast an address to a bytes32.
+ */
+function toBytes32(address a) pure returns (bytes32 b) {
+ assembly {
+ b := a
+ }
+}
diff --git a/test/foundry/new/helpers/event-utils/OrdersMatchedEventsLib.sol b/test/foundry/new/helpers/event-utils/OrdersMatchedEventsLib.sol
new file mode 100644
index 000000000..4e6677d64
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/OrdersMatchedEventsLib.sol
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { FuzzTestContext } from "../FuzzTestContextLib.sol";
+
+import { getEventHashWithTopics } from "./EventHashes.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+library OrdersMatchedEventsLib {
+ event OrdersMatched(bytes32[] orderHashes);
+
+ function getOrdersMatchedEventHash(
+ FuzzTestContext memory context
+ ) internal pure returns (bytes32 eventHash) {
+ uint256 totalAvailableOrders = 0;
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ ++totalAvailableOrders;
+ }
+ }
+
+ bytes32[] memory orderHashes = new bytes32[](totalAvailableOrders);
+
+ totalAvailableOrders = 0;
+ for (
+ uint256 i = 0;
+ i < context.executionState.orderDetails.length;
+ ++i
+ ) {
+ if (
+ context.executionState.orderDetails[i].unavailableReason ==
+ UnavailableReason.AVAILABLE
+ ) {
+ orderHashes[totalAvailableOrders++] = context
+ .executionState
+ .orderDetails[i]
+ .orderHash;
+ }
+ }
+
+ return
+ getEventHashWithTopics(
+ address(context.seaport), // emitter
+ OrdersMatched.selector, // topic0
+ keccak256(abi.encode(orderHashes)) // dataHash
+ );
+ }
+}
diff --git a/test/foundry/new/helpers/event-utils/TransferEventsLib.sol b/test/foundry/new/helpers/event-utils/TransferEventsLib.sol
new file mode 100644
index 000000000..dbf11d9fd
--- /dev/null
+++ b/test/foundry/new/helpers/event-utils/TransferEventsLib.sol
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ MemoryPointer
+} from "../../../../../contracts/helpers/ArrayHelpers.sol";
+
+import {
+ Execution,
+ ItemType,
+ ReceivedItem
+} from "seaport-sol/SeaportStructs.sol";
+
+import { FuzzTestContext } from "../FuzzTestContextLib.sol";
+
+import { getEventHashWithTopics, getTopicsHash } from "./EventHashes.sol";
+
+import {
+ ERC20TransferEvent,
+ ERC721TransferEvent,
+ ERC1155TransferEvent,
+ EventSerializer,
+ vm
+} from "./EventSerializer.sol";
+
+library TransferEventsLib {
+ using { toBytes32 } for address;
+ using TransferEventsLibCasts for *;
+ using EventSerializer for *;
+
+ // ERC721 and ERC20 share the same topic0 for the Transfer event, but
+ // for ERC721, the third parameter (identifier) is indexed.
+ // The topic0 does not change based on which parameters are indexed.
+ event Transfer(
+ address indexed from,
+ address indexed to,
+ uint256 valueOrIdentifier
+ );
+
+ event TransferSingle(
+ address indexed operator,
+ address indexed from,
+ address indexed to,
+ uint256 id,
+ uint256 value
+ );
+
+ /**
+ * @dev Serializes a token transfer log for an ERC20, ERC721, or ERC1155.
+ *
+ * @param execution The execution that corresponds to the transfer event.
+ * @param objectKey The key to use for the object in the JSON.
+ * @param valueKey The key to use for the value in the JSON.
+ * @param context The context of the fuzz test.
+ *
+ * @return json The json for the event.
+ */
+ function serializeTransferLog(
+ Execution memory execution,
+ string memory objectKey,
+ string memory valueKey,
+ FuzzTestContext memory context
+ ) internal returns (string memory json) {
+ ItemType itemType = execution.item.itemType;
+
+ if (itemType == ItemType.ERC20) {
+ ReceivedItem memory item = execution.item;
+
+ ERC20TransferEvent memory eventData = ERC20TransferEvent(
+ "ERC20",
+ item.token,
+ execution.offerer,
+ address(item.recipient),
+ item.amount
+ );
+ return eventData.serializeERC20TransferEvent(objectKey, valueKey);
+ }
+
+ if (itemType == ItemType.ERC721) {
+ ReceivedItem memory item = execution.item;
+
+ return
+ ERC721TransferEvent(
+ "ERC721",
+ item.token,
+ execution.offerer,
+ address(item.recipient),
+ item.identifier
+ // getTopicsHash(
+ // Transfer.selector, // topic0
+ // execution.offerer.toBytes32(), // topic1
+ // toBytes32(item.recipient), // topic2
+ // bytes32(item.identifier) // topic3
+ // ),
+ // keccak256(""),
+ // getERC721TransferEventHash(execution)
+ ).serializeERC721TransferEvent(objectKey, valueKey);
+ }
+ if (itemType == ItemType.ERC1155) {
+ ReceivedItem memory item = execution.item;
+ address operator = _getConduit(execution.conduitKey, context);
+
+ ERC1155TransferEvent memory eventData = ERC1155TransferEvent(
+ "ERC1155",
+ item.token,
+ operator,
+ execution.offerer,
+ address(item.recipient),
+ item.identifier,
+ item.amount
+ // getTopicsHash(
+ // TransferSingle.selector, // topic0
+ // _getConduit(execution.conduitKey, context).toBytes32(), // topic1 = operator
+ // execution.offerer.toBytes32(), // topic2 = from
+ // toBytes32(item.recipient) // topic3 = to
+ // ),
+ // keccak256(abi.encode(item.identifier, item.amount)), // dataHash
+ // getERC1155TransferEventHash(execution, context) // event hash
+ );
+
+ return eventData.serializeERC1155TransferEvent(objectKey, valueKey);
+ }
+
+ revert("Invalid event log");
+ }
+
+ /**
+ * @dev Serializes an array of token transfer logs.
+ */
+ function serializeTransferLogs(
+ Execution[] memory value,
+ string memory objectKey,
+ string memory valueKey,
+ FuzzTestContext memory context
+ ) internal returns (string memory) {
+ string memory obj = string.concat(objectKey, valueKey);
+ uint256 length = value.length;
+ string memory out;
+ for (uint256 i; i < length; i++) {
+ string memory _log = serializeTransferLog(
+ value[i],
+ obj,
+ string.concat("event", vm.toString(i)),
+ context
+ );
+ uint256 len;
+ assembly {
+ len := mload(_log)
+ }
+ if (length > 0) {
+ out = _log;
+ }
+ }
+ return vm.serializeString(objectKey, valueKey, out);
+ }
+
+ function getTransferEventHash(
+ Execution memory execution,
+ FuzzTestContext memory context
+ ) internal view returns (bytes32 eventHash) {
+ ItemType itemType = execution.item.itemType;
+
+ if (itemType == ItemType.ERC20) {
+ // ReceivedItem memory item = execution.item;
+ return getERC20TransferEventHash(execution);
+ }
+
+ if (itemType == ItemType.ERC721) {
+ // ReceivedItem memory item = execution.item;
+ return getERC721TransferEventHash(execution);
+ }
+ if (itemType == ItemType.ERC1155) {
+ // ReceivedItem memory item = execution.item;
+ // address operator = _getConduit(execution.conduitKey, context);
+ return getERC1155TransferEventHash(execution, context);
+ }
+ }
+
+ function getERC20TransferEventHash(
+ Execution memory execution
+ ) internal pure returns (bytes32) {
+ ReceivedItem memory item = execution.item;
+ return
+ getEventHashWithTopics(
+ item.token, // emitter
+ Transfer.selector, // topic0
+ execution.offerer.toBytes32(), // topic1
+ toBytes32(item.recipient), // topic2
+ keccak256(abi.encode(item.amount)) // dataHash
+ );
+ }
+
+ function getERC721TransferEventHash(
+ Execution memory execution
+ ) internal pure returns (bytes32) {
+ ReceivedItem memory item = execution.item;
+ return
+ getEventHashWithTopics(
+ item.token, // emitter
+ Transfer.selector, // topic0
+ execution.offerer.toBytes32(), // topic1
+ toBytes32(item.recipient), // topic2
+ bytes32(item.identifier), // topic3
+ keccak256("") // dataHash
+ );
+ }
+
+ function getERC1155TransferEventHash(
+ Execution memory execution,
+ FuzzTestContext memory context
+ ) internal view returns (bytes32) {
+ ReceivedItem memory item = execution.item;
+ return
+ getEventHashWithTopics(
+ item.token, // emitter
+ TransferSingle.selector, // topic0
+ _getConduit(execution.conduitKey, context).toBytes32(), // topic1 = operator
+ execution.offerer.toBytes32(), // topic2 = from
+ toBytes32(item.recipient), // topic3 = to
+ keccak256(abi.encode(item.identifier, item.amount)) // dataHash
+ );
+ }
+
+ function _getConduit(
+ bytes32 conduitKey,
+ FuzzTestContext memory context
+ ) internal view returns (address) {
+ if (conduitKey == bytes32(0)) return address(context.seaport);
+ (address conduit, bool exists) = context.conduitController.getConduit(
+ conduitKey
+ );
+ if (exists) return conduit;
+ revert("TransferEventsLib: bad conduit key");
+ }
+}
+
+/**
+ * @dev Low level helper to cast an address to a bytes32.
+ */
+function toBytes32(address a) pure returns (bytes32 b) {
+ assembly {
+ b := a
+ }
+}
+
+/**
+ * @dev Low level helper.
+ */
+library TransferEventsLibCasts {
+ function cast(
+ function(
+ MemoryPointer,
+ function(MemoryPointer, MemoryPointer)
+ internal
+ pure
+ returns (MemoryPointer),
+ MemoryPointer
+ ) internal pure returns (MemoryPointer) fnIn
+ )
+ internal
+ pure
+ returns (
+ function(
+ Execution[] memory,
+ function(Execution memory, FuzzTestContext memory)
+ internal
+ view
+ returns (bytes32),
+ FuzzTestContext memory
+ ) internal pure returns (bytes32[] memory) fnOut
+ )
+ {
+ assembly {
+ fnOut := fnIn
+ }
+ }
+}
diff --git a/test/foundry/new/helpers/sol/FulfillAvailableHelper.t.sol b/test/foundry/new/helpers/sol/FulfillAvailableHelper.t.sol
new file mode 100644
index 000000000..be3603798
--- /dev/null
+++ b/test/foundry/new/helpers/sol/FulfillAvailableHelper.t.sol
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import {
+ ConsiderationItemLib,
+ OfferItemLib,
+ OrderParametersLib,
+ SeaportArrays
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ ConsiderationItem,
+ OfferItem,
+ FulfillmentComponent,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ FulfillAvailableHelper
+} from "seaport-sol/fulfillments/available/FulfillAvailableHelper.sol";
+
+contract FulfillAvailableHelperTest is Test {
+ using ConsiderationItemLib for ConsiderationItem;
+ using OfferItemLib for OfferItem;
+ using OrderParametersLib for OrderParameters;
+
+ FulfillAvailableHelper test;
+
+ function setUp() public {
+ test = new FulfillAvailableHelper();
+ }
+
+ function testNaive() public {
+ OrderParameters memory orderParameters = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1234)),
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(5678)
+ )
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1234)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(5678)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(9101112))
+ )
+ );
+
+ (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ ) = test.getNaiveFulfillmentComponents(
+ SeaportArrays.OrderParametersArray(orderParameters)
+ );
+
+ assertEq(offer.length, 2);
+ assertEq(offer[0].length, 1);
+ assertEq(offer[0][0].orderIndex, 0);
+ assertEq(offer[0][0].itemIndex, 0);
+ assertEq(offer[1].length, 1);
+ assertEq(offer[1][0].orderIndex, 0);
+ assertEq(offer[1][0].itemIndex, 1);
+ assertEq(consideration.length, 3);
+ assertEq(consideration[0].length, 1);
+ assertEq(consideration[0][0].orderIndex, 0);
+ assertEq(consideration[0][0].itemIndex, 0);
+ assertEq(consideration[1].length, 1);
+ assertEq(consideration[1][0].orderIndex, 0);
+ assertEq(consideration[1][0].itemIndex, 1);
+ assertEq(consideration[2].length, 1);
+ assertEq(consideration[2][0].orderIndex, 0);
+ assertEq(consideration[2][0].itemIndex, 2);
+
+ OrderParameters memory parameters2 = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1235)),
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(5679)
+ ),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(9101113))
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1235)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(5679))
+ )
+ );
+
+ (offer, consideration) = test.getNaiveFulfillmentComponents(
+ SeaportArrays.OrderParametersArray(orderParameters, parameters2)
+ );
+ assertEq(offer.length, 5);
+ assertEq(offer[0].length, 1);
+ assertEq(offer[0][0].orderIndex, 0);
+ assertEq(offer[0][0].itemIndex, 0);
+ assertEq(offer[1].length, 1);
+ assertEq(offer[1][0].orderIndex, 0);
+ assertEq(offer[1][0].itemIndex, 1);
+ assertEq(offer[2].length, 1);
+ assertEq(offer[2][0].orderIndex, 1);
+ assertEq(offer[2][0].itemIndex, 0);
+ assertEq(offer[3].length, 1);
+ assertEq(offer[3][0].orderIndex, 1);
+ assertEq(offer[3][0].itemIndex, 1);
+ assertEq(offer[4].length, 1);
+ assertEq(offer[4][0].orderIndex, 1);
+ assertEq(offer[4][0].itemIndex, 2);
+ assertEq(consideration.length, 5);
+ assertEq(consideration[0].length, 1);
+ assertEq(consideration[0][0].orderIndex, 0);
+ assertEq(consideration[0][0].itemIndex, 0);
+ assertEq(consideration[1].length, 1);
+ assertEq(consideration[1][0].orderIndex, 0);
+ assertEq(consideration[1][0].itemIndex, 1);
+ assertEq(consideration[2].length, 1);
+ assertEq(consideration[2][0].orderIndex, 0);
+ assertEq(consideration[2][0].itemIndex, 2);
+ assertEq(consideration[3].length, 1);
+ assertEq(consideration[3][0].orderIndex, 1);
+ assertEq(consideration[3][0].itemIndex, 0);
+ assertEq(consideration[4].length, 1);
+ assertEq(consideration[4][0].orderIndex, 1);
+ assertEq(consideration[4][0].itemIndex, 1);
+ }
+
+ function testAggregated_single() public {
+ OrderParameters memory parameters = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ ),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1235)),
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ )
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1234)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ );
+
+ (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ ) = test.getAggregatedFulfillmentComponents(
+ SeaportArrays.OrderParametersArray(parameters)
+ );
+ assertEq(offer.length, 2, "offer length incorrect");
+ assertEq(offer[0].length, 2, "offer index 0 length incorrect");
+ assertEq(
+ offer[0][0].orderIndex,
+ 0,
+ "offer index 0 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[0][0].itemIndex,
+ 0,
+ "offer index 0 index 0 item index incorrect"
+ );
+ assertEq(
+ offer[0][1].orderIndex,
+ 0,
+ "offer index 0 index 1 order index incorrect"
+ );
+ assertEq(
+ offer[0][1].itemIndex,
+ 2,
+ "offer index 0 index 1 item index incorrect"
+ );
+ assertEq(offer[1].length, 1, "offer index 1 length incorrect");
+ assertEq(
+ offer[1][0].orderIndex,
+ 0,
+ "offer index 1 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[1][0].itemIndex,
+ 1,
+ "offer index 1 index 0 item index incorrect"
+ );
+
+ assertEq(consideration.length, 2, "consideration length incorrect");
+ assertEq(
+ consideration[0].length,
+ 1,
+ "consideration index 0 length incorrect"
+ );
+ assertEq(
+ consideration[0][0].orderIndex,
+ 0,
+ "consideration index 0 index 0 order index incorrect"
+ );
+ assertEq(
+ consideration[0][0].itemIndex,
+ 0,
+ "consideration index 0 index 0 item index incorrect"
+ );
+ assertEq(
+ consideration[1].length,
+ 2,
+ "consideration index 1 length incorrect"
+ );
+ assertEq(
+ consideration[1][0].orderIndex,
+ 0,
+ "consideration index 1 index 0 order index incorrect"
+ );
+ assertEq(
+ consideration[1][0].itemIndex,
+ 1,
+ "consideration index 1 index 0 item index incorrect"
+ );
+ assertEq(
+ consideration[1][1].orderIndex,
+ 0,
+ "consideration index 1 index 1 order index incorrect"
+ );
+ assertEq(
+ consideration[1][1].itemIndex,
+ 2,
+ "consideration index 1 index 1 item index incorrect"
+ );
+ }
+
+ function testAggregated_multi() public {
+ OrderParameters memory parameters = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ ),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1235)),
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ )
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1234)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ );
+ OrderParameters memory parameters2 = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ ),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ );
+
+ (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ ) = test.getAggregatedFulfillmentComponents(
+ SeaportArrays.OrderParametersArray(parameters, parameters2)
+ );
+
+ assertEq(offer.length, 3, "offer length incorrect");
+ assertEq(offer[0].length, 3, "offer index 0 length incorrect");
+ assertEq(
+ offer[0][0].orderIndex,
+ 0,
+ "offer index 0 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[0][0].itemIndex,
+ 0,
+ "offer index 0 index 0 item index incorrect"
+ );
+ assertEq(
+ offer[0][1].orderIndex,
+ 0,
+ "offer index 0 index 1 order index incorrect"
+ );
+ assertEq(
+ offer[0][1].itemIndex,
+ 2,
+ "offer index 0 index 1 item index incorrect"
+ );
+ assertEq(
+ offer[0][2].orderIndex,
+ 1,
+ "offer index 0 index 2 order index incorrect"
+ );
+ assertEq(
+ offer[0][2].itemIndex,
+ 0,
+ "offer index 0 index 2 item index incorrect"
+ );
+
+ assertEq(offer[1].length, 1, "offer index 1 length incorrect");
+ assertEq(
+ offer[1][0].orderIndex,
+ 0,
+ "offer index 1 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[1][0].itemIndex,
+ 1,
+ "offer index 1 index 0 item index incorrect"
+ );
+
+ assertEq(offer[2].length, 1, "offer index 2 length incorrect");
+ assertEq(
+ offer[2][0].orderIndex,
+ 1,
+ "offer index 2 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[2][0].itemIndex,
+ 1,
+ "offer index 2 index 0 item index incorrect"
+ );
+
+ assertEq(consideration.length, 2, "consideration length incorrect");
+ assertEq(
+ consideration[0].length,
+ 1,
+ "consideration index 0 length incorrect"
+ );
+ assertEq(
+ consideration[0][0].orderIndex,
+ 0,
+ "consideration index 0 index 0 order index incorrect"
+ );
+ assertEq(
+ consideration[0][0].itemIndex,
+ 0,
+ "consideration index 0 index 0 item index incorrect"
+ );
+
+ assertEq(
+ consideration[1].length,
+ 4,
+ "consideration index 1 length incorrect"
+ );
+ assertEq(
+ consideration[1][0].orderIndex,
+ 0,
+ "consideration index 1 index 0 order index incorrect"
+ );
+ assertEq(
+ consideration[1][0].itemIndex,
+ 1,
+ "consideration index 1 index 0 item index incorrect"
+ );
+ assertEq(
+ consideration[1][1].orderIndex,
+ 0,
+ "consideration index 1 index 1 order index incorrect"
+ );
+ assertEq(
+ consideration[1][1].itemIndex,
+ 2,
+ "consideration index 1 index 1 item index incorrect"
+ );
+ assertEq(
+ consideration[1][2].orderIndex,
+ 1,
+ "consideration index 1 index 2 order index incorrect"
+ );
+ assertEq(
+ consideration[1][2].itemIndex,
+ 0,
+ "consideration index 1 index 2 item index incorrect"
+ );
+ assertEq(
+ consideration[1][3].orderIndex,
+ 1,
+ "consideration index 1 index 3 order index incorrect"
+ );
+ assertEq(
+ consideration[1][3].itemIndex,
+ 1,
+ "consideration index 1 index 3 item index incorrect"
+ );
+ }
+
+ function testAggregated_multi_conduitKey() public {
+ OrderParameters memory parameters = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ ),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1235)),
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ )
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withToken(address(1234)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ );
+ OrderParameters memory parameters2 = OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib.empty().withItemType(ItemType.ERC20).withToken(
+ address(1234)
+ ),
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678)),
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC1155)
+ .withToken(address(5678))
+ )
+ )
+ .withConduitKey(bytes32(uint256(1)));
+
+ (
+ FulfillmentComponent[][] memory offer,
+ FulfillmentComponent[][] memory consideration
+ ) = test.getAggregatedFulfillmentComponents(
+ SeaportArrays.OrderParametersArray(parameters, parameters2)
+ );
+
+ assertEq(offer.length, 4, "offer length incorrect");
+ assertEq(offer[0].length, 2, "offer index 0 length incorrect");
+ assertEq(
+ offer[0][0].orderIndex,
+ 0,
+ "offer index 0 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[0][0].itemIndex,
+ 0,
+ "offer index 0 index 0 item index incorrect"
+ );
+ assertEq(
+ offer[0][1].orderIndex,
+ 0,
+ "offer index 0 index 1 order index incorrect"
+ );
+ assertEq(
+ offer[0][1].itemIndex,
+ 2,
+ "offer index 0 index 1 item index incorrect"
+ );
+ // assertEq(
+ // offer[0][2].orderIndex,
+ // 1,
+ // "offer index 0 index 2 order index incorrect"
+ // );
+ // assertEq(
+ // offer[0][2].itemIndex,
+ // 0,
+ // "offer index 0 index 2 item index incorrect"
+ // );
+
+ assertEq(offer[1].length, 1, "offer index 1 length incorrect");
+ assertEq(
+ offer[1][0].orderIndex,
+ 0,
+ "offer index 1 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[1][0].itemIndex,
+ 1,
+ "offer index 1 index 0 item index incorrect"
+ );
+
+ assertEq(offer[2].length, 1, "offer index 2 length incorrect");
+ assertEq(
+ offer[2][0].orderIndex,
+ 1,
+ "offer index 2 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[2][0].itemIndex,
+ 0,
+ "offer index 2 index 0 item index incorrect"
+ );
+
+ assertEq(offer[3].length, 1, "offer index 2 length incorrect");
+ assertEq(
+ offer[3][0].orderIndex,
+ 1,
+ "offer index 2 index 0 order index incorrect"
+ );
+ assertEq(
+ offer[3][0].itemIndex,
+ 1,
+ "offer index 2 index 0 item index incorrect"
+ );
+
+ assertEq(consideration.length, 2, "consideration length incorrect");
+ assertEq(
+ consideration[0].length,
+ 1,
+ "consideration index 0 length incorrect"
+ );
+ assertEq(
+ consideration[0][0].orderIndex,
+ 0,
+ "consideration index 0 index 0 order index incorrect"
+ );
+ assertEq(
+ consideration[0][0].itemIndex,
+ 0,
+ "consideration index 0 index 0 item index incorrect"
+ );
+
+ assertEq(
+ consideration[1].length,
+ 4,
+ "consideration index 1 length incorrect"
+ );
+ assertEq(
+ consideration[1][0].orderIndex,
+ 0,
+ "consideration index 1 index 0 order index incorrect"
+ );
+ assertEq(
+ consideration[1][0].itemIndex,
+ 1,
+ "consideration index 1 index 0 item index incorrect"
+ );
+ assertEq(
+ consideration[1][1].orderIndex,
+ 0,
+ "consideration index 1 index 1 order index incorrect"
+ );
+ assertEq(
+ consideration[1][1].itemIndex,
+ 2,
+ "consideration index 1 index 1 item index incorrect"
+ );
+ assertEq(
+ consideration[1][2].orderIndex,
+ 1,
+ "consideration index 1 index 2 order index incorrect"
+ );
+ assertEq(
+ consideration[1][2].itemIndex,
+ 0,
+ "consideration index 1 index 2 item index incorrect"
+ );
+ assertEq(
+ consideration[1][3].orderIndex,
+ 1,
+ "consideration index 1 index 3 order index incorrect"
+ );
+ assertEq(
+ consideration[1][3].itemIndex,
+ 1,
+ "consideration index 1 index 3 item index incorrect"
+ );
+ }
+}
diff --git a/test/foundry/new/helpers/sol/MatchFulfillmentHelper.t.sol b/test/foundry/new/helpers/sol/MatchFulfillmentHelper.t.sol
new file mode 100644
index 000000000..01d8003db
--- /dev/null
+++ b/test/foundry/new/helpers/sol/MatchFulfillmentHelper.t.sol
@@ -0,0 +1,2026 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol";
+
+import {
+ ConsiderationItemLib,
+ FulfillmentComponentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ OrderParametersLib,
+ SeaportArrays
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ ConsiderationItem,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import {
+ MatchFulfillmentHelper
+} from "seaport-sol/fulfillments/match/MatchFulfillmentHelper.sol";
+
+import {
+ MatchComponent,
+ MatchComponentType
+} from "seaport-sol/lib/types/MatchComponentType.sol";
+
+import { Account, BaseOrderTest } from "../../BaseOrderTest.sol";
+
+contract MatchFulfillmentHelperTest is BaseOrderTest {
+ using Strings for uint256;
+
+ using ConsiderationItemLib for ConsiderationItem;
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using MatchComponentType for MatchComponent;
+ using OfferItemLib for OfferItem;
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderParametersLib for OrderParameters;
+
+ // MatchFulfillmentHelper matcher;
+
+ struct MatchFulfillmentContext {
+ FuzzArgs args;
+ }
+
+ struct FuzzArgs {
+ bool useDifferentConduitKeys;
+ }
+
+ function setUp() public virtual override {
+ super.setUp();
+
+ // matcher = new MatchFulfillmentHelper();
+ }
+
+ function testGetMatchedFulfillments_self() public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withItemType(ItemType.ERC20)
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withItemType(ItemType.ERC20)
+ .withAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(order, offerer1, 0);
+
+ Fulfillment memory expectedFulfillment = Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ });
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order),
+ new bytes32[](1),
+ new UnavailableReason[](1)
+ );
+
+ assertEq(fulfillments.length, 1);
+ assertEq(fulfillments[0], expectedFulfillment, "fulfillments[0]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_self_conduitDisparity() public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withItemType(ItemType.ERC20)
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withItemType(ItemType.ERC20)
+ .withAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(order, offerer1, 0);
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withItemType(ItemType.ERC20)
+ .withAmount(101)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withItemType(ItemType.ERC20)
+ .withAmount(101)
+ )
+ )
+ .withConduitKey(conduitKey),
+ signature: ""
+ });
+
+ otherOrder = _toMatchableOrder(otherOrder, offerer1, 0);
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }),
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order, otherOrder),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 2);
+ assertEq(fulfillments[0], expectedFulfillments[0], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[1], "fulfillments[1]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order, otherOrder),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_1ItemTo1Item() public {
+ execGetMatchedFulfillments_1ItemTo1Item(false);
+ execGetMatchedFulfillments_1ItemTo1Item(true);
+ }
+
+ function execGetMatchedFulfillments_1ItemTo1Item(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ // No expected difference between the fulfillments when the two orders
+ // use different conduit keys, so just toggle it back and for to make
+ // sure nothing goes wrong.
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(otherOrder, order),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 2, "fulfillments.length");
+ assertEq(fulfillments[0], expectedFulfillments[1], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[0], "fulfillments[1]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(otherOrder, order),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_1ItemTo1Item_ascending() public {
+ execGetMatchedFulfillments_1ItemTo1Item_ascending(false);
+ execGetMatchedFulfillments_1ItemTo1Item_ascending(true);
+ }
+
+ function execGetMatchedFulfillments_1ItemTo1Item_ascending(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(otherOrder, order),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 2, "fulfillments.length");
+ assertEq(fulfillments[0], expectedFulfillments[1], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[0], "fulfillments[1]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(otherOrder, order),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_1ItemTo1Item_descending() public {
+ execGetMatchedFulfillments_1ItemTo1Item_descending(false);
+ execGetMatchedFulfillments_1ItemTo1Item_descending(true);
+ }
+
+ function execGetMatchedFulfillments_1ItemTo1Item_descending(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withStartAmount(100)
+ .withEndAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withStartAmount(100)
+ .withEndAmount(1)
+ )
+ ),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withStartAmount(100)
+ .withEndAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withStartAmount(100)
+ .withEndAmount(1)
+ )
+ ),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(otherOrder, order),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 2, "fulfillments.length");
+ assertEq(fulfillments[0], expectedFulfillments[1], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[0], "fulfillments[1]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(otherOrder, order),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_1ItemTo1Item_descending_leftover()
+ public
+ {
+ execGetMatchedFulfillments_1ItemTo1Item_descending_leftover(false);
+ execGetMatchedFulfillments_1ItemTo1Item_descending_leftover(true);
+ }
+
+ function execGetMatchedFulfillments_1ItemTo1Item_descending_leftover(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withStartAmount(100)
+ .withEndAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withStartAmount(1)
+ .withEndAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (
+ Fulfillment[] memory fulfillments,
+ MatchComponent[] memory leftoverOffer,
+ MatchComponent[] memory leftoverConsideration
+ ) = matcher.getMatchedFulfillments(
+ SeaportArrays.Orders(otherOrder, order),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 2, "fulfillments.length");
+ assertEq(fulfillments[0], expectedFulfillments[1], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[0], "fulfillments[1]");
+ assertEq(leftoverOffer.length, 1, "leftoverOffer.length");
+ assertEq(leftoverOffer[0].getAmount(), 99, "leftoverOffer[0].amount()");
+ assertEq(
+ leftoverConsideration.length,
+ 0,
+ "leftoverConsideration.length"
+ );
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(otherOrder, order),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_1ItemTo1ItemExcessOffer() public {
+ execGetMatchedFulfillments_1ItemTo1ItemExcessOffer(false);
+ execGetMatchedFulfillments_1ItemTo1ItemExcessOffer(true);
+ }
+
+ function execGetMatchedFulfillments_1ItemTo1ItemExcessOffer(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(100)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(200)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ )
+ .withOfferer(offerer2.addr),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(otherOrder, order),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 2, "fulfillments.length");
+ assertEq(fulfillments[0], expectedFulfillments[1], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[0], "fulfillments[1]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(otherOrder, order),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_3ItemsTo1Item() public {
+ execGetMatchedFulfillments_3ItemsTo1Item(false);
+ execGetMatchedFulfillments_3ItemsTo1Item(true);
+ }
+
+ function execGetMatchedFulfillments_3ItemsTo1Item(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(80)
+ .withRecipient(offerer2.addr)
+ )
+ )
+ .withOfferer(offerer2.addr),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 2 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order, otherOrder),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 3, "fulfillments.length");
+
+ assertEq(fulfillments[0], expectedFulfillments[2], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[1], "fulfillments[1]");
+ assertEq(fulfillments[2], expectedFulfillments[0], "fulfillments[2]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order, otherOrder),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_3ItemsTo1Item_extra() public {
+ execGetMatchedFulfillments_3ItemsTo1Item_extra(false);
+ execGetMatchedFulfillments_3ItemsTo1Item_extra(true);
+ }
+
+ function execGetMatchedFulfillments_3ItemsTo1Item_extra(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(110)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(80)
+ .withRecipient(offerer2.addr)
+ )
+ )
+ .withOfferer(offerer2.addr),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 2 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order, otherOrder),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 3, "fulfillments.length");
+
+ assertEq(fulfillments[0], expectedFulfillments[2], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[1], "fulfillments[1]");
+ assertEq(fulfillments[2], expectedFulfillments[0], "fulfillments[2]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order, otherOrder),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_3ItemsTo2Items() public {
+ execGetMatchedFulfillments_3ItemsTo2Items(false);
+ execGetMatchedFulfillments_3ItemsTo2Items(true);
+ }
+
+ function execGetMatchedFulfillments_3ItemsTo2Items(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(90)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(80)
+ .withRecipient(offerer2.addr)
+ )
+ )
+ .withOfferer(offerer2.addr),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 2 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order, otherOrder),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 3, "fulfillments.length");
+
+ // Note: the expected fulfillments will need to change in this case.
+ assertEq(fulfillments[0], expectedFulfillments[0], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[1], "fulfillments[1]");
+ assertEq(fulfillments[2], expectedFulfillments[2], "fulfillments[2]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order, otherOrder),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_3ItemsTo2Items_swap() public {
+ execGetMatchedFulfillments_3ItemsTo2Items_swap(false);
+ execGetMatchedFulfillments_3ItemsTo2Items_swap(true);
+ }
+
+ function execGetMatchedFulfillments_3ItemsTo2Items_swap(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(90),
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(80)
+ .withRecipient(offerer2.addr)
+ )
+ )
+ .withOfferer(offerer2.addr),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 2 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order, otherOrder),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+
+ assertEq(fulfillments.length, 3, "fulfillments.length");
+
+ assertEq(fulfillments[0], expectedFulfillments[0], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[1], "fulfillments[1]");
+ assertEq(fulfillments[2], expectedFulfillments[2], "fulfillments[2]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order, otherOrder),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_DoubleOrderPairs_1ItemTo1Item() public {
+ execGetMatchedFulfillments_DoubleOrderPairs_1ItemTo1Item(false, false);
+ execGetMatchedFulfillments_DoubleOrderPairs_1ItemTo1Item(true, false);
+ execGetMatchedFulfillments_DoubleOrderPairs_1ItemTo1Item(false, true);
+ // Can't do true, true until we set up another test conduit.
+ }
+
+ function execGetMatchedFulfillments_DoubleOrderPairs_1ItemTo1Item(
+ bool useDifferentConduitsBetweenPrimeAndMirror,
+ bool useDifferentConduitsBetweenOrderPairs
+ ) public {
+ Order memory orderOne = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ orderOne = _toMatchableOrder(
+ orderOne,
+ offerer1,
+ useDifferentConduitsBetweenPrimeAndMirror
+ ? 0
+ : useDifferentConduitsBetweenOrderPairs
+ ? 1
+ : 2
+ );
+
+ Order memory otherOrderOne = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(100)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(100)
+ )
+ ),
+ signature: ""
+ });
+
+ if (useDifferentConduitsBetweenPrimeAndMirror) {
+ otherOrderOne.parameters = otherOrderOne.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrderOne = _toMatchableOrder(
+ otherOrderOne,
+ offerer2,
+ useDifferentConduitsBetweenPrimeAndMirror
+ ? 0
+ : useDifferentConduitsBetweenOrderPairs
+ ? 1
+ : 2
+ );
+
+ Order memory orderTwo = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(101)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(101)
+ )
+ ),
+ signature: ""
+ });
+
+ if (useDifferentConduitsBetweenOrderPairs) {
+ orderTwo.parameters = orderTwo.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ orderTwo = _toMatchableOrder(
+ orderTwo,
+ offerer1,
+ useDifferentConduitsBetweenPrimeAndMirror
+ ? 0
+ : useDifferentConduitsBetweenOrderPairs
+ ? 1
+ : 2
+ );
+
+ Order memory otherOrderTwo = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(101)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(101)
+ )
+ ),
+ signature: ""
+ });
+
+ if (
+ useDifferentConduitsBetweenPrimeAndMirror ||
+ useDifferentConduitsBetweenOrderPairs
+ ) {
+ otherOrderTwo.parameters = otherOrderTwo.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrderTwo = _toMatchableOrder(
+ otherOrderTwo,
+ offerer2,
+ useDifferentConduitsBetweenPrimeAndMirror
+ ? 0
+ : useDifferentConduitsBetweenOrderPairs
+ ? 1
+ : 2
+ );
+
+ Fulfillment[] memory expectedFulfillments;
+
+ if (!useDifferentConduitsBetweenOrderPairs) {
+ expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 }),
+ FulfillmentComponent({ orderIndex: 3, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({
+ orderIndex: 0,
+ itemIndex: 0
+ }),
+ FulfillmentComponent({
+ orderIndex: 2,
+ itemIndex: 0
+ })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }),
+ FulfillmentComponent({ orderIndex: 2, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({
+ orderIndex: 1,
+ itemIndex: 0
+ }),
+ FulfillmentComponent({
+ orderIndex: 3,
+ itemIndex: 0
+ })
+ )
+ })
+ );
+ } else {
+ // [
+ // ([(1, 0)], [(0, 0), (2, 0)]),
+ // ([(3, 0)], [(0, 0)]),
+ // ([(0, 0)], [(1, 0), (3, 0)]),
+ // ([(2, 0)], [(1, 0)])
+ // ]
+ expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({
+ orderIndex: 0,
+ itemIndex: 0
+ }),
+ FulfillmentComponent({
+ orderIndex: 2,
+ itemIndex: 0
+ })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 3, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({
+ orderIndex: 0,
+ itemIndex: 0
+ })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({
+ orderIndex: 1,
+ itemIndex: 0
+ }),
+ FulfillmentComponent({
+ orderIndex: 3,
+ itemIndex: 0
+ })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 2, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays
+ .FulfillmentComponents(
+ FulfillmentComponent({
+ orderIndex: 1,
+ itemIndex: 0
+ })
+ )
+ })
+ );
+ }
+
+ bytes32[] memory orderHashes = new bytes32[](4);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(
+ orderOne,
+ otherOrderOne,
+ orderTwo,
+ otherOrderTwo
+ ),
+ orderHashes,
+ new UnavailableReason[](4)
+ );
+
+ if (!useDifferentConduitsBetweenOrderPairs) {
+ assertEq(fulfillments.length, 2, "fulfillments.length");
+ assertEq(
+ fulfillments[0],
+ expectedFulfillments[0],
+ "fulfillments[0]"
+ );
+ assertEq(
+ fulfillments[1],
+ expectedFulfillments[1],
+ "fulfillments[1]"
+ );
+ } else {
+ assertEq(fulfillments.length, 4, "fulfillments.length");
+ assertEq(
+ fulfillments[0],
+ expectedFulfillments[0],
+ "fulfillments[0]"
+ );
+ assertEq(
+ fulfillments[1],
+ expectedFulfillments[1],
+ "fulfillments[1]"
+ );
+ assertEq(
+ fulfillments[2],
+ expectedFulfillments[2],
+ "fulfillments[2]"
+ );
+ assertEq(
+ fulfillments[3],
+ expectedFulfillments[3],
+ "fulfillments[3]"
+ );
+ }
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(
+ orderOne,
+ otherOrderOne,
+ orderTwo,
+ otherOrderTwo
+ ),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testGetMatchedFulfillments_consolidatedConsideration() public {
+ execGetMatchedFulfillments_consolidatedConsideration(false);
+ execGetMatchedFulfillments_consolidatedConsideration(true);
+ }
+
+ function execGetMatchedFulfillments_consolidatedConsideration(
+ bool useDifferentConduits
+ ) public {
+ Order memory order = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(90),
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(10)
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(90),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ order = _toMatchableOrder(
+ order,
+ offerer1,
+ useDifferentConduits ? 1 : 0
+ );
+
+ Order memory otherOrder = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(30)
+ )
+ )
+ .withConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(10)
+ )
+ )
+ .withOfferer(offerer2.addr),
+ signature: ""
+ });
+
+ if (useDifferentConduits) {
+ otherOrder.parameters = otherOrder.parameters.withConduitKey(
+ conduitKey
+ );
+ }
+
+ otherOrder = _toMatchableOrder(
+ otherOrder,
+ offerer2,
+ useDifferentConduits ? 2 : 0
+ );
+
+ Fulfillment[] memory expectedFulfillments = SeaportArrays.Fulfillments(
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 }),
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 2 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 0 })
+ )
+ }),
+ Fulfillment({
+ offerComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 0, itemIndex: 1 })
+ ),
+ considerationComponents: SeaportArrays.FulfillmentComponents(
+ FulfillmentComponent({ orderIndex: 1, itemIndex: 0 })
+ )
+ })
+ );
+
+ bytes32[] memory orderHashes = new bytes32[](2);
+
+ (Fulfillment[] memory fulfillments, , ) = matcher
+ .getMatchedFulfillments(
+ SeaportArrays.Orders(order, otherOrder),
+ orderHashes,
+ new UnavailableReason[](2)
+ );
+ assertEq(fulfillments.length, 3, "fulfillments.length");
+
+ assertEq(fulfillments[0], expectedFulfillments[0], "fulfillments[0]");
+ assertEq(fulfillments[1], expectedFulfillments[1], "fulfillments[1]");
+ assertEq(fulfillments[2], expectedFulfillments[2], "fulfillments[2]");
+
+ seaport.matchOrders({
+ orders: SeaportArrays.Orders(order, otherOrder),
+ fulfillments: fulfillments
+ });
+ }
+
+ function testRemainingItems_availableOrder() public {
+ Order memory order1 = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1)
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(11)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(2)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ // Note: there's no order 2.
+
+ bytes32[] memory orderHashes = new bytes32[](1);
+
+ (
+ ,
+ MatchComponent[] memory remainingOffer,
+ MatchComponent[] memory remainingConsideration
+ ) = matcher.getMatchedFulfillments(
+ SeaportArrays.Orders(order1),
+ orderHashes,
+ new UnavailableReason[](1)
+ );
+
+ assertEq(remainingOffer.length, 2, "remainingOffer.length");
+ assertEq(
+ remainingConsideration.length,
+ 2,
+ "remainingConsideration.length"
+ );
+ assertEq(
+ remainingOffer[0].getOrderIndex(),
+ 0,
+ "remainingOffer[0].orderIndex"
+ );
+ assertEq(
+ remainingOffer[0].getItemIndex(),
+ 0,
+ "remainingOffer[0].itemIndex"
+ );
+ assertEq(remainingOffer[0].getAmount(), 10, "remainingOffer[0].amount");
+ assertEq(
+ remainingOffer[1].getOrderIndex(),
+ 0,
+ "remainingOffer[1].orderIndex"
+ );
+ assertEq(
+ remainingOffer[1].getItemIndex(),
+ 1,
+ "remainingOffer[1].itemIndex"
+ );
+ assertEq(remainingOffer[1].getAmount(), 11, "remainingOffer[1].amount");
+
+ assertEq(
+ remainingConsideration[0].getOrderIndex(),
+ 0,
+ "remainingConsideration[0].orderIndex"
+ );
+ assertEq(
+ remainingConsideration[0].getItemIndex(),
+ 0,
+ "remainingConsideration[0].itemIndex"
+ );
+ assertEq(
+ remainingConsideration[0].getAmount(),
+ 1,
+ "remainingConsideration[0].amount"
+ );
+ assertEq(
+ remainingConsideration[1].getOrderIndex(),
+ 0,
+ "remainingConsideration[1].orderIndex"
+ );
+ assertEq(
+ remainingConsideration[1].getItemIndex(),
+ 1,
+ "remainingConsideration[1].itemIndex"
+ );
+ assertEq(
+ remainingConsideration[1].getAmount(),
+ 2,
+ "remainingConsideration[1].amount"
+ );
+ }
+
+ function testRemainingItems_unavailableOrder() public {
+ Order memory order1 = Order({
+ parameters: OrderParametersLib
+ .empty()
+ .withOffer(
+ SeaportArrays.OfferItems(
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(10),
+ OfferItemLib
+ .empty()
+ .withToken(address(erc20s[0]))
+ .withAmount(11)
+ )
+ )
+ .withTotalConsideration(
+ SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(1),
+ ConsiderationItemLib
+ .empty()
+ .withToken(address(erc20s[1]))
+ .withAmount(2)
+ )
+ )
+ .withOfferer(offerer1.addr),
+ signature: ""
+ });
+
+ // Note: there's no order 2.
+
+ bytes32[] memory orderHashes = new bytes32[](1);
+
+ (
+ ,
+ MatchComponent[] memory remainingOffer,
+ MatchComponent[] memory remainingConsideration
+ ) = matcher.getMatchedFulfillments(
+ SeaportArrays.Orders(order1),
+ orderHashes,
+ new UnavailableReason[](1)
+ );
+
+ assertEq(remainingOffer.length, 0);
+ assertEq(remainingConsideration.length, 0);
+ }
+
+ function assertEq(
+ Fulfillment memory left,
+ Fulfillment memory right,
+ string memory message
+ ) internal {
+ assertEq(
+ left.offerComponents,
+ right.offerComponents,
+ string.concat(message, " offerComponents")
+ );
+ assertEq(
+ left.considerationComponents,
+ right.considerationComponents,
+ string.concat(message, " considerationComponents")
+ );
+ }
+
+ function assertEq(
+ FulfillmentComponent[] memory left,
+ FulfillmentComponent[] memory right,
+ string memory message
+ ) internal {
+ assertEq(left.length, right.length, string.concat(message, " length"));
+
+ for (uint256 i = 0; i < left.length; i++) {
+ assertEq(
+ left[i],
+ right[i],
+ string.concat(message, " index ", i.toString())
+ );
+ }
+ }
+
+ function assertEq(
+ FulfillmentComponent memory left,
+ FulfillmentComponent memory right,
+ string memory message
+ ) internal {
+ assertEq(
+ left.orderIndex,
+ right.orderIndex,
+ string.concat(message, " orderIndex")
+ );
+ assertEq(
+ left.itemIndex,
+ right.itemIndex,
+ string.concat(message, " itemIndex")
+ );
+ }
+
+ function _toMatchableOrder(
+ Order memory order,
+ Account memory offerer,
+ uint256 salt
+ ) internal view returns (Order memory) {
+ for (uint256 i = 0; i < order.parameters.offer.length; i++) {
+ order.parameters.offer[i] = order
+ .parameters
+ .offer[i]
+ .copy()
+ .withItemType(ItemType.ERC20);
+ }
+
+ for (uint256 i = 0; i < order.parameters.consideration.length; i++) {
+ order.parameters.consideration[i] = order
+ .parameters
+ .consideration[i]
+ .copy()
+ .withItemType(ItemType.ERC20);
+ }
+
+ OrderParameters memory parameters = order
+ .parameters
+ .copy()
+ .withOfferer(offerer.addr)
+ .withStartTime(block.timestamp)
+ // Bump the end time by 100 so that the test doesn't try to match the
+ // same order twice.
+ .withEndTime(block.timestamp + 1)
+ .withTotalOriginalConsiderationItems(
+ order.parameters.consideration.length
+ )
+ .withSalt(salt);
+
+ OrderComponents memory orderComponents = parameters
+ .toOrderComponents(seaport.getCounter(offerer.addr))
+ .withCounter(seaport.getCounter(offerer.addr));
+
+ bytes32 orderHash = seaport.getOrderHash(orderComponents);
+
+ bytes memory signature = signOrder(seaport, offerer.key, orderHash);
+
+ return Order({ parameters: parameters, signature: signature });
+ }
+}
diff --git a/test/foundry/new/helpers/sol/MatchFulfillmentPriv.t.sol b/test/foundry/new/helpers/sol/MatchFulfillmentPriv.t.sol
new file mode 100644
index 000000000..48be27257
--- /dev/null
+++ b/test/foundry/new/helpers/sol/MatchFulfillmentPriv.t.sol
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol";
+
+import { Test } from "forge-std/Test.sol";
+
+import { LibSort } from "solady/src/utils/LibSort.sol";
+
+import {
+ ConsiderationItemLib,
+ OfferItemLib,
+ OrderParametersLib
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ MatchFulfillmentLib,
+ ProcessComponentParams
+} from "seaport-sol/fulfillments/match/MatchFulfillmentLib.sol";
+
+import {
+ MatchComponent,
+ MatchComponentType
+} from "seaport-sol/lib/types/MatchComponentType.sol";
+
+import { MatchArrays } from "seaport-sol/fulfillments/lib/MatchArrays.sol";
+
+import {
+ ConsiderationItem,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+contract MatchFulfillmentLibTest is Test {
+ using ConsiderationItemLib for ConsiderationItem;
+ using MatchComponentType for MatchComponent;
+ using OfferItemLib for OfferItem;
+ using OrderParametersLib for OrderParameters;
+ using Strings for uint256;
+
+ MatchComponent[] _components;
+ MatchComponent[] consideration;
+ MatchComponent[] offer;
+
+ using MatchComponentType for MatchComponent[];
+
+ function testConsolidateComponents(uint240[10] memory amounts) public {
+ // copy to dynamic array
+ MatchComponent[] memory toBeSorted = new MatchComponent[](10);
+ for (uint256 i = 0; i < 10; i++) {
+ MatchComponent memory temp = MatchComponentType
+ .createMatchComponent(amounts[i], 0, 0);
+ toBeSorted[i] = temp;
+ }
+ // sort dynamic array in-place
+ MatchArrays.sortByAmount(toBeSorted);
+ // copy to storage
+ for (uint256 i = 0; i < 10; i++) {
+ _components.push(toBeSorted[i]);
+ }
+ // call function
+ MatchFulfillmentLib.consolidateComponents(
+ _components,
+ _components.length
+ );
+ assertLt(_components.length, 2, "consolidateComponents length");
+ for (uint256 i; i < _components.length; ++i) {
+ assertGt(_components[i].getAmount(), 0, "consolidateComponents");
+ }
+ }
+
+ function testProcessOfferComponent() public {
+ FulfillmentComponent[] memory offerFulfillmentComponents = MatchArrays
+ .allocateFulfillmentComponents(2);
+
+ offer.push(MatchComponentType.createMatchComponent(1, 0, 0));
+ consideration.push(MatchComponentType.createMatchComponent(1, 0, 0));
+ ProcessComponentParams memory params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: new FulfillmentComponent[](0),
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processOfferComponent(offer, consideration, params);
+ assertEq(
+ params.offerItemIndex,
+ 1,
+ "processOfferComponent offerItemIndex"
+ );
+ assertEq(
+ offer[0].getAmount(),
+ 0,
+ "processOfferComponent offer[0].getAmount()"
+ );
+ assertEq(
+ consideration[0].getAmount(),
+ 0,
+ "processOfferComponent consideration[0].getAmount() 1"
+ );
+ assertEq(
+ params.offerFulfillmentComponents.length,
+ 1,
+ "offerFulfillmentComponents length"
+ );
+
+ offerFulfillmentComponents = MatchArrays.allocateFulfillmentComponents(
+ 2
+ );
+ consideration[0] = MatchComponentType.createMatchComponent(2, 0, 0);
+ offer[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: new FulfillmentComponent[](0),
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processOfferComponent(offer, consideration, params);
+ assertEq(
+ params.offerItemIndex,
+ 1,
+ "processOfferComponent offerItemIndex"
+ );
+ assertEq(
+ offer[0].getAmount(),
+ 0,
+ "processOfferComponent offer[0].getAmount()"
+ );
+ assertEq(
+ consideration[0].getAmount(),
+ 1,
+ "processOfferComponent consideration[0].getAmount() 2"
+ );
+ assertEq(
+ params.offerFulfillmentComponents.length,
+ 1,
+ "offerFulfillmentComponents length"
+ );
+
+ offerFulfillmentComponents = MatchArrays.allocateFulfillmentComponents(
+ 2
+ );
+ consideration[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ offer[0] = MatchComponentType.createMatchComponent(2, 0, 0);
+ params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: new FulfillmentComponent[](0),
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processOfferComponent(offer, consideration, params);
+ assertEq(
+ params.offerItemIndex,
+ 0,
+ "processOfferComponent offerItemIndex"
+ );
+ assertEq(
+ params.offerFulfillmentComponents.length,
+ 1,
+ "offerFulfillmentComponents length"
+ );
+ assertEq(
+ offer[0].getAmount(),
+ 1,
+ "processOfferComponent offer[0].getAmount()"
+ );
+ assertEq(
+ consideration[0].getAmount(),
+ 0,
+ "processOfferComponent consideration[0].getAmount() 3"
+ );
+ assertEq(params.offerFulfillmentComponents.length, 1);
+
+ offerFulfillmentComponents = MatchArrays.allocateFulfillmentComponents(
+ 2
+ );
+
+ consideration[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ offer[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: new FulfillmentComponent[](0),
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processOfferComponent(offer, consideration, params);
+ assertEq(
+ consideration[0].getAmount(),
+ 0,
+ "consideration[0].getAmount() 4"
+ );
+ assertEq(offer[0].getAmount(), 0, "offer[0].getAmount()");
+ assertEq(
+ params.offerItemIndex,
+ 1,
+ "processOfferComponent offerItemIndex"
+ );
+ assertEq(params.offerFulfillmentComponents.length, 1);
+ }
+
+ function testProcessConsiderationComponents() public {
+ FulfillmentComponent[] memory offerFulfillmentComponents = MatchArrays
+ .allocateFulfillmentComponents(2);
+ FulfillmentComponent[]
+ memory considerationFulfillmentComponents = MatchArrays
+ .allocateFulfillmentComponents(2);
+ offer.push(MatchComponentType.createMatchComponent(1, 0, 0));
+ consideration.push(MatchComponentType.createMatchComponent(1, 0, 0));
+ ProcessComponentParams memory params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: considerationFulfillmentComponents,
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processConsiderationComponent(
+ offer,
+ consideration,
+ params
+ );
+ assertEq(
+ params.offerItemIndex,
+ 1,
+ "processConsiderationComponent offerItemIndex"
+ );
+
+ assertEq(
+ offer[0].getAmount(),
+ 0,
+ "processConsiderationComponent offer[0].getAmount()"
+ );
+ assertEq(
+ consideration[0].getAmount(),
+ 0,
+ "processConsiderationComponent consideration[0].getAmount()"
+ );
+
+ consideration[0] = MatchComponentType.createMatchComponent(2, 0, 0);
+ offer[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: considerationFulfillmentComponents,
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processConsiderationComponent(
+ offer,
+ consideration,
+ params
+ );
+ assertEq(
+ params.offerItemIndex,
+ 1,
+ "processConsiderationComponent offerItemIndex"
+ );
+
+ assertEq(
+ offer[0].getAmount(),
+ 0,
+ "processConsiderationComponent offer[0].getAmount()"
+ );
+ assertEq(
+ consideration[0].getAmount(),
+ 1,
+ "processConsiderationComponent consideration[0].getAmount()"
+ );
+
+ consideration[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ offer[0] = MatchComponentType.createMatchComponent(2, 0, 0);
+ params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: considerationFulfillmentComponents,
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ MatchFulfillmentLib.processConsiderationComponent(
+ offer,
+ consideration,
+ params
+ );
+ assertEq(
+ params.offerItemIndex,
+ 0,
+ "processConsiderationComponent offerItemIndex"
+ );
+
+ assertEq(
+ offer[0].getAmount(),
+ 1,
+ "processConsiderationComponent offer[0].getAmount()"
+ );
+ assertEq(
+ consideration[0].getAmount(),
+ 0,
+ "processConsiderationComponent consideration[0].getAmount()"
+ );
+
+ consideration[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ offer[0] = MatchComponentType.createMatchComponent(1, 0, 0);
+ params = ProcessComponentParams({
+ offerFulfillmentComponents: offerFulfillmentComponents,
+ considerationFulfillmentComponents: considerationFulfillmentComponents,
+ offerItemIndex: 0,
+ considerationItemIndex: 0,
+ midCredit: false
+ });
+ // offerFulfillmentIndex: 1,
+ // considerationFulfillmentIndex: 0
+
+ MatchFulfillmentLib.processConsiderationComponent(
+ offer,
+ consideration,
+ params
+ );
+ assertEq(
+ params.offerItemIndex,
+ 1,
+ "processConsiderationComponent offerItemIndex"
+ );
+ }
+
+ function clear(MatchComponent[] storage components) internal {
+ while (components.length > 0) {
+ components.pop();
+ }
+ }
+
+ function assertEq(
+ MatchComponent memory left,
+ MatchComponent memory right
+ ) internal {
+ FulfillmentComponent memory leftComponent = left
+ .toFulfillmentComponent();
+ FulfillmentComponent memory rightComponent = right
+ .toFulfillmentComponent();
+ assertEq(leftComponent, rightComponent, "component");
+ assertEq(left.getAmount(), right.getAmount(), "amount");
+ }
+
+ event LogFulfillmentComponent(FulfillmentComponent);
+ event LogFulfillment(Fulfillment);
+
+ function assertEq(
+ Fulfillment memory left,
+ Fulfillment memory right,
+ string memory message
+ ) internal {
+ emit LogFulfillment(left);
+ emit LogFulfillment(right);
+ emit Spacer();
+ assertEq(
+ left.offerComponents,
+ right.offerComponents,
+ string.concat(message, " offerComponents")
+ );
+ assertEq(
+ left.considerationComponents,
+ right.considerationComponents,
+ string.concat(message, " considerationComponents")
+ );
+ }
+
+ function assertEq(
+ FulfillmentComponent[] memory left,
+ FulfillmentComponent[] memory right,
+ string memory message
+ ) internal {
+ assertEq(left.length, right.length, string.concat(message, " length"));
+
+ for (uint256 i = 0; i < left.length; i++) {
+ assertEq(
+ left[i],
+ right[i],
+ string.concat(message, " index ", i.toString())
+ );
+ }
+ }
+
+ event Spacer();
+
+ function assertEq(
+ FulfillmentComponent memory left,
+ FulfillmentComponent memory right,
+ string memory message
+ ) internal {
+ emit LogFulfillmentComponent(left);
+ emit LogFulfillmentComponent(right);
+ emit Spacer();
+ assertEq(
+ left.orderIndex,
+ right.orderIndex,
+ string.concat(message, " orderIndex")
+ );
+ assertEq(
+ left.itemIndex,
+ right.itemIndex,
+ string.concat(message, " itemIndex")
+ );
+ }
+}
diff --git a/test/foundry/new/helpers/sol/lib/fulfillment/AmountDeriverHelper.t.sol b/test/foundry/new/helpers/sol/lib/fulfillment/AmountDeriverHelper.t.sol
new file mode 100644
index 000000000..ab459e4cd
--- /dev/null
+++ b/test/foundry/new/helpers/sol/lib/fulfillment/AmountDeriverHelper.t.sol
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+import {
+ AmountDeriverHelper
+} from "seaport-sol/lib/fulfillment/AmountDeriverHelper.sol";
+
+contract TestAmountDeriverHelper is AmountDeriverHelper {
+ function applyFraction(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 startAmount,
+ uint256 endAmount,
+ bool roundUp
+ ) public view returns (uint256) {
+ return
+ _applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startAmount: startAmount,
+ endAmount: endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: roundUp // round up considerations
+ });
+ }
+
+ function getFraction(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 value
+ ) public pure returns (uint256) {
+ return
+ _getFraction({
+ numerator: numerator,
+ denominator: denominator,
+ value: value
+ });
+ }
+
+ function locateCurrentAmount(
+ uint256 startAmount,
+ uint256 endAmount,
+ uint256 startTime,
+ uint256 endTime,
+ bool roundUp
+ ) public view returns (uint256) {
+ return
+ _locateCurrentAmount({
+ startAmount: startAmount,
+ endAmount: endAmount,
+ startTime: startTime,
+ endTime: endTime,
+ roundUp: roundUp
+ });
+ }
+}
+
+contract AmountDeriverHelperTest is Test {
+ TestAmountDeriverHelper helper;
+
+ function setUp() public {
+ helper = new TestAmountDeriverHelper();
+ }
+
+ function coerceNumeratorAndDenominator(
+ uint120 numerator,
+ uint120 denominator
+ ) internal view returns (uint120 newNumerator, uint120 newDenominator) {
+ numerator = uint120(bound(numerator, 1, type(uint120).max));
+ denominator = uint120(bound(denominator, 1, type(uint120).max));
+ if (numerator > denominator) {
+ (numerator, denominator) = (denominator, numerator);
+ }
+ return (numerator, denominator);
+ }
+
+ function testDeriveFractionCompatibleAmountsAndTimes(
+ uint256 originalStartAmount,
+ uint256 originalEndAmount,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 currentTime,
+ uint120 numerator,
+ uint120 denominator
+ ) public {
+ startTime = bound(startTime, 1, type(uint40).max - 2);
+ endTime = bound(endTime, startTime + 1, type(uint40).max);
+
+ (numerator, denominator) = coerceNumeratorAndDenominator(
+ numerator,
+ denominator
+ );
+
+ originalStartAmount = bound(originalStartAmount, 1, type(uint256).max);
+ originalEndAmount = bound(originalEndAmount, 1, type(uint256).max);
+
+ (uint256 newStartAmount, uint256 newEndAmount) = helper
+ .deriveFractionCompatibleAmounts(
+ originalStartAmount,
+ originalEndAmount,
+ startTime,
+ endTime,
+ numerator,
+ denominator
+ );
+
+ currentTime = bound(currentTime, startTime, endTime - 1);
+
+ vm.warp(currentTime);
+
+ // will revert if invalid
+ helper.applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startTime: startTime,
+ endTime: endTime,
+ startAmount: newStartAmount,
+ endAmount: newEndAmount,
+ roundUp: false
+ });
+
+ // will revert if invalid
+ helper.applyFraction({
+ numerator: numerator,
+ denominator: denominator,
+ startTime: startTime,
+ endTime: endTime,
+ startAmount: newStartAmount,
+ endAmount: newEndAmount,
+ roundUp: true
+ });
+ }
+}
diff --git a/test/foundry/new/helpers/sol/lib/types/MatchComponentType.t.sol b/test/foundry/new/helpers/sol/lib/types/MatchComponentType.t.sol
new file mode 100644
index 000000000..de45b362b
--- /dev/null
+++ b/test/foundry/new/helpers/sol/lib/types/MatchComponentType.t.sol
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { Test } from "forge-std/Test.sol";
+
+import {
+ MatchComponent,
+ MatchComponentType
+} from "seaport-core/helpers/sol/lib/types/MatchComponentType.sol";
+
+contract MatchComponentTypeTest is Test {
+ using MatchComponentType for MatchComponent;
+
+ function testCreateGetAndUnpack() public {
+ MatchComponent memory component = MatchComponentType
+ .createMatchComponent(1, 2, 3);
+ assertEq(component.getAmount(), 1, "amount");
+ assertEq(component.getOrderIndex(), 2, "orderIndex");
+ assertEq(component.getItemIndex(), 3, "itemIndex");
+
+ (uint256 amount, uint256 orderIndex, uint256 itemIndex) = component
+ .unpack();
+ assertEq(amount, 1, "unpacked amount");
+ assertEq(orderIndex, 2, "unpacked orderIndex");
+ assertEq(itemIndex, 3, "unpacked itemIndex");
+ }
+
+ function testCreateGetAndUnpack(
+ uint240 amount,
+ uint8 orderIndex,
+ uint8 itemIndex
+ ) public {
+ MatchComponent memory component = MatchComponentType
+ .createMatchComponent(amount, orderIndex, itemIndex);
+ assertEq(component.getAmount(), amount, "amount");
+ assertEq(component.getOrderIndex(), orderIndex, "orderIndex");
+ assertEq(component.getItemIndex(), itemIndex, "itemIndex");
+
+ (
+ uint256 unpackedAmount,
+ uint256 unpackedOrderIndex,
+ uint256 unpackedItemIndex
+ ) = component.unpack();
+ assertEq(unpackedAmount, amount, "unpacked amount");
+ assertEq(unpackedOrderIndex, orderIndex, "unpacked orderIndex");
+ assertEq(unpackedItemIndex, itemIndex, "unpacked itemIndex");
+ }
+
+ function testSetters() public {
+ MatchComponent memory component = MatchComponentType
+ .createMatchComponent(1, 2, 3);
+
+ MatchComponent memory newComponent = component.setAmount(4);
+ assertEq(newComponent.getAmount(), 4, "amount");
+ assertEq(newComponent.getOrderIndex(), 2, "orderIndex");
+ assertEq(newComponent.getItemIndex(), 3, "itemIndex");
+
+ newComponent = component.setOrderIndex(5);
+ assertEq(newComponent.getAmount(), 1, "amount");
+ assertEq(newComponent.getOrderIndex(), 5, "orderIndex");
+ assertEq(newComponent.getItemIndex(), 3, "itemIndex");
+
+ newComponent = component.setItemIndex(6);
+ assertEq(newComponent.getAmount(), 1, "amount");
+ assertEq(newComponent.getOrderIndex(), 2, "orderIndex");
+ assertEq(newComponent.getItemIndex(), 6, "itemIndex");
+ }
+
+ function testSetters(
+ uint240 amount,
+ uint8 orderIndex,
+ uint8 itemIndex
+ ) public {
+ MatchComponent memory component = MatchComponentType
+ .createMatchComponent(1, 2, 3);
+
+ MatchComponent memory newComponent = component.setAmount(amount);
+ assertEq(newComponent.getAmount(), amount, "amount");
+ assertEq(newComponent.getOrderIndex(), 2, "orderIndex");
+ assertEq(newComponent.getItemIndex(), 3, "itemIndex");
+
+ newComponent = component.setOrderIndex(orderIndex);
+ assertEq(newComponent.getAmount(), 1, "amount");
+ assertEq(newComponent.getOrderIndex(), orderIndex, "orderIndex");
+ assertEq(newComponent.getItemIndex(), 3, "itemIndex");
+
+ newComponent = component.setItemIndex(itemIndex);
+ assertEq(newComponent.getAmount(), 1, "amount");
+ assertEq(newComponent.getOrderIndex(), 2, "orderIndex");
+ assertEq(newComponent.getItemIndex(), itemIndex, "itemIndex");
+ }
+}
diff --git a/test/foundry/new/zones/ValidationOffererZone.sol b/test/foundry/new/zones/ValidationOffererZone.sol
new file mode 100644
index 000000000..50747f7bf
--- /dev/null
+++ b/test/foundry/new/zones/ValidationOffererZone.sol
@@ -0,0 +1,159 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ItemType } from "seaport-sol/SeaportEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem,
+ ZoneParameters
+} from "seaport-sol/SeaportStructs.sol";
+
+import {
+ ContractOffererInterface
+} from "seaport-core/interfaces/ContractOffererInterface.sol";
+
+import { ZoneInterface } from "seaport-core/interfaces/ZoneInterface.sol";
+
+contract ValidationOffererZone is ContractOffererInterface, ZoneInterface {
+ error IncorrectSpentAmount(address fulfiller, bytes32 got, uint256 want);
+
+ uint256 expectedMaxSpentAmount;
+
+ constructor(uint256 expectedMax) {
+ expectedMaxSpentAmount = expectedMax;
+ }
+
+ receive() external payable {}
+
+ /**
+ * @dev Validates that the parties have received the correct items.
+ *
+ * @param zoneParameters The zone parameters, including the SpentItem and
+ * ReceivedItem arrays.
+ *
+ * @return validOrderMagicValue The magic value to indicate things are OK.
+ */
+ function validateOrder(
+ ZoneParameters calldata zoneParameters
+ ) external view override returns (bytes4 validOrderMagicValue) {
+ validate(zoneParameters.fulfiller, zoneParameters.offer);
+
+ // Return the selector of validateOrder as the magic value.
+ validOrderMagicValue = this.validateOrder.selector;
+ }
+
+ /**
+ * @dev Generates an order with the specified minimum and maximum spent items,
+ */
+ function generateOrder(
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata c
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ return previewOrder(address(this), address(this), a, b, c);
+ }
+
+ /**
+ * @dev View function to preview an order generated in response to a minimum
+ * set of received items, maximum set of spent items, and context
+ * (supplied as extraData).
+ */
+ function previewOrder(
+ address,
+ address,
+ SpentItem[] calldata a,
+ SpentItem[] calldata b,
+ bytes calldata
+ )
+ public
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ return (a, _convertSpentToReceived(b));
+ }
+
+ function _convertSpentToReceived(
+ SpentItem[] calldata spentItems
+ ) internal view returns (ReceivedItem[] memory) {
+ ReceivedItem[] memory receivedItems = new ReceivedItem[](
+ spentItems.length
+ );
+ for (uint256 i = 0; i < spentItems.length; ++i) {
+ receivedItems[i] = _convertSpentToReceived(spentItems[i]);
+ }
+ return receivedItems;
+ }
+
+ function _convertSpentToReceived(
+ SpentItem calldata spentItem
+ ) internal view returns (ReceivedItem memory) {
+ return
+ ReceivedItem({
+ itemType: spentItem.itemType,
+ token: spentItem.token,
+ identifier: spentItem.identifier,
+ amount: spentItem.amount,
+ recipient: payable(address(this))
+ });
+ }
+
+ function ratifyOrder(
+ SpentItem[] calldata spentItems /* offer */,
+ ReceivedItem[] calldata /* consideration */,
+ bytes calldata /* context */,
+ bytes32[] calldata /* orderHashes */,
+ uint256 /* contractNonce */
+ ) external view override returns (bytes4 /* ratifyOrderMagicValue */) {
+ validate(address(0), spentItems);
+ return ValidationOffererZone.ratifyOrder.selector;
+ }
+
+ function validate(
+ address fulfiller,
+ SpentItem[] calldata offer
+ ) internal view {
+ if (offer[0].amount > expectedMaxSpentAmount) {
+ revert IncorrectSpentAmount(
+ fulfiller,
+ bytes32(offer[0].amount),
+ expectedMaxSpentAmount
+ );
+ }
+ }
+
+ function getSeaportMetadata()
+ external
+ pure
+ override(ContractOffererInterface, ZoneInterface)
+ returns (string memory name, Schema[] memory schemas)
+ {
+ // Return the metadata.
+ name = "HashValidationZoneOfferer";
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ContractOffererInterface, ZoneInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ interfaceId == type(ZoneInterface).interfaceId;
+ }
+}
diff --git a/test/foundry/offerers/BadOfferer.t.sol b/test/foundry/offerers/BadOfferer.t.sol
index 94c05f15f..b6d3e45f5 100644
--- a/test/foundry/offerers/BadOfferer.t.sol
+++ b/test/foundry/offerers/BadOfferer.t.sol
@@ -108,7 +108,9 @@ contract BadOffererTest is BaseOrderTest, ZoneInteractionErrors {
seaport: consideration,
id: id,
eoa: false,
- shouldFail: false // shouldn't fail because the revert happens within GenerateOrder, so it can be safely skipped
+ // shouldn't fail because the revert happens within
+ // GenerateOrder, so it can be safely skipped
+ shouldFail: false
})
);
test(
diff --git a/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol b/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol
index 038d3829d..57a9bb634 100644
--- a/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol
+++ b/test/foundry/offerers/ContractOffersNativeTokenOfferItems.t.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
-import "forge-std/Test.sol";
+import { Test } from "forge-std/Test.sol";
import { BaseOrderTest } from "../utils/BaseOrderTest.sol";
diff --git a/test/foundry/offerers/TestPoolOffererImpl.t.sol b/test/foundry/offerers/TestPoolOffererImpl.t.sol
index f5ce478ae..ffa1a83a5 100644
--- a/test/foundry/offerers/TestPoolOffererImpl.t.sol
+++ b/test/foundry/offerers/TestPoolOffererImpl.t.sol
@@ -22,6 +22,12 @@ import {
IERC20
} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
+import {
+ ContractOffererInterface
+} from "../../../contracts/interfaces/ContractOffererInterface.sol";
+
+import { ERC165 } from "../../../contracts/interfaces/ERC165.sol";
+
import { TestERC20 } from "../../../contracts/test/TestERC20.sol";
import { TestERC721 } from "../../../contracts/test/TestERC721.sol";
@@ -58,7 +64,7 @@ contract TestPoolFactoryImpl {
}
}
-contract TestPoolImpl is TestPoolOfferer {
+contract TestPoolImpl is ERC165, TestPoolOfferer {
using EnumerableSet for EnumerableSet.UintSet;
constructor(
@@ -81,6 +87,12 @@ contract TestPoolImpl is TestPoolOfferer {
function inTokenIds(uint256 id) external view returns (bool) {
return tokenIds.contains(id);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override(ERC165, TestPoolOfferer) returns (bool) {
+ return super.supportsInterface(interfaceId);
+ }
}
contract TestPoolOffererImpl is Test {
diff --git a/test/foundry/offerers/impl/AdjustedAmountOfferer.sol b/test/foundry/offerers/impl/AdjustedAmountOfferer.sol
index ff739a9f8..f4e924e4d 100644
--- a/test/foundry/offerers/impl/AdjustedAmountOfferer.sol
+++ b/test/foundry/offerers/impl/AdjustedAmountOfferer.sol
@@ -9,13 +9,15 @@ import {
ContractOffererInterface
} from "../../../../contracts/interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import {
SpentItem,
ReceivedItem,
Schema
} from "../../../../contracts/lib/ConsiderationStructs.sol";
-contract AdjustedAmountOfferer is ContractOffererInterface {
+contract AdjustedAmountOfferer is ContractOffererInterface, ERC165 {
int256 immutable offerAmountAdjust;
int256 immutable considerationAmountAdjust;
@@ -122,6 +124,20 @@ contract AdjustedAmountOfferer is ContractOffererInterface {
return AdjustedAmountOfferer.ratifyOrder.selector;
}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/test/foundry/offerers/impl/BadOfferer.sol b/test/foundry/offerers/impl/BadOfferer.sol
index 51f5d151d..4006b8001 100644
--- a/test/foundry/offerers/impl/BadOfferer.sol
+++ b/test/foundry/offerers/impl/BadOfferer.sol
@@ -10,19 +10,21 @@ import {
ContractOffererInterface
} from "../../../../contracts/interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import { ItemType } from "../../../../contracts/lib/ConsiderationEnums.sol";
import {
- SpentItem,
ReceivedItem,
- Schema
+ Schema,
+ SpentItem
} from "../../../../contracts/lib/ConsiderationStructs.sol";
interface ERC20Mintable {
function mint(address to, uint256 amount) external;
}
-contract BadOfferer is ContractOffererInterface {
+contract BadOfferer is ERC165, ContractOffererInterface {
error IntentionalRevert();
ERC20Interface token1;
@@ -47,7 +49,8 @@ contract BadOfferer is ContractOffererInterface {
}
/**
- * @dev Generates an order with the specified minimum and maximum spent items,
+ * @dev Generates an order with the specified minimum and maximum spent
+ * items.
*/
function generateOrder(
address a,
@@ -123,6 +126,20 @@ contract BadOfferer is ContractOffererInterface {
return BadOfferer.ratifyOrder.selector;
}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/test/foundry/offerers/impl/PassthroughOfferer.sol b/test/foundry/offerers/impl/PassthroughOfferer.sol
index 40264a5cf..96b2ac1c5 100644
--- a/test/foundry/offerers/impl/PassthroughOfferer.sol
+++ b/test/foundry/offerers/impl/PassthroughOfferer.sol
@@ -10,13 +10,15 @@ import {
ContractOffererInterface
} from "../../../../contracts/interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import {
SpentItem,
ReceivedItem,
Schema
} from "../../../../contracts/lib/ConsiderationStructs.sol";
-contract PassthroughOfferer is ContractOffererInterface {
+contract PassthroughOfferer is ERC165, ContractOffererInterface {
constructor(
address[] memory seaports,
ERC20Interface _token1,
@@ -101,6 +103,20 @@ contract PassthroughOfferer is ContractOffererInterface {
return PassthroughOfferer.ratifyOrder.selector;
}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/test/foundry/offerers/impl/StatefulRatifierOfferer.sol b/test/foundry/offerers/impl/StatefulRatifierOfferer.sol
index c106afd39..14f03550b 100644
--- a/test/foundry/offerers/impl/StatefulRatifierOfferer.sol
+++ b/test/foundry/offerers/impl/StatefulRatifierOfferer.sol
@@ -10,6 +10,8 @@ import {
ContractOffererInterface
} from "../../../../contracts/interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import {
ItemType,
Side
@@ -25,7 +27,7 @@ interface ERC20Mintable {
function mint(address to, uint256 amount) external;
}
-contract StatefulRatifierOfferer is ContractOffererInterface {
+contract StatefulRatifierOfferer is ERC165, ContractOffererInterface {
error IncorrectValue(uint256 actual, uint256 expected);
error IncorrectToken(address actual, address expected);
error IncorrectItemType(ItemType actual, ItemType expected);
@@ -262,6 +264,20 @@ contract StatefulRatifierOfferer is ContractOffererInterface {
return ContractOffererInterface.ratifyOrder.selector;
}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/test/foundry/offerers/impl/TestPoolOfferer.sol b/test/foundry/offerers/impl/TestPoolOfferer.sol
index 40ca802fb..568c80d04 100644
--- a/test/foundry/offerers/impl/TestPoolOfferer.sol
+++ b/test/foundry/offerers/impl/TestPoolOfferer.sol
@@ -5,6 +5,8 @@ import {
ContractOffererInterface
} from "../../../../contracts/interfaces/ContractOffererInterface.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import { ItemType } from "../../../../contracts/lib/ConsiderationEnums.sol";
import {
@@ -27,7 +29,7 @@ import {
import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
-contract TestPoolOfferer is ContractOffererInterface, Ownable {
+contract TestPoolOfferer is ERC165, ContractOffererInterface, Ownable {
using EnumerableSet for EnumerableSet.UintSet;
error OnlySeaport();
@@ -192,6 +194,20 @@ contract TestPoolOfferer is ContractOffererInterface, Ownable {
return ContractOffererInterface.ratifyOrder.selector;
}
+ function supportsInterface(
+ bytes4 interfaceId
+ )
+ public
+ view
+ virtual
+ override(ERC165, ContractOffererInterface)
+ returns (bool)
+ {
+ return
+ interfaceId == type(ContractOffererInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
+
/**
* @dev Returns the metadata for this contract offerer.
*/
diff --git a/test/foundry/utils/BaseConsiderationTest.sol b/test/foundry/utils/BaseConsiderationTest.sol
index efd20fecf..9d0935e46 100644
--- a/test/foundry/utils/BaseConsiderationTest.sol
+++ b/test/foundry/utils/BaseConsiderationTest.sol
@@ -28,8 +28,6 @@ import { DifferentialTest } from "./DifferentialTest.sol";
import { StructCopier } from "./StructCopier.sol";
-import { stdStorage, StdStorage } from "forge-std/Test.sol";
-
import { Conduit } from "../../../contracts/conduit/Conduit.sol";
import { Consideration } from "../../../contracts/lib/Consideration.sol";
@@ -40,8 +38,6 @@ import {
/// @dev Base test case that deploys Consideration and its dependencies
contract BaseConsiderationTest is DifferentialTest, StructCopier {
- using stdStorage for StdStorage;
-
ConsiderationInterface consideration;
ConsiderationInterface referenceConsideration;
bytes32 conduitKeyOne;
diff --git a/test/foundry/utils/BaseOrderTest.sol b/test/foundry/utils/BaseOrderTest.sol
index b061f05b2..78034688a 100644
--- a/test/foundry/utils/BaseOrderTest.sol
+++ b/test/foundry/utils/BaseOrderTest.sol
@@ -1,7 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
-import { stdStorage, StdStorage } from "forge-std/Test.sol";
+import {
+ FulfillAvailableHelper,
+ MatchFulfillmentHelper
+} from "seaport-sol/SeaportSol.sol";
+
+import {
+ AdditionalRecipient,
+ Fulfillment,
+ FulfillmentComponent,
+ Order,
+ OrderComponents,
+ OrderParameters
+} from "seaport-sol/SeaportStructs.sol";
import {
ConsiderationInterface
@@ -13,14 +25,6 @@ import {
BasicOrder_additionalRecipients_data_cdPtr,
TwoWords
} from "../../../contracts/lib/ConsiderationConstants.sol";
-import {
- AdditionalRecipient,
- Fulfillment,
- FulfillmentComponent,
- Order,
- OrderComponents,
- OrderParameters
-} from "../../../contracts/lib/ConsiderationStructs.sol";
import { ArithmeticUtil } from "./ArithmeticUtil.sol";
@@ -30,12 +34,13 @@ import { AmountDeriver } from "../../../contracts/lib/AmountDeriver.sol";
/// @dev base test class for cases that depend on pre-deployed token contracts
contract BaseOrderTest is OrderBuilder, AmountDeriver {
- using stdStorage for StdStorage;
using ArithmeticUtil for uint256;
using ArithmeticUtil for uint128;
using ArithmeticUtil for uint120;
- ///@dev used to store address and key outputs from makeAddrAndKey(name)
+ /**
+ * @dev used to store address and key outputs from makeAddrAndKey(name)
+ */
struct Account {
address addr;
uint256 key;
@@ -57,6 +62,10 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
AdditionalRecipient[] additionalRecipients;
Account offerer1;
+ Account offerer2;
+
+ FulfillAvailableHelper fulfill;
+ MatchFulfillmentHelper matcher;
event Transfer(address indexed from, address indexed to, uint256 value);
@@ -81,14 +90,18 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
_;
}
- /// @dev convenience wrapper for makeAddrAndKey
+ /**
+ * @dev convenience wrapper for makeAddrAndKey
+ */
function makeAccount(string memory name) internal returns (Account memory) {
(address addr, uint256 key) = makeAddrAndKey(name);
return Account(addr, key);
}
- /// @dev convenience wrapper for makeAddrAndKey that also allocates tokens,
- /// ether, and approvals
+ /**
+ * @dev convenience wrapper for makeAddrAndKey that also allocates tokens,
+ * ether, and approvals
+ */
function makeAndAllocateAccount(
string memory name
) internal returns (Account memory) {
@@ -116,8 +129,13 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
allocateTokensAndApprovals(bob, uint128(MAX_INT));
allocateTokensAndApprovals(cal, uint128(MAX_INT));
allocateTokensAndApprovals(offerer1.addr, uint128(MAX_INT));
+ allocateTokensAndApprovals(offerer2.addr, uint128(MAX_INT));
offerer1 = makeAndAllocateAccount("offerer1");
+ offerer2 = makeAndAllocateAccount("offerer2");
+
+ fulfill = new FulfillAvailableHelper();
+ matcher = new MatchFulfillmentHelper();
}
function resetOfferComponents() internal {
@@ -275,8 +293,8 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
if (overwriteItemsLength) {
// Get the array length from the calldata and
// store the length - amtToSubtractFromItemsLength in the calldata
- // so that the length value does _not_ accurately represent the actual
- // total array length.
+ // so that the length value does _not_ accurately represent the
+ // actual total array length.
_subtractAmountFromLengthInOrderCalldata(
fulfillOrderCalldata,
relativeOrderParametersOffset,
@@ -305,9 +323,9 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
fulfillOrderCalldata
);
- // If overwriteItemsLength is True, the call should
- // have failed (success should be False) and if overwriteItemsLength is False,
- // the call should have succeeded (success should be True).
+ // If overwriteItemsLength is true, the call should
+ // have failed (success should be False) and if overwriteItemsLength is
+ // false, the call should have succeeded (success should be true).
assertEq(success, !overwriteItemsLength);
}
@@ -331,7 +349,8 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
}
/**
- * @dev return OrderComponents for a given OrderParameters and offerer counter
+ * @dev return OrderComponents for a given OrderParameters and offerer
+ * counter
*/
function getOrderComponents(
OrderParameters memory parameters,
@@ -393,7 +412,10 @@ contract BaseOrderTest is OrderBuilder, AmountDeriver {
);
}
- ///@dev allow signing for this contract since it needs to be recipient of basic order to reenter on receive
+ /**
+ * @dev allow signing for this contract since it needs to be recipient of
+ * basic order to reenter on receive
+ */
function isValidSignature(
bytes32,
bytes memory
diff --git a/test/foundry/utils/EIP712MerkleTree.sol b/test/foundry/utils/EIP712MerkleTree.sol
index b20a7d96b..0c1c98bce 100644
--- a/test/foundry/utils/EIP712MerkleTree.sol
+++ b/test/foundry/utils/EIP712MerkleTree.sol
@@ -2,20 +2,27 @@
pragma solidity ^0.8.17;
import { MurkyBase } from "murky/common/MurkyBase.sol";
+
import {
TypehashDirectory
} from "../../../contracts/test/TypehashDirectory.sol";
+
import { Test } from "forge-std/Test.sol";
+
import {
ConsiderationInterface
} from "../../../contracts/interfaces/ConsiderationInterface.sol";
+
import {
OrderComponents
} from "../../../contracts/lib/ConsiderationStructs.sol";
+
import { Math } from "openzeppelin-contracts/contracts/utils/math/Math.sol";
-///@dev Seaport doesn't sort leaves when hashing for bulk orders, but Murky
-/// does, so implement a custom hashLeafPairs function
+/**
+ * @dev Seaport doesn't sort leaves when hashing for bulk orders, but Murky
+ * does, so implement a custom hashLeafPairs function
+ */
contract MerkleUnsorted is MurkyBase {
function hashLeafPairs(
bytes32 left,
@@ -40,10 +47,12 @@ contract EIP712MerkleTree is Test {
merkle = new MerkleUnsorted();
}
- /// @dev Creates a single bulk signature: a base signature + a three byte
- /// index + a series of 32 byte proofs. The height of the tree is
- /// determined by the length of the orderComponents array and only
- /// fills empty orders into the tree to make the length a power of 2.
+ /**
+ * @dev Creates a single bulk signature: a base signature + a three byte
+ * index + a series of 32 byte proofs. The height of the tree is determined
+ * by the length of the orderComponents array and only fills empty orders
+ * into the tree to make the length a power of 2.
+ */
function signBulkOrder(
ConsiderationInterface consideration,
uint256 privateKey,
@@ -99,10 +108,12 @@ contract EIP712MerkleTree is Test {
);
}
- /// @dev Creates a single bulk signature: a base signature + a three byte
- /// index + a series of 32 byte proofs. The height of the tree is
- /// determined by the height parameter and this function will fill
- /// empty orders into the tree until the specified height is reached.
+ /**
+ * @dev Creates a single bulk signature: a base signature + a three byte
+ * index + a series of 32 byte proofs. The height of the tree is determined
+ * by the height parameter and this function will fill empty orders into the
+ * tree until the specified height is reached.
+ */
function signSparseBulkOrder(
ConsiderationInterface consideration,
uint256 privateKey,
@@ -150,7 +161,89 @@ contract EIP712MerkleTree is Test {
mstore(0x20, root)
}
// else it is even and our "root" is first component
- // (this can def be done in a branchless way but who has the time??)
+ // (this can def be done in a branchless way but who has the
+ // time??)
+ if iszero(and(hashIndex, 1)) {
+ mstore(0, root)
+ mstore(0x20, heightEmptyHash)
+ }
+ // compute new intermediate hash (or final root)
+ root := keccak256(0, 0x40)
+ }
+ // divide hashIndex by 2 to get index of next layer
+ // 0 -> 0
+ // 1 -> 0
+ // 2 -> 1
+ // 3 -> 1
+ // etc
+ hashIndex /= 2;
+ }
+
+ return
+ _getSignature(
+ consideration,
+ privateKey,
+ _lookupBulkOrderTypehash(height),
+ root,
+ emptyHashes,
+ orderIndex,
+ useCompact2098
+ );
+ }
+
+ /**
+ * @dev Creates a single bulk signature: a base signature + a three byte
+ * index + a series of 32 byte proofs. The height of the tree is determined
+ * by the height parameter and this function will fill empty orders into the
+ * tree until the specified height is reached.
+ */
+ function signSparseBulkOrder(
+ ConsiderationInterface consideration,
+ uint256 privateKey,
+ bytes32 orderHash,
+ uint256 height,
+ uint24 orderIndex,
+ bool useCompact2098
+ ) public view returns (bytes memory) {
+ require(orderIndex < 2 ** height, "orderIndex out of bounds");
+ // get initial empty order components hash
+ bytes32 emptyComponentsHash = consideration.getOrderHash(
+ emptyOrderComponents
+ );
+
+ // calculate intermediate hashes of a sparse order tree
+ // this will also serve as our proof
+ bytes32[] memory emptyHashes = new bytes32[]((height));
+ // first layer is empty order hash
+ emptyHashes[0] = emptyComponentsHash;
+ for (uint256 i = 1; i < height; i++) {
+ bytes32 nextHash;
+ bytes32 lastHash = emptyHashes[i - 1];
+ // subsequent layers are hash of emptyHeight+emptyHeight
+ assembly {
+ mstore(0, lastHash)
+ mstore(0x20, lastHash)
+ nextHash := keccak256(0, 0x40)
+ }
+ emptyHashes[i] = nextHash;
+ }
+ // begin calculating order tree root
+ bytes32 root = orderHash;
+ // hashIndex is the index within the layer of the non-sparse hash
+ uint24 hashIndex = orderIndex;
+
+ for (uint256 i = 0; i < height; i++) {
+ // get sparse hash at this height
+ bytes32 heightEmptyHash = emptyHashes[i];
+ assembly {
+ // if the hashIndex is odd, our "root" is second component
+ if and(hashIndex, 1) {
+ mstore(0, heightEmptyHash)
+ mstore(0x20, root)
+ }
+ // else it is even and our "root" is first component
+ // (this can def be done in a branchless way but who has the
+ // time??)
if iszero(and(hashIndex, 1)) {
mstore(0, root)
mstore(0x20, heightEmptyHash)
@@ -179,7 +272,9 @@ contract EIP712MerkleTree is Test {
);
}
- /// @dev same lookup seaport optimized does
+ /**
+ * @dev same lookup seaport optimized does
+ */
function _lookupBulkOrderTypehash(
uint256 treeHeight
) internal view returns (bytes32 typeHash) {
diff --git a/test/foundry/zone/PostFulfillmentCheck.t.sol b/test/foundry/zone/PostFulfillmentCheck.t.sol
index 1d321c00c..e012cdfe1 100644
--- a/test/foundry/zone/PostFulfillmentCheck.t.sol
+++ b/test/foundry/zone/PostFulfillmentCheck.t.sol
@@ -3,8 +3,6 @@ pragma solidity ^0.8.17;
import { BaseOrderTest } from "../utils/BaseOrderTest.sol";
-import { BaseConduitTest } from "../conduit/BaseConduitTest.sol";
-
import { TestZone } from "./impl/TestZone.sol";
import {
@@ -16,20 +14,20 @@ import {
} from "./impl/PostFullfillmentStatefulTestZone.sol";
import {
- ConsiderationItem,
- OfferItem,
- ItemType,
+ AdditionalRecipient,
AdvancedOrder,
- CriteriaResolver,
BasicOrderParameters,
- AdditionalRecipient,
- FulfillmentComponent
+ ConsiderationItem,
+ CriteriaResolver,
+ FulfillmentComponent,
+ ItemType,
+ OfferItem
} from "../../../contracts/lib/ConsiderationStructs.sol";
import {
+ BasicOrderType,
OrderType,
- Side,
- BasicOrderType
+ Side
} from "../../../contracts/lib/ConsiderationEnums.sol";
import {
@@ -453,7 +451,8 @@ contract PostFulfillmentCheckTest is BaseOrderTest {
address[] memory allAdditional = new address[](
uint256(context.numOriginalAdditional) + context.numTips
);
- // make new stateful zone with a larger amount so each additional recipient can receive
+ // make new stateful zone with a larger amount so each additional
+ // recipient can receive
statefulZone = new PostFulfillmentStatefulTestZone(5000);
// clear storage array just in case
delete additionalRecipients;
@@ -472,7 +471,8 @@ contract PostFulfillmentCheckTest is BaseOrderTest {
allAdditional[i] = recipient;
// add to consideration items that will be hashed with order
addErc20ConsiderationItem(recipient, 1);
- // add to the additional recipients array included with the basic order
+ // add to the additional recipients array included with the basic
+ // order
additionalRecipients.push(
AdditionalRecipient({ recipient: recipient, amount: 1 })
);
@@ -486,7 +486,8 @@ contract PostFulfillmentCheckTest is BaseOrderTest {
// add to all additional
allAdditional[i + context.numOriginalAdditional] = recipient;
// do not add to consideration items that will be hashed with order
- // add to the additional recipients array included with the basic order
+ // add to the additional recipients array included with the basic
+ // order
additionalRecipients.push(
AdditionalRecipient({ recipient: recipient, amount: 1 })
);
@@ -636,12 +637,22 @@ contract PostFulfillmentCheckTest is BaseOrderTest {
numTips: 0
})
);
+ test(
+ this.execMatchAdvancedOrdersWithConduit,
+ Context({
+ consideration: referenceConsideration,
+ numOriginalAdditional: 0,
+ numTips: 0
+ })
+ );
}
function execMatchAdvancedOrdersWithConduit(
Context memory context
) external stateless {
- TestTransferValidationZoneOfferer transferValidationZone = new TestTransferValidationZoneOfferer();
+ TestTransferValidationZoneOfferer transferValidationZone = new TestTransferValidationZoneOfferer(
+ address(0)
+ );
addErc20OfferItem(50);
addErc721ConsiderationItem(alice, 42);
diff --git a/test/foundry/zone/TestTransferValidationZoneFuzz.t.sol b/test/foundry/zone/TestTransferValidationZoneFuzz.t.sol
new file mode 100644
index 000000000..d52eea43c
--- /dev/null
+++ b/test/foundry/zone/TestTransferValidationZoneFuzz.t.sol
@@ -0,0 +1,1437 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import { BaseOrderTest } from "../utils/BaseOrderTest.sol";
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ Fulfillment,
+ FulfillmentComponent,
+ ItemType,
+ OfferItem,
+ Order,
+ OrderComponents,
+ OrderType
+} from "../../../contracts/lib/ConsiderationStructs.sol";
+
+import { UnavailableReason } from "seaport-sol/SpaceEnums.sol";
+
+import {
+ ConsiderationInterface
+} from "../../../contracts/interfaces/ConsiderationInterface.sol";
+
+import {
+ ConsiderationItemLib,
+ FulfillmentComponentLib,
+ FulfillmentLib,
+ OfferItemLib,
+ OrderComponentsLib,
+ OrderLib,
+ SeaportArrays
+} from "../../../contracts/helpers/sol/lib/SeaportStructLib.sol";
+
+import {
+ TestTransferValidationZoneOfferer
+} from "../../../contracts/test/TestTransferValidationZoneOfferer.sol";
+
+import {
+ FulfillAvailableHelper
+} from "seaport-sol/fulfillments/available/FulfillAvailableHelper.sol";
+
+import {
+ MatchFulfillmentHelper
+} from "seaport-sol/fulfillments/match/MatchFulfillmentHelper.sol";
+
+import { TestZone } from "./impl/TestZone.sol";
+
+contract TestTransferValidationZoneOffererTest is BaseOrderTest {
+ using FulfillmentLib for Fulfillment;
+ using FulfillmentComponentLib for FulfillmentComponent;
+ using FulfillmentComponentLib for FulfillmentComponent[];
+ using OfferItemLib for OfferItem;
+ using OfferItemLib for OfferItem[];
+ using ConsiderationItemLib for ConsiderationItem;
+ using ConsiderationItemLib for ConsiderationItem[];
+ using OrderComponentsLib for OrderComponents;
+ using OrderLib for Order;
+ using OrderLib for Order[];
+
+ MatchFulfillmentHelper matchFulfillmentHelper;
+ FulfillAvailableHelper fulfillAvailableFulfillmentHelper;
+ TestTransferValidationZoneOfferer zone;
+ TestZone testZone;
+
+ // constant strings for recalling struct lib defaults
+ // ideally these live in a base test class
+ string constant SINGLE_721 = "single 721";
+ string constant VALIDATION_ZONE = "validation zone";
+
+ function setUp() public virtual override {
+ super.setUp();
+ matchFulfillmentHelper = new MatchFulfillmentHelper();
+ fulfillAvailableFulfillmentHelper = new FulfillAvailableHelper();
+ zone = new TestTransferValidationZoneOfferer(address(0));
+ testZone = new TestZone();
+
+ // create a default consideration for a single 721;
+ // note that it does not have recipient, token or
+ // identifier set
+ ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withStartAmount(1)
+ .withEndAmount(1)
+ .saveDefault(SINGLE_721);
+
+ // create a default offerItem for a single 721;
+ // note that it does not have token or identifier set
+ OfferItemLib
+ .empty()
+ .withItemType(ItemType.ERC721)
+ .withStartAmount(1)
+ .withEndAmount(1)
+ .saveDefault(SINGLE_721);
+
+ OrderComponentsLib
+ .empty()
+ .withOfferer(offerer1.addr)
+ .withZone(address(zone))
+ // fill in offer later
+ // fill in consideration later
+ .withOrderType(OrderType.FULL_RESTRICTED)
+ .withStartTime(block.timestamp)
+ .withEndTime(block.timestamp + 1)
+ .withZoneHash(bytes32(0)) // not strictly necessary
+ .withSalt(0)
+ .withConduitKey(conduitKeyOne)
+ .saveDefault(VALIDATION_ZONE);
+ // fill in counter later
+ }
+
+ struct Context {
+ ConsiderationInterface seaport;
+ FulfillFuzzInputs fulfillArgs;
+ MatchFuzzInputs matchArgs;
+ }
+
+ struct FulfillFuzzInputs {
+ uint256 tokenId;
+ uint128 amount;
+ uint128 excessNativeTokens;
+ uint256 orderCount;
+ uint256 considerationItemsPerOrderCount;
+ uint256 maximumFulfilledCount;
+ address offerRecipient;
+ address considerationRecipient;
+ bytes32 zoneHash;
+ uint256 salt;
+ bool shouldAggregateFulfillmentComponents;
+ bool shouldUseConduit;
+ bool shouldUseTransferValidationZone;
+ bool shouldIncludeNativeConsideration;
+ bool shouldIncludeExcessOfferItems;
+ bool shouldSpecifyRecipient;
+ bool shouldIncludeJunkDataInAdvancedOrder;
+ }
+
+ struct MatchFuzzInputs {
+ uint256 tokenId;
+ uint128 amount;
+ uint128 excessNativeTokens;
+ uint256 orderPairCount;
+ uint256 considerationItemsPerPrimeOrderCount;
+ // This is currently used only as the unspent prime offer item recipient
+ // but would also set the recipient for unspent mirror offer items if
+ // any were added in the test in the future.
+ address unspentPrimeOfferItemRecipient;
+ string primeOfferer;
+ string mirrorOfferer;
+ bytes32 zoneHash;
+ uint256 salt;
+ bool shouldUseConduit;
+ bool shouldUseTransferValidationZoneForPrime;
+ bool shouldUseTransferValidationZoneForMirror;
+ bool shouldIncludeNativeConsideration;
+ bool shouldIncludeExcessOfferItems;
+ bool shouldSpecifyUnspentOfferItemRecipient;
+ bool shouldIncludeJunkDataInAdvancedOrder;
+ }
+
+ // Used for stack depth management.
+ struct MatchAdvancedOrdersInfra {
+ Order[] orders;
+ Fulfillment[] fulfillments;
+ AdvancedOrder[] advancedOrders;
+ CriteriaResolver[] criteriaResolvers;
+ uint256 callerBalanceBefore;
+ uint256 callerBalanceAfter;
+ uint256 primeOffererBalanceBefore;
+ uint256 primeOffererBalanceAfter;
+ }
+
+ // Used for stack depth management.
+ struct FulfillAvailableAdvancedOrdersInfra {
+ AdvancedOrder[] advancedOrders;
+ FulfillmentComponent[][] offerFulfillmentComponents;
+ FulfillmentComponent[][] considerationFulfillmentComponents;
+ CriteriaResolver[] criteriaResolvers;
+ uint256 callerBalanceBefore;
+ uint256 callerBalanceAfter;
+ uint256 considerationRecipientNativeBalanceBefore;
+ uint256 considerationRecipientToken1BalanceBefore;
+ uint256 considerationRecipientToken2BalanceBefore;
+ uint256 considerationRecipientNativeBalanceAfter;
+ uint256 considerationRecipientToken1BalanceAfter;
+ uint256 considerationRecipientToken2BalanceAfter;
+ }
+
+ // Used for stack depth management.
+ struct OrderAndFulfillmentInfra {
+ OfferItem[] offerItemArray;
+ ConsiderationItem[] considerationItemArray;
+ OrderComponents orderComponents;
+ Order[] orders;
+ Fulfillment fulfillment;
+ Fulfillment[] fulfillments;
+ }
+
+ // Used for stack depth management.
+ struct OrderComponentInfra {
+ OrderComponents orderComponents;
+ OrderComponents[] orderComponentsArray;
+ OfferItem[][] offerItemArray;
+ ConsiderationItem[][] considerationItemArray;
+ ConsiderationItem nativeConsiderationItem;
+ ConsiderationItem erc20ConsiderationItemOne;
+ ConsiderationItem erc20ConsiderationItemTwo;
+ }
+
+ FulfillFuzzInputs emptyFulfill;
+ MatchFuzzInputs emptyMatch;
+
+ Account fuzzPrimeOfferer;
+ Account fuzzMirrorOfferer;
+
+ function test(
+ function(Context memory) external fn,
+ Context memory context
+ ) internal {
+ try fn(context) {
+ fail("Expected revert");
+ } catch (bytes memory reason) {
+ assertPass(reason);
+ }
+ }
+
+ function testMatchAdvancedOrdersFuzz(
+ MatchFuzzInputs memory matchArgs
+ ) public {
+ // Avoid weird overflow issues.
+ matchArgs.amount = uint128(
+ bound(matchArgs.amount, 1, 0xffffffffffffffff)
+ );
+ // Avoid trying to mint the same token.
+ matchArgs.tokenId = bound(matchArgs.tokenId, 0xff, 0xffffffffffffffff);
+ // Make 1-8 order pairs per call. Each order pair will have 1-2 offer
+ // items on the prime side (depending on whether
+ // shouldIncludeExcessOfferItems is true or false).
+ matchArgs.orderPairCount = bound(matchArgs.orderPairCount, 1, 8);
+ // Use 1-3 (prime) consideration items per order.
+ matchArgs.considerationItemsPerPrimeOrderCount = bound(
+ matchArgs.considerationItemsPerPrimeOrderCount,
+ 1,
+ 3
+ );
+ // To put three items in the consideration, native tokens must be
+ // included.
+ matchArgs.shouldIncludeNativeConsideration =
+ matchArgs.shouldIncludeNativeConsideration ||
+ matchArgs.considerationItemsPerPrimeOrderCount >= 3;
+ // Only include an excess offer item when NOT using the transfer
+ // validation zone or the zone will revert.
+ matchArgs.shouldIncludeExcessOfferItems =
+ matchArgs.shouldIncludeExcessOfferItems &&
+ !(matchArgs.shouldUseTransferValidationZoneForPrime ||
+ matchArgs.shouldUseTransferValidationZoneForMirror);
+ // Include some excess native tokens to check that they're ending up
+ // with the caller afterward.
+ matchArgs.excessNativeTokens = uint128(
+ bound(
+ matchArgs.excessNativeTokens,
+ 0,
+ 0xfffffffffffffffffffffffffffff
+ )
+ );
+ // Don't set the offer recipient to the null address, because that's the
+ // way to indicate that the caller should be the recipient.
+ matchArgs.unspentPrimeOfferItemRecipient = _nudgeAddressIfProblematic(
+ address(
+ uint160(
+ bound(
+ uint160(matchArgs.unspentPrimeOfferItemRecipient),
+ 1,
+ type(uint160).max
+ )
+ )
+ )
+ );
+
+ // TODO: REMOVE: I probably need to create an array of addresses with
+ // dirty balances and an array of addresses that are contracts that
+ // cause problems with native token transfers.
+
+ test(
+ this.execMatchAdvancedOrdersFuzz,
+ Context(consideration, emptyFulfill, matchArgs)
+ );
+ test(
+ this.execMatchAdvancedOrdersFuzz,
+ Context(referenceConsideration, emptyFulfill, matchArgs)
+ );
+ }
+
+ function execMatchAdvancedOrdersFuzz(
+ Context memory context
+ ) external stateless {
+ // Set up the infrastructure for this function in a struct to avoid
+ // stack depth issues.
+ MatchAdvancedOrdersInfra memory infra = MatchAdvancedOrdersInfra({
+ orders: new Order[](context.matchArgs.orderPairCount),
+ fulfillments: new Fulfillment[](context.matchArgs.orderPairCount),
+ advancedOrders: new AdvancedOrder[](
+ context.matchArgs.orderPairCount
+ ),
+ criteriaResolvers: new CriteriaResolver[](0),
+ callerBalanceBefore: 0,
+ callerBalanceAfter: 0,
+ primeOffererBalanceBefore: 0,
+ primeOffererBalanceAfter: 0
+ });
+
+ // TODO: (Someday) See if the stack can tolerate fuzzing criteria
+ // resolvers.
+
+ // The prime offerer is offering NFTs and considering ERC20/Native.
+ fuzzPrimeOfferer = makeAndAllocateAccount(
+ context.matchArgs.primeOfferer
+ );
+ // The mirror offerer is offering ERC20/Native and considering NFTs.
+ fuzzMirrorOfferer = makeAndAllocateAccount(
+ context.matchArgs.mirrorOfferer
+ );
+
+ // Set fuzzMirrorOfferer as the zone's expected offer recipient.
+ zone.setExpectedOfferRecipient(fuzzMirrorOfferer.addr);
+
+ // Create the orders and fulfuillments.
+ (
+ infra.orders,
+ infra.fulfillments
+ ) = _buildOrdersAndFulfillmentsMirrorOrdersFromFuzzArgs(context);
+
+ // Set up the advanced orders array.
+ infra.advancedOrders = new AdvancedOrder[](infra.orders.length);
+
+ // Convert the orders to advanced orders.
+ for (uint256 i = 0; i < infra.orders.length; i++) {
+ infra.advancedOrders[i] = infra.orders[i].toAdvancedOrder(
+ 1,
+ 1,
+ context.matchArgs.shouldIncludeJunkDataInAdvancedOrder
+ ? bytes(abi.encodePacked(context.matchArgs.salt))
+ : bytes("")
+ );
+ }
+
+ // Set up event expectations.
+ if (
+ // If the fuzzPrimeOfferer and fuzzMirrorOfferer are the same
+ // address, then the ERC20 transfers will be filtered.
+ fuzzPrimeOfferer.addr != fuzzMirrorOfferer.addr
+ ) {
+ if (
+ // When shouldIncludeNativeConsideration is false, there will be
+ // exactly one token1 consideration item per orderPairCount. And
+ // they'll all get aggregated into a single transfer.
+ !context.matchArgs.shouldIncludeNativeConsideration
+ ) {
+ // This checks that the ERC20 transfers were all aggregated into
+ // a single transfer.
+ vm.expectEmit(true, true, false, true, address(token1));
+ emit Transfer(
+ address(fuzzMirrorOfferer.addr), // from
+ address(fuzzPrimeOfferer.addr), // to
+ context.matchArgs.amount * context.matchArgs.orderPairCount
+ );
+ }
+
+ if (
+ // When considerationItemsPerPrimeOrderCount is 3, there will be
+ // exactly one token2 consideration item per orderPairCount.
+ // And they'll all get aggregated into a single transfer.
+ context.matchArgs.considerationItemsPerPrimeOrderCount >= 3
+ ) {
+ vm.expectEmit(true, true, false, true, address(token2));
+ emit Transfer(
+ address(fuzzMirrorOfferer.addr), // from
+ address(fuzzPrimeOfferer.addr), // to
+ context.matchArgs.amount * context.matchArgs.orderPairCount
+ );
+ }
+ }
+
+ // Store the native token balances before the call for later reference.
+ infra.callerBalanceBefore = address(this).balance;
+ infra.primeOffererBalanceBefore = address(fuzzPrimeOfferer.addr)
+ .balance;
+
+ // Make the call to Seaport.
+ context.seaport.matchAdvancedOrders{
+ value: (context.matchArgs.amount *
+ context.matchArgs.orderPairCount) +
+ context.matchArgs.excessNativeTokens
+ }(
+ infra.advancedOrders,
+ infra.criteriaResolvers,
+ infra.fulfillments,
+ // If shouldSpecifyUnspentOfferItemRecipient is true, send the
+ // unspent offer items to the recipient specified by the fuzz args.
+ // Otherwise, pass in the zero address, which will result in the
+ // unspent offer items being sent to the caller.
+ context.matchArgs.shouldSpecifyUnspentOfferItemRecipient
+ ? address(context.matchArgs.unspentPrimeOfferItemRecipient)
+ : address(0)
+ );
+
+ // Note the native token balances after the call for later checks.
+ infra.callerBalanceAfter = address(this).balance;
+ infra.primeOffererBalanceAfter = address(fuzzPrimeOfferer.addr).balance;
+
+ // The expected call count is the number of prime orders using the
+ // transfer validation zone, plus the number of mirror orders using the
+ // transfer validation zone. So, expected call count can be 0,
+ // context.matchArgs.orderPairCount, or context.matchArgs.orderPairCount
+ // * 2.
+ uint256 expectedCallCount = 0;
+ if (context.matchArgs.shouldUseTransferValidationZoneForPrime) {
+ expectedCallCount += context.matchArgs.orderPairCount;
+ }
+ if (context.matchArgs.shouldUseTransferValidationZoneForMirror) {
+ expectedCallCount += context.matchArgs.orderPairCount;
+ }
+ assertTrue(zone.callCount() == expectedCallCount);
+
+ // Check that the NFTs were transferred to the expected recipient.
+ for (uint256 i = 0; i < context.matchArgs.orderPairCount; i++) {
+ assertEq(
+ test721_1.ownerOf(context.matchArgs.tokenId + i),
+ fuzzMirrorOfferer.addr
+ );
+ }
+
+ if (context.matchArgs.shouldIncludeExcessOfferItems) {
+ // Check that the excess offer NFTs were transferred to the expected
+ // recipient.
+ for (uint256 i = 0; i < context.matchArgs.orderPairCount; i++) {
+ assertEq(
+ test721_1.ownerOf((context.matchArgs.tokenId + i) * 2),
+ context.matchArgs.shouldSpecifyUnspentOfferItemRecipient
+ ? context.matchArgs.unspentPrimeOfferItemRecipient
+ : address(this)
+ );
+ }
+ }
+
+ if (context.matchArgs.shouldIncludeNativeConsideration) {
+ // Check that ETH is moving from the caller to the prime offerer.
+ // This also checks that excess native tokens are being swept back
+ // to the caller.
+ assertEq(
+ infra.callerBalanceBefore -
+ context.matchArgs.amount *
+ context.matchArgs.orderPairCount,
+ infra.callerBalanceAfter
+ );
+ assertEq(
+ infra.primeOffererBalanceBefore +
+ context.matchArgs.amount *
+ context.matchArgs.orderPairCount,
+ infra.primeOffererBalanceAfter
+ );
+ } else {
+ assertEq(infra.callerBalanceBefore, infra.callerBalanceAfter);
+ }
+ }
+
+ function xtestFulfillAvailableAdvancedFuzz(
+ FulfillFuzzInputs memory fulfillArgs
+ ) public {
+ // Limit this value to avoid overflow issues.
+ fulfillArgs.amount = uint128(
+ bound(fulfillArgs.amount, 1, 0xffffffffffffffff)
+ );
+ // Limit this value to avoid overflow issues.
+ fulfillArgs.tokenId = bound(fulfillArgs.tokenId, 1, 0xffffffffffffffff);
+ // Create between 1 and 16 orders.
+ fulfillArgs.orderCount = bound(fulfillArgs.orderCount, 1, 16);
+ // Use between 1 and 3 consideration items per order.
+ fulfillArgs.considerationItemsPerOrderCount = bound(
+ fulfillArgs.considerationItemsPerOrderCount,
+ 1,
+ 3
+ );
+ // To put three items in the consideration, native tokens must be
+ // included.
+ fulfillArgs.shouldIncludeNativeConsideration =
+ fulfillArgs.shouldIncludeNativeConsideration ||
+ fulfillArgs.considerationItemsPerOrderCount >= 3;
+ // TODO: (Someday) Think about excess offer items.
+ // Fulfill between 1 and the orderCount.
+ fulfillArgs.maximumFulfilledCount = bound(
+ fulfillArgs.maximumFulfilledCount,
+ 1,
+ fulfillArgs.orderCount
+ );
+ // Limit this value to avoid overflow issues.
+ fulfillArgs.excessNativeTokens = uint128(
+ bound(
+ fulfillArgs.excessNativeTokens,
+ 0,
+ 0xfffffffffffffffffffffffffffff
+ )
+ );
+ // Don't set the offer recipient to the null address, because that's the
+ // way to indicate that the caller should be the recipient and because
+ // some tokens refuse to transfer to the null address.
+ fulfillArgs.offerRecipient = _nudgeAddressIfProblematic(
+ address(
+ uint160(
+ bound(
+ uint160(fulfillArgs.offerRecipient),
+ 1,
+ type(uint160).max
+ )
+ )
+ )
+ );
+ // Don't set the consideration recipient to the null address, because
+ // some tokens refuse to transfer to the null address.
+ fulfillArgs.considerationRecipient = _nudgeAddressIfProblematic(
+ address(
+ uint160(
+ bound(
+ uint160(fulfillArgs.considerationRecipient),
+ 1,
+ type(uint160).max
+ )
+ )
+ )
+ );
+
+ test(
+ this.execFulfillAvailableAdvancedFuzz,
+ Context(consideration, fulfillArgs, emptyMatch)
+ );
+ test(
+ this.execFulfillAvailableAdvancedFuzz,
+ Context(referenceConsideration, fulfillArgs, emptyMatch)
+ );
+ }
+
+ function execFulfillAvailableAdvancedFuzz(
+ Context memory context
+ ) external stateless {
+ // TODO: (Someday) See if the stack can tolerate fuzzing criteria
+ // resolvers.
+
+ // Set up the infrastructure.
+ FulfillAvailableAdvancedOrdersInfra
+ memory infra = FulfillAvailableAdvancedOrdersInfra({
+ advancedOrders: new AdvancedOrder[](
+ context.fulfillArgs.orderCount
+ ),
+ offerFulfillmentComponents: new FulfillmentComponent[][](
+ context.fulfillArgs.orderCount
+ ),
+ considerationFulfillmentComponents: new FulfillmentComponent[][](
+ context.fulfillArgs.orderCount
+ ),
+ criteriaResolvers: new CriteriaResolver[](0),
+ callerBalanceBefore: address(this).balance,
+ callerBalanceAfter: address(this).balance,
+ considerationRecipientNativeBalanceBefore: context
+ .fulfillArgs
+ .considerationRecipient
+ .balance,
+ considerationRecipientToken1BalanceBefore: token1.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ ),
+ considerationRecipientToken2BalanceBefore: token2.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ ),
+ considerationRecipientNativeBalanceAfter: context
+ .fulfillArgs
+ .considerationRecipient
+ .balance,
+ considerationRecipientToken1BalanceAfter: token1.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ ),
+ considerationRecipientToken2BalanceAfter: token2.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ )
+ });
+
+ // Use a conduit sometimes.
+ bytes32 conduitKey = context.fulfillArgs.shouldUseConduit
+ ? conduitKeyOne
+ : bytes32(0);
+
+ // Mint enough ERC721s to cover the number of NFTs for sale.
+ for (uint256 i; i < context.fulfillArgs.orderCount; i++) {
+ test721_1.mint(offerer1.addr, context.fulfillArgs.tokenId + i);
+ }
+
+ // Mint enough ERC20s to cover price per NFT * NFTs for sale.
+ token1.mint(
+ address(this),
+ context.fulfillArgs.amount * context.fulfillArgs.orderCount
+ );
+ token2.mint(
+ address(this),
+ context.fulfillArgs.amount * context.fulfillArgs.orderCount
+ );
+
+ // Create the orders.
+ infra.advancedOrders = _buildOrdersFromFuzzArgs(context, offerer1.key);
+
+ // Create the fulfillments.
+ if (context.fulfillArgs.shouldAggregateFulfillmentComponents) {
+ (
+ infra.offerFulfillmentComponents,
+ infra.considerationFulfillmentComponents
+ ) = fulfillAvailableFulfillmentHelper
+ .getAggregatedFulfillmentComponents(infra.advancedOrders);
+ } else {
+ (
+ infra.offerFulfillmentComponents,
+ infra.considerationFulfillmentComponents
+ ) = fulfillAvailableFulfillmentHelper.getNaiveFulfillmentComponents(
+ infra.advancedOrders
+ );
+ }
+
+ // If the fuzz args call for using the transfer validation zone, make
+ // sure that it is actually enforcing the expected requirements.
+ if (context.fulfillArgs.shouldUseTransferValidationZone) {
+ address strangerAddress = address(0xdeafbeef);
+
+ // Make the call to Seaport.
+ context.seaport.fulfillAvailableAdvancedOrders{
+ value: context.fulfillArgs.excessNativeTokens +
+ (
+ context.fulfillArgs.shouldIncludeNativeConsideration
+ ? (context.fulfillArgs.amount *
+ context.fulfillArgs.maximumFulfilledCount)
+ : 0
+ )
+ }({
+ advancedOrders: infra.advancedOrders,
+ criteriaResolvers: infra.criteriaResolvers,
+ offerFulfillments: infra.offerFulfillmentComponents,
+ considerationFulfillments: infra
+ .considerationFulfillmentComponents,
+ fulfillerConduitKey: bytes32(conduitKey),
+ recipient: strangerAddress,
+ maximumFulfilled: infra.advancedOrders.length
+ });
+ }
+
+ if (
+ !context.fulfillArgs.shouldIncludeNativeConsideration &&
+ // If the fuzz args pick this address as the consideration
+ // recipient, then the ERC20 transfers and the native token
+ // transfers will be filtered, so there will be no events.
+ address(context.fulfillArgs.considerationRecipient) != address(this)
+ ) {
+ // This checks that the ERC20 transfers were not all aggregated
+ // into a single transfer.
+ vm.expectEmit(true, true, false, true, address(token1));
+ emit Transfer(
+ address(this), // from
+ address(context.fulfillArgs.considerationRecipient), // to
+ // The value should in the transfer event should either be
+ // the amount * the number of NFTs for sale (if aggregating) or
+ // the amount (if not aggregating).
+ context.fulfillArgs.amount *
+ (
+ context.fulfillArgs.shouldAggregateFulfillmentComponents
+ ? context.fulfillArgs.maximumFulfilledCount
+ : 1
+ )
+ );
+
+ if (context.fulfillArgs.considerationItemsPerOrderCount >= 2) {
+ // This checks that the second consideration item is being
+ // properly handled.
+ vm.expectEmit(true, true, false, true, address(token2));
+ emit Transfer(
+ address(this), // from
+ address(context.fulfillArgs.considerationRecipient), // to
+ context.fulfillArgs.amount *
+ (
+ context
+ .fulfillArgs
+ .shouldAggregateFulfillmentComponents
+ ? context.fulfillArgs.maximumFulfilledCount
+ : 1
+ ) // value
+ );
+ }
+ }
+
+ // Store balances before the call for later comparison.
+ infra.callerBalanceBefore = address(this).balance;
+ infra.considerationRecipientNativeBalanceBefore = address(
+ context.fulfillArgs.considerationRecipient
+ ).balance;
+ infra.considerationRecipientToken1BalanceBefore = token1.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ );
+ infra.considerationRecipientToken2BalanceBefore = token2.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ );
+
+ // Make the call to Seaport. When the fuzz args call for using native
+ // consideration, send enough native tokens to cover the amount per sale
+ // * the number of sales. Otherwise, send just the excess native
+ // tokens.
+ context.seaport.fulfillAvailableAdvancedOrders{
+ value: context.fulfillArgs.excessNativeTokens +
+ (
+ context.fulfillArgs.shouldIncludeNativeConsideration
+ ? context.fulfillArgs.amount *
+ context.fulfillArgs.maximumFulfilledCount
+ : 0
+ )
+ }({
+ advancedOrders: infra.advancedOrders,
+ criteriaResolvers: infra.criteriaResolvers,
+ offerFulfillments: infra.offerFulfillmentComponents,
+ considerationFulfillments: infra.considerationFulfillmentComponents,
+ fulfillerConduitKey: bytes32(conduitKey),
+ // If the fuzz args call for specifying a recipient, pass in the
+ // offer recipient. Otherwise, pass in the null address, which
+ // sets the caller as the recipient.
+ recipient: context.fulfillArgs.shouldSpecifyRecipient
+ ? context.fulfillArgs.offerRecipient
+ : address(0),
+ maximumFulfilled: context.fulfillArgs.maximumFulfilledCount
+ });
+
+ // Store balances after the call for later comparison.
+ infra.callerBalanceAfter = address(this).balance;
+ infra.considerationRecipientNativeBalanceAfter = address(
+ context.fulfillArgs.considerationRecipient
+ ).balance;
+ infra.considerationRecipientToken1BalanceAfter = token1.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ );
+ infra.considerationRecipientToken2BalanceAfter = token2.balanceOf(
+ context.fulfillArgs.considerationRecipient
+ );
+
+ // Check that the zone was called the expected number of times.
+ if (context.fulfillArgs.shouldUseTransferValidationZone) {
+ assertTrue(
+ zone.callCount() == context.fulfillArgs.maximumFulfilledCount
+ );
+ }
+
+ // Check that the NFTs were transferred to the expected recipient.
+ for (
+ uint256 i = 0;
+ i < context.fulfillArgs.maximumFulfilledCount;
+ i++
+ ) {
+ assertEq(
+ test721_1.ownerOf(context.fulfillArgs.tokenId + i),
+ context.fulfillArgs.shouldSpecifyRecipient
+ ? context.fulfillArgs.offerRecipient
+ : address(this)
+ );
+ }
+
+ // Check that the ERC20s or native tokens were transferred to the
+ // expected recipient according to the fuzz args.
+ if (context.fulfillArgs.shouldIncludeNativeConsideration) {
+ if (
+ address(context.fulfillArgs.considerationRecipient) ==
+ address(this)
+ ) {
+ // Edge case: If the fuzz args pick this address for the
+ // consideration recipient, then the caller's balance should not
+ // change.
+ assertEq(infra.callerBalanceAfter, infra.callerBalanceBefore);
+ } else {
+ // Check that the consideration recipient's native balance was
+ // increased by the amount * the number of NFTs for sale.
+ assertEq(
+ infra.considerationRecipientNativeBalanceAfter,
+ infra.considerationRecipientNativeBalanceBefore +
+ context.fulfillArgs.amount *
+ context.fulfillArgs.maximumFulfilledCount
+ );
+ // The consideration (amount * maximumFulfilledCount) should be
+ // spent, and the excessNativeTokens should be returned.
+ assertEq(
+ infra.callerBalanceAfter +
+ context.fulfillArgs.amount *
+ context.fulfillArgs.maximumFulfilledCount,
+ infra.callerBalanceBefore
+ );
+ }
+ } else {
+ // The `else` here is the case where no native consieration is used.
+ if (
+ address(context.fulfillArgs.considerationRecipient) ==
+ address(this)
+ ) {
+ // Edge case: If the fuzz args pick this address for the
+ // consideration recipient, then the caller's balance should not
+ // change.
+ assertEq(
+ infra.considerationRecipientToken1BalanceAfter,
+ infra.considerationRecipientToken1BalanceBefore
+ );
+ } else {
+ assertEq(
+ infra.considerationRecipientToken1BalanceAfter,
+ infra.considerationRecipientToken1BalanceBefore +
+ context.fulfillArgs.amount *
+ context.fulfillArgs.maximumFulfilledCount
+ );
+ }
+
+ if (context.fulfillArgs.considerationItemsPerOrderCount >= 2) {
+ if (
+ address(context.fulfillArgs.considerationRecipient) ==
+ address(this)
+ ) {
+ // Edge case: If the fuzz args pick this address for the
+ // consideration recipient, then the caller's balance should
+ // not change.
+ assertEq(
+ infra.considerationRecipientToken2BalanceAfter,
+ infra.considerationRecipientToken2BalanceBefore
+ );
+ } else {
+ assertEq(
+ infra.considerationRecipientToken2BalanceAfter,
+ infra.considerationRecipientToken2BalanceBefore +
+ context.fulfillArgs.amount *
+ context.fulfillArgs.maximumFulfilledCount
+ );
+ }
+ }
+ }
+ }
+
+ function _buildOrdersFromFuzzArgs(
+ Context memory context,
+ uint256 key
+ ) internal returns (AdvancedOrder[] memory advancedOrders) {
+ // Create the OrderComponents array from the fuzz args.
+ OrderComponents[] memory orderComponents;
+ orderComponents = _buildOrderComponentsArrayFromFuzzArgs(context);
+
+ // Set up the AdvancedOrder array.
+ AdvancedOrder[] memory _advancedOrders = new AdvancedOrder[](
+ context.fulfillArgs.orderCount
+ );
+
+ // Iterate over the OrderComponents array and build an AdvancedOrder
+ // for each OrderComponents.
+ Order memory order;
+ for (uint256 i = 0; i < orderComponents.length; i++) {
+ if (orderComponents[i].orderType == OrderType.CONTRACT) {
+ revert("Not implemented.");
+ } else {
+ // Create the order.
+ order = _toOrder(context.seaport, orderComponents[i], key);
+ // Convert it to an AdvancedOrder and add it to the array.
+ _advancedOrders[i] = order.toAdvancedOrder(
+ 1,
+ 1,
+ // Reusing salt here for junk data.
+ context.fulfillArgs.shouldIncludeJunkDataInAdvancedOrder
+ ? bytes(abi.encodePacked(context.fulfillArgs.salt))
+ : bytes("")
+ );
+ }
+ }
+
+ return _advancedOrders;
+ }
+
+ function _buildOrderComponentsArrayFromFuzzArgs(
+ Context memory context
+ ) internal returns (OrderComponents[] memory _orderComponentsArray) {
+ // Set up the OrderComponentInfra struct.
+ OrderComponentInfra memory orderComponentInfra = OrderComponentInfra(
+ OrderComponentsLib.empty(),
+ new OrderComponents[](context.fulfillArgs.orderCount),
+ new OfferItem[][](context.fulfillArgs.orderCount),
+ new ConsiderationItem[][](context.fulfillArgs.orderCount),
+ ConsiderationItemLib.empty(),
+ ConsiderationItemLib.empty(),
+ ConsiderationItemLib.empty()
+ );
+
+ // Create three different consideration items.
+ (
+ orderComponentInfra.nativeConsiderationItem,
+ orderComponentInfra.erc20ConsiderationItemOne,
+ orderComponentInfra.erc20ConsiderationItemTwo
+ ) = _createReusableConsiderationItems(
+ context.fulfillArgs.amount,
+ context.fulfillArgs.considerationRecipient
+ );
+
+ // Iterate once for each order and create the OfferItems[] and
+ // ConsiderationItems[] for each order.
+ for (uint256 i; i < context.fulfillArgs.orderCount; i++) {
+ // Add a one-element OfferItems[] to the OfferItems[][].
+ orderComponentInfra.offerItemArray[i] = SeaportArrays.OfferItems(
+ OfferItemLib
+ .fromDefault(SINGLE_721)
+ .withToken(address(test721_1))
+ .withIdentifierOrCriteria(context.fulfillArgs.tokenId + i)
+ );
+
+ if (context.fulfillArgs.considerationItemsPerOrderCount == 1) {
+ // If the fuzz args call for native consideration...
+ if (context.fulfillArgs.shouldIncludeNativeConsideration) {
+ // ...add a native consideration item...
+ orderComponentInfra.considerationItemArray[
+ i
+ ] = SeaportArrays.ConsiderationItems(
+ orderComponentInfra.nativeConsiderationItem
+ );
+ } else {
+ // ...otherwise, add an ERC20 consideration item.
+ orderComponentInfra.considerationItemArray[
+ i
+ ] = SeaportArrays.ConsiderationItems(
+ orderComponentInfra.erc20ConsiderationItemOne
+ );
+ }
+ } else if (
+ context.fulfillArgs.considerationItemsPerOrderCount == 2
+ ) {
+ // If the fuzz args call for native consideration...
+ if (context.fulfillArgs.shouldIncludeNativeConsideration) {
+ // ...add a native consideration item and an ERC20
+ // consideration item...
+ orderComponentInfra.considerationItemArray[
+ i
+ ] = SeaportArrays.ConsiderationItems(
+ orderComponentInfra.nativeConsiderationItem,
+ orderComponentInfra.erc20ConsiderationItemOne
+ );
+ } else {
+ // ...otherwise, add two ERC20 consideration items.
+ orderComponentInfra.considerationItemArray[
+ i
+ ] = SeaportArrays.ConsiderationItems(
+ orderComponentInfra.erc20ConsiderationItemOne,
+ orderComponentInfra.erc20ConsiderationItemTwo
+ );
+ }
+ } else {
+ orderComponentInfra.considerationItemArray[i] = SeaportArrays
+ .ConsiderationItems(
+ orderComponentInfra.nativeConsiderationItem,
+ orderComponentInfra.erc20ConsiderationItemOne,
+ orderComponentInfra.erc20ConsiderationItemTwo
+ );
+ }
+ }
+
+ // Use either the transfer validation zone or the test zone for all
+ // orders.
+ address fuzzyZone;
+
+ if (context.fulfillArgs.shouldUseTransferValidationZone) {
+ zone = new TestTransferValidationZoneOfferer(
+ context.fulfillArgs.shouldSpecifyRecipient
+ ? context.fulfillArgs.offerRecipient
+ : address(this)
+ );
+ fuzzyZone = address(zone);
+ } else {
+ fuzzyZone = address(testZone);
+ }
+
+ bytes32 conduitKey;
+
+ // Iterate once for each order and create the OrderComponents.
+ for (uint256 i = 0; i < context.fulfillArgs.orderCount; i++) {
+ // if context.fulfillArgs.shouldUseConduit is false: don't use conduits at all.
+ // if context.fulfillArgs.shouldUseConduit is true:
+ // if context.fulfillArgs.tokenId % 2 == 0:
+ // use conduits for some and not for others
+ // if context.fulfillArgs.tokenId % 2 != 0:
+ // use conduits for all
+ // This is plainly deranged, but it allows for conduit use
+ // for all, for some, and none without weighing down the stack.
+ conduitKey = !context
+ .fulfillArgs
+ .shouldIncludeNativeConsideration &&
+ context.fulfillArgs.shouldUseConduit &&
+ (context.fulfillArgs.tokenId % 2 == 0 ? i % 2 == 0 : true)
+ ? conduitKeyOne
+ : bytes32(0);
+
+ // Build the order components.
+ orderComponentInfra.orderComponents = OrderComponentsLib
+ .fromDefault(VALIDATION_ZONE)
+ .withOffer(orderComponentInfra.offerItemArray[i])
+ .withConsideration(
+ orderComponentInfra.considerationItemArray[i]
+ )
+ .withZone(fuzzyZone)
+ .withZoneHash(context.fulfillArgs.zoneHash)
+ .withConduitKey(conduitKey)
+ .withSalt(context.fulfillArgs.salt % (i + 1)); // Is this dumb?
+
+ // Add the OrderComponents to the OrderComponents[].
+ orderComponentInfra.orderComponentsArray[i] = orderComponentInfra
+ .orderComponents;
+ }
+
+ // Return the OrderComponents[].
+ return orderComponentInfra.orderComponentsArray;
+ }
+
+ function _createReusableConsiderationItems(
+ uint256 amount,
+ address recipient
+ )
+ internal
+ view
+ returns (
+ ConsiderationItem memory nativeConsiderationItem,
+ ConsiderationItem memory erc20ConsiderationItemOne,
+ ConsiderationItem memory erc20ConsiderationItemTwo
+ )
+ {
+ // Create a reusable native consideration item.
+ nativeConsiderationItem = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.NATIVE)
+ .withIdentifierOrCriteria(0)
+ .withStartAmount(amount)
+ .withEndAmount(amount)
+ .withRecipient(recipient);
+
+ // Create a reusable ERC20 consideration item.
+ erc20ConsiderationItemOne = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withToken(address(token1))
+ .withIdentifierOrCriteria(0)
+ .withStartAmount(amount)
+ .withEndAmount(amount)
+ .withRecipient(recipient);
+
+ // Create a second reusable ERC20 consideration item.
+ erc20ConsiderationItemTwo = ConsiderationItemLib
+ .empty()
+ .withItemType(ItemType.ERC20)
+ .withIdentifierOrCriteria(0)
+ .withToken(address(token2))
+ .withStartAmount(amount)
+ .withEndAmount(amount)
+ .withRecipient(recipient);
+ }
+
+ function _buildPrimeOfferItemArray(
+ Context memory context,
+ uint256 i
+ ) internal view returns (OfferItem[] memory _offerItemArray) {
+ // Set up the OfferItem array.
+ OfferItem[] memory offerItemArray = new OfferItem[](
+ context.matchArgs.shouldIncludeExcessOfferItems ? 2 : 1
+ );
+
+ // If the fuzz args call for an excess offer item...
+ if (context.matchArgs.shouldIncludeExcessOfferItems) {
+ // Create the OfferItem array containing the offered item and the
+ // excess item.
+ offerItemArray = SeaportArrays.OfferItems(
+ OfferItemLib
+ .fromDefault(SINGLE_721)
+ .withToken(address(test721_1))
+ .withIdentifierOrCriteria(context.matchArgs.tokenId + i),
+ OfferItemLib
+ .fromDefault(SINGLE_721)
+ .withToken(address(test721_1))
+ .withIdentifierOrCriteria(
+ (context.matchArgs.tokenId + i) * 2
+ )
+ );
+ } else {
+ // Otherwise, create the OfferItem array containing the one offered
+ // item.
+ offerItemArray = SeaportArrays.OfferItems(
+ OfferItemLib
+ .fromDefault(SINGLE_721)
+ .withToken(address(test721_1))
+ .withIdentifierOrCriteria(context.matchArgs.tokenId + i)
+ );
+ }
+
+ return offerItemArray;
+ }
+
+ function _buildPrimeConsiderationItemArray(
+ Context memory context
+ )
+ internal
+ view
+ returns (ConsiderationItem[] memory _considerationItemArray)
+ {
+ // Set up the ConsiderationItem array.
+ ConsiderationItem[]
+ memory considerationItemArray = new ConsiderationItem[](
+ context.matchArgs.considerationItemsPerPrimeOrderCount
+ );
+
+ // Create the consideration items.
+ (
+ ConsiderationItem memory nativeConsiderationItem,
+ ConsiderationItem memory erc20ConsiderationItemOne,
+ ConsiderationItem memory erc20ConsiderationItemTwo
+ ) = _createReusableConsiderationItems(
+ context.matchArgs.amount,
+ fuzzPrimeOfferer.addr
+ );
+
+ if (context.matchArgs.considerationItemsPerPrimeOrderCount == 1) {
+ // If the fuzz args call for native consideration...
+ if (context.matchArgs.shouldIncludeNativeConsideration) {
+ // ...add a native consideration item...
+ considerationItemArray = SeaportArrays.ConsiderationItems(
+ nativeConsiderationItem
+ );
+ } else {
+ // ...otherwise, add an ERC20 consideration item.
+ considerationItemArray = SeaportArrays.ConsiderationItems(
+ erc20ConsiderationItemOne
+ );
+ }
+ } else if (
+ context.matchArgs.considerationItemsPerPrimeOrderCount == 2
+ ) {
+ // If the fuzz args call for native consideration...
+ if (context.matchArgs.shouldIncludeNativeConsideration) {
+ // ...add a native consideration item and an ERC20
+ // consideration item...
+ considerationItemArray = SeaportArrays.ConsiderationItems(
+ nativeConsiderationItem,
+ erc20ConsiderationItemOne
+ );
+ } else {
+ // ...otherwise, add two ERC20 consideration items.
+ considerationItemArray = SeaportArrays.ConsiderationItems(
+ erc20ConsiderationItemOne,
+ erc20ConsiderationItemTwo
+ );
+ }
+ } else {
+ // If the fuzz args call for three consideration items per prime
+ // order, add all three consideration items.
+ considerationItemArray = SeaportArrays.ConsiderationItems(
+ nativeConsiderationItem,
+ erc20ConsiderationItemOne,
+ erc20ConsiderationItemTwo
+ );
+ }
+
+ return considerationItemArray;
+ }
+
+ function _buildMirrorOfferItemArray(
+ Context memory context
+ ) internal view returns (OfferItem[] memory _offerItemArray) {
+ // Set up the OfferItem array.
+ OfferItem[] memory offerItemArray = new OfferItem[](1);
+
+ // Create some consideration items.
+ (
+ ConsiderationItem memory nativeConsiderationItem,
+ ConsiderationItem memory erc20ConsiderationItemOne,
+ ConsiderationItem memory erc20ConsiderationItemTwo
+ ) = _createReusableConsiderationItems(
+ context.matchArgs.amount,
+ fuzzPrimeOfferer.addr
+ );
+
+ // Convert them to OfferItems.
+ OfferItem memory nativeOfferItem = _toOfferItem(
+ nativeConsiderationItem
+ );
+ OfferItem memory erc20OfferItemOne = _toOfferItem(
+ erc20ConsiderationItemOne
+ );
+ OfferItem memory erc20OfferItemTwo = _toOfferItem(
+ erc20ConsiderationItemTwo
+ );
+
+ if (context.matchArgs.considerationItemsPerPrimeOrderCount == 1) {
+ // If the fuzz args call for native consideration...
+ if (context.matchArgs.shouldIncludeNativeConsideration) {
+ // ...add a native consideration item...
+ offerItemArray = SeaportArrays.OfferItems(nativeOfferItem);
+ } else {
+ // ...otherwise, add an ERC20 consideration item.
+ offerItemArray = SeaportArrays.OfferItems(erc20OfferItemOne);
+ }
+ } else if (
+ context.matchArgs.considerationItemsPerPrimeOrderCount == 2
+ ) {
+ // If the fuzz args call for native consideration...
+ if (context.matchArgs.shouldIncludeNativeConsideration) {
+ // ...add a native consideration item and an ERC20
+ // consideration item...
+ offerItemArray = SeaportArrays.OfferItems(
+ nativeOfferItem,
+ erc20OfferItemOne
+ );
+ } else {
+ // ...otherwise, add two ERC20 consideration items.
+ offerItemArray = SeaportArrays.OfferItems(
+ erc20OfferItemOne,
+ erc20OfferItemTwo
+ );
+ }
+ } else {
+ offerItemArray = SeaportArrays.OfferItems(
+ nativeOfferItem,
+ erc20OfferItemOne,
+ erc20OfferItemTwo
+ );
+ }
+
+ return offerItemArray;
+ }
+
+ function buildMirrorConsiderationItemArray(
+ Context memory context,
+ uint256 i
+ )
+ internal
+ view
+ returns (ConsiderationItem[] memory _considerationItemArray)
+ {
+ // Set up the ConsiderationItem array.
+ ConsiderationItem[]
+ memory considerationItemArray = new ConsiderationItem[](
+ context.matchArgs.considerationItemsPerPrimeOrderCount
+ );
+
+ // Note that the consideration array here will always be just one NFT
+ // so because the second NFT on the offer side is meant to be excess.
+ considerationItemArray = SeaportArrays.ConsiderationItems(
+ ConsiderationItemLib
+ .fromDefault(SINGLE_721)
+ .withToken(address(test721_1))
+ .withIdentifierOrCriteria(context.matchArgs.tokenId + i)
+ .withRecipient(fuzzMirrorOfferer.addr)
+ );
+
+ return considerationItemArray;
+ }
+
+ function _buildOrderComponents(
+ Context memory context,
+ OfferItem[] memory offerItemArray,
+ ConsiderationItem[] memory considerationItemArray,
+ address offerer,
+ bool shouldUseTransferValidationZone
+ ) internal view returns (OrderComponents memory _orderComponents) {
+ OrderComponents memory orderComponents = OrderComponentsLib.empty();
+
+ // Create the offer and consideration item arrays.
+ OfferItem[] memory _offerItemArray = offerItemArray;
+ ConsiderationItem[]
+ memory _considerationItemArray = considerationItemArray;
+
+ // Build the OrderComponents for the prime offerer's order.
+ orderComponents = OrderComponentsLib
+ .fromDefault(VALIDATION_ZONE)
+ .withOffer(_offerItemArray)
+ .withConsideration(_considerationItemArray)
+ .withZone(address(0))
+ .withOrderType(OrderType.FULL_OPEN)
+ .withConduitKey(
+ context.matchArgs.tokenId % 2 == 0 ? conduitKeyOne : bytes32(0)
+ )
+ .withOfferer(offerer)
+ .withCounter(context.seaport.getCounter(offerer));
+
+ // If the fuzz args call for a transfer validation zone...
+ if (shouldUseTransferValidationZone) {
+ // ... set the zone to the transfer validation zone and
+ // set the order type to FULL_RESTRICTED.
+ orderComponents = orderComponents
+ .copy()
+ .withZone(address(zone))
+ .withOrderType(OrderType.FULL_RESTRICTED);
+ }
+
+ return orderComponents;
+ }
+
+ function _buildOrdersAndFulfillmentsMirrorOrdersFromFuzzArgs(
+ Context memory context
+ ) internal returns (Order[] memory, Fulfillment[] memory) {
+ uint256 i;
+
+ // Set up the OrderAndFulfillmentInfra struct.
+ OrderAndFulfillmentInfra memory infra = OrderAndFulfillmentInfra(
+ new OfferItem[](context.matchArgs.orderPairCount),
+ new ConsiderationItem[](context.matchArgs.orderPairCount),
+ OrderComponentsLib.empty(),
+ new Order[](context.matchArgs.orderPairCount * 2),
+ FulfillmentLib.empty(),
+ new Fulfillment[](context.matchArgs.orderPairCount * 2)
+ );
+
+ // Iterate once for each orderPairCount, which is
+ // used as the number of order pairs to make here.
+ for (i = 0; i < context.matchArgs.orderPairCount; i++) {
+ // Mint the NFTs for the prime offerer to sell.
+ test721_1.mint(
+ fuzzPrimeOfferer.addr,
+ context.matchArgs.tokenId + i
+ );
+ test721_1.mint(
+ fuzzPrimeOfferer.addr,
+ (context.matchArgs.tokenId + i) * 2
+ );
+
+ // Build the OfferItem array for the prime offerer's order.
+ infra.offerItemArray = _buildPrimeOfferItemArray(context, i);
+ // Build the ConsiderationItem array for the prime offerer's order.
+ infra.considerationItemArray = _buildPrimeConsiderationItemArray(
+ context
+ );
+
+ // Build the OrderComponents for the prime offerer's order.
+ infra.orderComponents = _buildOrderComponents(
+ context,
+ infra.offerItemArray,
+ infra.considerationItemArray,
+ fuzzPrimeOfferer.addr,
+ context.matchArgs.shouldUseTransferValidationZoneForPrime
+ );
+
+ // Add the order to the orders array.
+ infra.orders[i] = _toOrder(
+ context.seaport,
+ infra.orderComponents,
+ fuzzPrimeOfferer.key
+ );
+
+ // Build the offerItemArray for the mirror offerer's order.
+ infra.offerItemArray = _buildMirrorOfferItemArray(context);
+
+ // Build the considerationItemArray for the mirror offerer's order.
+ // Note that the consideration on the mirror is always just one NFT,
+ // even if the prime order has an excess item.
+ infra.considerationItemArray = buildMirrorConsiderationItemArray(
+ context,
+ i
+ );
+
+ // Build the OrderComponents for the mirror offerer's order.
+ infra.orderComponents = _buildOrderComponents(
+ context,
+ infra.offerItemArray,
+ infra.considerationItemArray,
+ fuzzMirrorOfferer.addr,
+ context.matchArgs.shouldUseTransferValidationZoneForMirror
+ );
+
+ // Create the order and add the order to the orders array.
+ infra.orders[i + context.matchArgs.orderPairCount] = _toOrder(
+ context.seaport,
+ infra.orderComponents,
+ fuzzMirrorOfferer.key
+ );
+ }
+
+ bytes32[] memory orderHashes = new bytes32[](
+ context.matchArgs.orderPairCount * 2
+ );
+
+ UnavailableReason[] memory unavailableReasons = new UnavailableReason[](
+ context.matchArgs.orderPairCount * 2
+ );
+
+ // Build fulfillments.
+ (infra.fulfillments, , ) = matchFulfillmentHelper
+ .getMatchedFulfillments(
+ infra.orders,
+ orderHashes,
+ unavailableReasons
+ );
+
+ return (infra.orders, infra.fulfillments);
+ }
+
+ function _toOrder(
+ ConsiderationInterface seaport,
+ OrderComponents memory orderComponents,
+ uint256 pkey
+ ) internal view returns (Order memory order) {
+ bytes32 orderHash = seaport.getOrderHash(orderComponents);
+ bytes memory signature = signOrder(seaport, pkey, orderHash);
+ order = OrderLib
+ .empty()
+ .withParameters(orderComponents.toOrderParameters())
+ .withSignature(signature);
+ }
+
+ function _toOfferItem(
+ ConsiderationItem memory item
+ ) internal pure returns (OfferItem memory) {
+ return
+ OfferItem({
+ itemType: item.itemType,
+ token: item.token,
+ identifierOrCriteria: item.identifierOrCriteria,
+ startAmount: item.startAmount,
+ endAmount: item.endAmount
+ });
+ }
+
+ function _nudgeAddressIfProblematic(
+ address _address
+ ) internal returns (address) {
+ bool success;
+ assembly {
+ // Transfer the native token and store if it succeeded or not.
+ success := call(gas(), _address, 1, 0, 0, 0, 0)
+ }
+
+ if (success) {
+ return _address;
+ } else {
+ return address(uint160(_address) + 1);
+ }
+ }
+}
diff --git a/test/foundry/zone/TestTransferValidationZoneOfferer.t.sol b/test/foundry/zone/TestTransferValidationZoneOfferer.t.sol
deleted file mode 100644
index 32a4eb268..000000000
--- a/test/foundry/zone/TestTransferValidationZoneOfferer.t.sol
+++ /dev/null
@@ -1,282 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.17;
-
-import { BaseOrderTest } from "../utils/BaseOrderTest.sol";
-import {
- ConsiderationItem,
- OfferItem,
- ItemType,
- OrderType,
- AdvancedOrder,
- Order,
- CriteriaResolver,
- BasicOrderParameters,
- AdditionalRecipient,
- FulfillmentComponent,
- Fulfillment,
- OrderComponents,
- OrderParameters
-} from "../../../contracts/lib/ConsiderationStructs.sol";
-import {
- ConsiderationInterface
-} from "../../../contracts/interfaces/ConsiderationInterface.sol";
-import {
- FulfillmentLib,
- FulfillmentComponentLib,
- OrderParametersLib,
- OrderComponentsLib,
- OrderLib,
- OfferItemLib,
- ConsiderationItemLib,
- SeaportArrays
-} from "../../../contracts/helpers/sol/lib/SeaportStructLib.sol";
-import {
- TestTransferValidationZoneOfferer
-} from "../../../contracts/test/TestTransferValidationZoneOfferer.sol";
-
-contract TestTransferValidationZoneOffererTest is BaseOrderTest {
- using FulfillmentLib for Fulfillment;
- using FulfillmentComponentLib for FulfillmentComponent;
- using FulfillmentComponentLib for FulfillmentComponent[];
- using OfferItemLib for OfferItem;
- using OfferItemLib for OfferItem[];
- using ConsiderationItemLib for ConsiderationItem;
- using ConsiderationItemLib for ConsiderationItem[];
- using OrderComponentsLib for OrderComponents;
- using OrderParametersLib for OrderParameters;
- using OrderLib for Order;
- using OrderLib for Order[];
-
- TestTransferValidationZoneOfferer zone;
-
- // constant strings for recalling struct lib "defaults"
- // ideally these live in a base test class
- string constant ONE_ETH = "one eth";
- string constant SINGLE_721 = "single 721";
- string constant VALIDATION_ZONE = "validation zone";
- string constant FIRST_FIRST = "first first";
- string constant SECOND_FIRST = "second first";
- string constant FIRST_SECOND__FIRST = "first&second first";
-
- function setUp() public virtual override {
- super.setUp();
- zone = new TestTransferValidationZoneOfferer();
-
- // create a default considerationItem for one ether;
- // note that it does not have recipient set
- ConsiderationItemLib
- .empty()
- .withItemType(ItemType.NATIVE)
- .withToken(address(0)) // not strictly necessary
- .withStartAmount(1 ether)
- .withEndAmount(1 ether)
- .withIdentifierOrCriteria(0)
- .saveDefault(ONE_ETH); // not strictly necessary
-
- // create a default offerItem for a single 721;
- // note that it does not have token or identifier set
- OfferItemLib
- .empty()
- .withItemType(ItemType.ERC721)
- .withStartAmount(1)
- .withEndAmount(1)
- .saveDefault(SINGLE_721);
-
- OrderComponentsLib
- .empty()
- .withOfferer(offerer1.addr)
- .withZone(address(zone))
- // fill in offer later
- // fill in consideration later
- .withOrderType(OrderType.FULL_RESTRICTED)
- .withStartTime(block.timestamp)
- .withEndTime(block.timestamp + 1)
- .withZoneHash(bytes32(0)) // not strictly necessary
- .withSalt(0)
- .withConduitKey(conduitKeyOne)
- .saveDefault(VALIDATION_ZONE); // not strictly necessary
- // fill in counter later
-
- // create a default fulfillmentComponent for first_first
- // corresponds to first offer or consideration item in the first order
- FulfillmentComponent memory firstFirst = FulfillmentComponentLib
- .empty()
- .withOrderIndex(0)
- .withItemIndex(0)
- .saveDefault(FIRST_FIRST);
- // create a default fulfillmentComponent for second_first
- // corresponds to first offer or consideration item in the second order
- FulfillmentComponent memory secondFirst = FulfillmentComponentLib
- .empty()
- .withOrderIndex(1)
- .withItemIndex(0)
- .saveDefault(SECOND_FIRST);
-
- // create a one-element array comtaining first_first
- SeaportArrays.FulfillmentComponents(firstFirst).saveDefaultMany(
- FIRST_FIRST
- );
- // create a one-element array comtaining second_first
- SeaportArrays.FulfillmentComponents(secondFirst).saveDefaultMany(
- SECOND_FIRST
- );
-
- // create a two-element array comtaining first_first and second_first
- SeaportArrays
- .FulfillmentComponents(firstFirst, secondFirst)
- .saveDefaultMany(FIRST_SECOND__FIRST);
- }
-
- struct Context {
- ConsiderationInterface seaport;
- }
-
- function test(
- function(Context memory) external fn,
- Context memory context
- ) internal {
- try fn(context) {
- fail("Expected revert");
- } catch (bytes memory reason) {
- assertPass(reason);
- }
- }
-
- function testAggregate() public {
- prepareAggregate();
-
- test(this.execAggregate, Context({ seaport: consideration }));
- test(this.execAggregate, Context({ seaport: referenceConsideration }));
- }
-
- ///@dev prepare aggregate test by minting tokens to offerer1
- function prepareAggregate() internal {
- test721_1.mint(offerer1.addr, 1);
- test721_2.mint(offerer1.addr, 1);
- }
-
- function execAggregate(Context memory context) external stateless {
- (
- Order[] memory orders,
- FulfillmentComponent[][] memory offerFulfillments,
- FulfillmentComponent[][] memory considerationFulfillments,
- bytes32 conduitKey,
- uint256 numOrders
- ) = _buildFulfillmentData(context);
-
- context.seaport.fulfillAvailableOrders{ value: 2 ether }({
- orders: orders,
- offerFulfillments: offerFulfillments,
- considerationFulfillments: considerationFulfillments,
- fulfillerConduitKey: conduitKey,
- maximumFulfilled: numOrders
- });
- }
-
- ///@dev build multiple orders from the same offerer
- function _buildOrders(
- Context memory context,
- OrderComponents[] memory orderComponents,
- uint256 key
- ) internal view returns (Order[] memory) {
- Order[] memory orders = new Order[](orderComponents.length);
- for (uint256 i = 0; i < orderComponents.length; i++) {
- orders[i] = toOrder(context.seaport, orderComponents[i], key);
- }
- return orders;
- }
-
- function _buildFulfillmentData(
- Context memory context
- )
- internal
- view
- returns (
- Order[] memory,
- FulfillmentComponent[][] memory,
- FulfillmentComponent[][] memory,
- bytes32,
- uint256
- )
- {
- ConsiderationItem[] memory considerationArray = SeaportArrays
- .ConsiderationItems(
- ConsiderationItemLib.fromDefault(ONE_ETH).withRecipient(
- offerer1.addr
- )
- );
- OfferItem[] memory offerArray = SeaportArrays.OfferItems(
- OfferItemLib
- .fromDefault(SINGLE_721)
- .withToken(address(test721_1))
- .withIdentifierOrCriteria(1)
- );
- // build first order components
- OrderComponents memory orderComponents = OrderComponentsLib
- .fromDefault(VALIDATION_ZONE)
- .withOffer(offerArray)
- .withConsideration(considerationArray)
- .withCounter(context.seaport.getCounter(offerer1.addr));
-
- // second order components only differs by what is offered
- offerArray = SeaportArrays.OfferItems(
- OfferItemLib
- .fromDefault(SINGLE_721)
- .withToken(address(test721_2))
- .withIdentifierOrCriteria(1)
- );
- // technically we do not need to copy() since first order components is
- // not used again, but to encourage good practices, make a copy and
- // edit that
- OrderComponents memory orderComponents2 = orderComponents
- .copy()
- .withOffer(offerArray);
-
- Order[] memory orders = _buildOrders(
- context,
- SeaportArrays.OrderComponentsArray(
- orderComponents,
- orderComponents2
- ),
- offerer1.key
- );
-
- // create fulfillments
- // offer fulfillments cannot be aggregated (cannot batch transfer 721s) so there will be one array per order
- FulfillmentComponent[][] memory offerFulfillments = SeaportArrays
- .FulfillmentComponentArrays(
- // first FulfillmentComponents[] is single FulfillmentComponent for test721_1 id 1
- FulfillmentComponentLib.fromDefaultMany(FIRST_FIRST),
- // second FulfillmentComponents[] is single FulfillmentComponent for test721_2 id 1
- FulfillmentComponentLib.fromDefaultMany(SECOND_FIRST)
- );
- // consideration fulfillments can be aggregated (can batch transfer eth) so there will be one array for both orders
- FulfillmentComponent[][]
- memory considerationFulfillments = SeaportArrays
- .FulfillmentComponentArrays(
- // two-element fulfillmentcomponents array, one for each order
- FulfillmentComponentLib.fromDefaultMany(FIRST_SECOND__FIRST)
- );
-
- return (
- orders,
- offerFulfillments,
- considerationFulfillments,
- conduitKeyOne,
- 2
- );
- }
-
- function toOrder(
- ConsiderationInterface seaport,
- OrderComponents memory orderComponents,
- uint256 pkey
- ) internal view returns (Order memory order) {
- bytes32 orderHash = seaport.getOrderHash(orderComponents);
- bytes memory signature = signOrder(seaport, pkey, orderHash);
- order = OrderLib
- .empty()
- .withParameters(orderComponents.toOrderParameters())
- .withSignature(signature);
- }
-}
diff --git a/test/foundry/zone/impl/BadZone.sol b/test/foundry/zone/impl/BadZone.sol
index da5a45ed4..216a7a704 100644
--- a/test/foundry/zone/impl/BadZone.sol
+++ b/test/foundry/zone/impl/BadZone.sol
@@ -6,11 +6,13 @@ import {
Schema
} from "../../../../contracts/lib/ConsiderationStructs.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import {
ZoneInterface
} from "../../../../contracts/interfaces/ZoneInterface.sol";
-contract BadZone is ZoneInterface {
+contract BadZone is ERC165, ZoneInterface {
function validateOrder(
ZoneParameters calldata zoneParameters
) external pure returns (bytes4 validOrderMagicValue) {
@@ -41,4 +43,12 @@ contract BadZone is ZoneInterface {
return ("BadZone", schemas);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol b/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol
index e765f9d93..f46ad1f21 100644
--- a/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol
+++ b/test/foundry/zone/impl/PostFullfillmentStatefulTestZone.sol
@@ -8,11 +8,13 @@ import {
import { ItemType } from "../../../../contracts/lib/ConsiderationEnums.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import {
ZoneInterface
} from "../../../../contracts/interfaces/ZoneInterface.sol";
-contract PostFulfillmentStatefulTestZone is ZoneInterface {
+contract PostFulfillmentStatefulTestZone is ERC165, ZoneInterface {
error IncorrectAmount(uint256 actual, uint256 expected);
error IncorrectItemType(ItemType actual, ItemType expected);
error IncorrectIdentifier(uint256 actual, uint256 expected);
@@ -38,7 +40,10 @@ contract PostFulfillmentStatefulTestZone is ZoneInterface {
) external returns (bytes4 validOrderMagicValue) {
// Check that the amount in the offer is correct.
if (zoneParameters.offer[0].amount != amountToCheck) {
- revert IncorrectAmount(zoneParameters.offer[0].amount, 50);
+ revert IncorrectAmount(
+ zoneParameters.offer[0].amount,
+ amountToCheck
+ );
}
// Check that the item type in the consideration is correct.
@@ -82,4 +87,12 @@ contract PostFulfillmentStatefulTestZone is ZoneInterface {
return ("PostFulfillmentStatefulTestZone", schemas);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/test/foundry/zone/impl/TestZone.sol b/test/foundry/zone/impl/TestZone.sol
index 709607e70..d46c0a3fc 100644
--- a/test/foundry/zone/impl/TestZone.sol
+++ b/test/foundry/zone/impl/TestZone.sol
@@ -6,11 +6,13 @@ import {
Schema
} from "../../../../contracts/lib/ConsiderationStructs.sol";
+import { ERC165 } from "../../../../contracts/interfaces/ERC165.sol";
+
import {
ZoneInterface
} from "../../../../contracts/interfaces/ZoneInterface.sol";
-contract TestZone is ZoneInterface {
+contract TestZone is ERC165, ZoneInterface {
// Called by Consideration whenever any extraData is provided by the caller.
function validateOrder(
ZoneParameters calldata
@@ -36,4 +38,12 @@ contract TestZone is ZoneInterface {
return ("TestZone", schemas);
}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view override(ERC165, ZoneInterface) returns (bool) {
+ return
+ interfaceId == type(ZoneInterface).interfaceId ||
+ super.supportsInterface(interfaceId);
+ }
}
diff --git a/test/router.spec.ts b/test/router.spec.ts
index 0a3ad77b9..a83c04665 100644
--- a/test/router.spec.ts
+++ b/test/router.spec.ts
@@ -141,7 +141,7 @@ describe(`SeaportRouter tests (Seaport v${VERSION})`, function () {
},
],
fulfillerConduitKey: toKey(0),
- recipient: buyer.address,
+ recipient: ethers.constants.AddressZero,
maximumFulfilled: 100,
};
@@ -676,4 +676,125 @@ describe(`SeaportRouter tests (Seaport v${VERSION})`, function () {
});
});
});
+ it("Should revert on all errors except NoSpecifiedOrdersAvailable()", async () => {
+ // Seller mints nfts
+ const nftId = await mintAndApprove721(seller, marketplaceContract.address);
+ const nftId2 = await mintAndApprove721(
+ seller,
+ marketplaceContract2.address
+ );
+
+ const offer = [getTestItem721(nftId)];
+ const offer2 = [getTestItem721(nftId2)];
+
+ const consideration = [
+ getItemETH(parseEther("10"), parseEther("10"), seller.address),
+ getItemETH(parseEther("1"), parseEther("1"), zone.address),
+ ];
+
+ const { order, value } = await createOrder(
+ seller,
+ zone,
+ offer,
+ consideration,
+ 0 // FULL_OPEN
+ );
+ const { order: order2 } = await createOrder(
+ seller,
+ zone,
+ offer2,
+ consideration,
+ 0, // FULL_OPEN
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ marketplaceContract2
+ );
+
+ const offerComponents = [[{ orderIndex: 0, itemIndex: 0 }]];
+ const considerationComponents = [
+ [{ orderIndex: 0, itemIndex: 0 }],
+ [{ orderIndex: 0, itemIndex: 1 }],
+ ];
+
+ const params = {
+ seaportContracts: [
+ marketplaceContract.address,
+ marketplaceContract2.address,
+ ],
+ advancedOrderParams: [
+ {
+ advancedOrders: [order],
+ criteriaResolvers: [],
+ offerFulfillments: offerComponents,
+ considerationFulfillments: considerationComponents,
+ etherValue: value,
+ },
+ {
+ advancedOrders: [order2],
+ criteriaResolvers: [],
+ offerFulfillments: offerComponents,
+ considerationFulfillments: considerationComponents,
+ etherValue: value,
+ },
+ ],
+ fulfillerConduitKey: toKey(0),
+ recipient: buyer.address,
+ maximumFulfilled: 100,
+ };
+
+ const buyerEthBalanceBefore = await provider.getBalance(buyer.address);
+
+ // Execute the first order so it is fulfilled thus invalid for the next call
+ await router.connect(buyer).fulfillAvailableAdvancedOrders(
+ {
+ ...params,
+ seaportContracts: params.seaportContracts.slice(0, 1),
+ advancedOrderParams: params.advancedOrderParams.slice(0, 1),
+ },
+ {
+ value,
+ }
+ );
+
+ // Execute orders
+ await router.connect(buyer).fulfillAvailableAdvancedOrders(params, {
+ value: value.mul(2),
+ });
+
+ // Ensure the recipient (buyer) owns both nfts
+ expect(await testERC721.ownerOf(nftId)).to.equal(buyer.address);
+ expect(await testERC721.ownerOf(nftId2)).to.equal(buyer.address);
+
+ // Ensure the excess eth was returned
+ const buyerEthBalanceAfter = await provider.getBalance(buyer.address);
+ expect(buyerEthBalanceBefore).to.be.gt(
+ buyerEthBalanceAfter.sub(value.mul(3))
+ );
+
+ // Try to execute the orders again, which should fail because both orders are fulfilled
+ await expect(
+ router.connect(buyer).fulfillAvailableAdvancedOrders(params, {
+ value,
+ })
+ ).to.be.revertedWithCustomError(router, "NoSpecifiedOrdersAvailable");
+
+ // Now let's try to throw an error that should bubble up.
+ // Set the order type to CONTRACT which should throw "InvalidContractOrder"
+ params.advancedOrderParams[0].advancedOrders[0].parameters.orderType = 4;
+ await expect(
+ router.connect(buyer).fulfillAvailableAdvancedOrders(params, {
+ value,
+ })
+ ).to.be.revertedWithCustomError(
+ marketplaceContract,
+ "InvalidContractOrder"
+ );
+ });
});
diff --git a/test/utils/helpers.ts b/test/utils/helpers.ts
index 5fceddba7..6941cb70c 100644
--- a/test/utils/helpers.ts
+++ b/test/utils/helpers.ts
@@ -9,7 +9,7 @@ import type {
Order,
} from "./types";
-export const VERSION = `1.4${process.env.REFERENCE ? "-reference" : ""}`;
+export const VERSION = `1.5${process.env.REFERENCE ? "-reference" : ""}`;
export const minRandom = (min: ethers.BigNumberish) => randomBN(10).add(min);
diff --git a/test/zone.spec.ts b/test/zone.spec.ts
index c58fc2c63..4d43217f8 100644
--- a/test/zone.spec.ts
+++ b/test/zone.spec.ts
@@ -1077,7 +1077,9 @@ describe(`Zone - Transfer Validation (Seaport v${VERSION})`, function () {
owner
);
- const zoneAddr = await TransferValidationZoneOffererFactory.deploy();
+ const zoneAddr = await TransferValidationZoneOffererFactory.deploy(
+ ethers.constants.AddressZero
+ );
const consideration = [
getItemETH(parseEther("10"), parseEther("10"), seller.address),
@@ -1086,7 +1088,7 @@ describe(`Zone - Transfer Validation (Seaport v${VERSION})`, function () {
const { order, orderHash, value } = await createOrder(
seller,
- zoneAddr,
+ zoneAddr.address,
offer,
consideration,
2 // FULL_RESTRICTED
@@ -1135,11 +1137,13 @@ describe(`Zone - Transfer Validation (Seaport v${VERSION})`, function () {
owner
);
- const zoneAddr = await TransferValidationZoneOffererFactory.deploy();
+ const zoneAddr = await TransferValidationZoneOffererFactory.deploy(
+ ethers.constants.AddressZero
+ );
const { order, orderHash, value } = await createOrder(
seller,
- zoneAddr,
+ zoneAddr.address,
offer,
consideration,
2, // FULL_RESTRICTED
@@ -1195,11 +1199,13 @@ describe(`Zone - Transfer Validation (Seaport v${VERSION})`, function () {
owner
);
- const zoneAddr = await TransferValidationZoneOffererFactory.deploy();
+ const zoneAddr = await TransferValidationZoneOffererFactory.deploy(
+ ethers.constants.AddressZero
+ );
const { order, orderHash, value } = await createOrder(
seller,
- zoneAddr,
+ zoneAddr.address,
offer,
consideration,
3 // PARTIAL_RESTRICTED
@@ -1258,7 +1264,9 @@ describe(`Zone - Transfer Validation (Seaport v${VERSION})`, function () {
);
const transferValidationZone =
- await TransferValidationZoneOffererFactory.deploy();
+ await TransferValidationZoneOffererFactory.deploy(
+ ethers.constants.AddressZero
+ );
const {
order: orderOne,
@@ -1266,7 +1274,7 @@ describe(`Zone - Transfer Validation (Seaport v${VERSION})`, function () {
value,
} = await createOrder(
seller,
- transferValidationZone,
+ transferValidationZone.address,
offer,
consideration,
2, // FULL_RESTRICTED
diff --git a/tsconfig.json b/tsconfig.json
index 4a8b9a26c..e8c99bdc9 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,6 +8,13 @@
"declaration": true,
"resolveJsonModule": true
},
- "include": ["./scripts", "./test", "./typechain-types", "./eip-712-types", "./*.config.ts", "./docs/prepare-docs.js"],
+ "include": [
+ "./scripts",
+ "./test",
+ "./typechain-types",
+ "./eip-712-types",
+ "./*.config.ts",
+ "./docs/prepare-docs.js"
+ ],
"files": ["./hardhat.config.ts"]
}
diff --git a/yarn.lock b/yarn.lock
index 26d2927cf..a993fc65d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4,19 +4,19 @@
"@babel/code-frame@^7.0.0":
version "7.18.6"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+ resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz"
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/helper-validator-identifier@^7.18.6":
version "7.19.1"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/highlight@^7.18.6":
version "7.18.6"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+ resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz"
integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
dependencies:
"@babel/helper-validator-identifier" "^7.18.6"
@@ -25,14 +25,14 @@
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
- resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@eslint/eslintrc@^1.3.3":
version "1.3.3"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95"
+ resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz"
integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==
dependencies:
ajv "^6.12.4"
@@ -47,7 +47,7 @@
"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449"
+ resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz"
integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==
dependencies:
"@ethersproject/address" "^5.7.0"
@@ -62,7 +62,7 @@
"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef"
+ resolved "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz"
integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
@@ -75,7 +75,7 @@
"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2"
+ resolved "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz"
integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==
dependencies:
"@ethersproject/abstract-provider" "^5.7.0"
@@ -86,7 +86,7 @@
"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37"
+ resolved "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz"
integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
@@ -97,14 +97,14 @@
"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c"
+ resolved "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz"
integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b"
+ resolved "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz"
integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -112,7 +112,7 @@
"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2"
+ resolved "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz"
integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -121,21 +121,21 @@
"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d"
+ resolved "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz"
integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==
dependencies:
"@ethersproject/logger" "^5.7.0"
"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e"
+ resolved "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz"
integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/contracts@5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
+ resolved "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz"
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
dependencies:
"@ethersproject/abi" "^5.7.0"
@@ -151,7 +151,7 @@
"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7"
+ resolved "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz"
integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
@@ -166,7 +166,7 @@
"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf"
+ resolved "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz"
integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
@@ -184,7 +184,7 @@
"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360"
+ resolved "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz"
integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
@@ -203,7 +203,7 @@
"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a"
+ resolved "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz"
integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -211,19 +211,19 @@
"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892"
+ resolved "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz"
integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==
"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0":
version "5.7.1"
- resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6"
+ resolved "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz"
integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==
dependencies:
"@ethersproject/logger" "^5.7.0"
"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102"
+ resolved "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz"
integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -231,14 +231,14 @@
"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30"
+ resolved "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz"
integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==
dependencies:
"@ethersproject/logger" "^5.7.0"
"@ethersproject/providers@5.7.2":
version "5.7.2"
- resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
+ resolved "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz"
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
dependencies:
"@ethersproject/abstract-provider" "^5.7.0"
@@ -264,7 +264,7 @@
"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c"
+ resolved "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz"
integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -272,7 +272,7 @@
"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304"
+ resolved "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz"
integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -280,7 +280,7 @@
"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb"
+ resolved "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz"
integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -289,7 +289,7 @@
"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3"
+ resolved "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz"
integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -301,7 +301,7 @@
"@ethersproject/solidity@5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8"
+ resolved "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz"
integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
@@ -313,7 +313,7 @@
"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2"
+ resolved "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz"
integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -322,7 +322,7 @@
"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b"
+ resolved "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz"
integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==
dependencies:
"@ethersproject/address" "^5.7.0"
@@ -337,7 +337,7 @@
"@ethersproject/units@5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1"
+ resolved "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz"
integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
@@ -346,7 +346,7 @@
"@ethersproject/wallet@5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d"
+ resolved "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz"
integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==
dependencies:
"@ethersproject/abstract-provider" "^5.7.0"
@@ -367,7 +367,7 @@
"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0":
version "5.7.1"
- resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae"
+ resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz"
integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==
dependencies:
"@ethersproject/base64" "^5.7.0"
@@ -378,7 +378,7 @@
"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0":
version "5.7.0"
- resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5"
+ resolved "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz"
integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==
dependencies:
"@ethersproject/bytes" "^5.7.0"
@@ -389,7 +389,7 @@
"@humanwhocodes/config-array@^0.11.6":
version "0.11.7"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f"
+ resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz"
integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==
dependencies:
"@humanwhocodes/object-schema" "^1.2.1"
@@ -398,27 +398,27 @@
"@humanwhocodes/module-importer@^1.0.1":
version "1.0.1"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
"@humanwhocodes/object-schema@^1.2.1":
version "1.2.1"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@jridgewell/resolve-uri@^3.0.3":
version "3.1.0"
- resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14"
- resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
- resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
@@ -426,7 +426,7 @@
"@metamask/eth-sig-util@^4.0.0":
version "4.0.1"
- resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088"
+ resolved "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz"
integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==
dependencies:
ethereumjs-abi "^0.6.8"
@@ -435,24 +435,19 @@
tweetnacl "^1.0.3"
tweetnacl-util "^0.15.1"
-"@noble/hashes@1.1.2":
+"@noble/hashes@1.1.2", "@noble/hashes@~1.1.1":
version "1.1.2"
- resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
+ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz"
integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==
-"@noble/hashes@~1.1.1":
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.4.tgz#2611ebf5764c1bf754da7c7794de4fb30512336d"
- integrity sha512-+PYsVPrTSqtVjatKt2A/Proukn2Yrz61OBThOCKErc5w2/r1Fh37vbDv0Eah7pyNltrmacjwTvdw3JoR+WE4TA==
-
"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0":
version "1.6.3"
- resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94"
+ resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz"
integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
dependencies:
"@nodelib/fs.stat" "2.0.5"
@@ -460,12 +455,12 @@
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
version "1.2.8"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
dependencies:
"@nodelib/fs.scandir" "2.1.5"
@@ -473,7 +468,7 @@
"@nomicfoundation/ethereumjs-block@^4.0.0":
version "4.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz#fdd5c045e7baa5169abeed0e1202bf94e4481c49"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz"
integrity sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==
dependencies:
"@nomicfoundation/ethereumjs-common" "^3.0.0"
@@ -485,7 +480,7 @@
"@nomicfoundation/ethereumjs-blockchain@^6.0.0":
version "6.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz#1a8c243a46d4d3691631f139bfb3a4a157187b0c"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz"
integrity sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==
dependencies:
"@nomicfoundation/ethereumjs-block" "^4.0.0"
@@ -503,7 +498,7 @@
"@nomicfoundation/ethereumjs-common@^3.0.0":
version "3.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz#f6bcc7753994555e49ab3aa517fc8bcf89c280b9"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz"
integrity sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==
dependencies:
"@nomicfoundation/ethereumjs-util" "^8.0.0"
@@ -511,7 +506,7 @@
"@nomicfoundation/ethereumjs-ethash@^2.0.0":
version "2.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz#11539c32fe0990e1122ff987d1b84cfa34774e81"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz"
integrity sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==
dependencies:
"@nomicfoundation/ethereumjs-block" "^4.0.0"
@@ -523,7 +518,7 @@
"@nomicfoundation/ethereumjs-evm@^1.0.0":
version "1.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz#99cd173c03b59107c156a69c5e215409098a370b"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz"
integrity sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==
dependencies:
"@nomicfoundation/ethereumjs-common" "^3.0.0"
@@ -537,12 +532,12 @@
"@nomicfoundation/ethereumjs-rlp@^4.0.0", "@nomicfoundation/ethereumjs-rlp@^4.0.0-beta.2":
version "4.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz#d9a9c5f0f10310c8849b6525101de455a53e771d"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz"
integrity sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==
"@nomicfoundation/ethereumjs-statemanager@^1.0.0":
version "1.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz#14a9d4e1c828230368f7ab520c144c34d8721e4b"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz"
integrity sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==
dependencies:
"@nomicfoundation/ethereumjs-common" "^3.0.0"
@@ -555,7 +550,7 @@
"@nomicfoundation/ethereumjs-trie@^5.0.0":
version "5.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz#dcfbe3be53a94bc061c9767a396c16702bc2f5b7"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz"
integrity sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==
dependencies:
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
@@ -565,7 +560,7 @@
"@nomicfoundation/ethereumjs-tx@^4.0.0":
version "4.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz#59dc7452b0862b30342966f7052ab9a1f7802f52"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz"
integrity sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==
dependencies:
"@nomicfoundation/ethereumjs-common" "^3.0.0"
@@ -575,7 +570,7 @@
"@nomicfoundation/ethereumjs-util@^8.0.0":
version "8.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz#deb2b15d2c308a731e82977aefc4e61ca0ece6c5"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz"
integrity sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==
dependencies:
"@nomicfoundation/ethereumjs-rlp" "^4.0.0-beta.2"
@@ -583,7 +578,7 @@
"@nomicfoundation/ethereumjs-vm@^6.0.0":
version "6.0.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz#2bb50d332bf41790b01a3767ffec3987585d1de6"
+ resolved "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz"
integrity sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==
dependencies:
"@nomicfoundation/ethereumjs-block" "^4.0.0"
@@ -605,7 +600,7 @@
"@nomicfoundation/hardhat-chai-matchers@^1.0.5":
version "1.0.5"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.5.tgz#75d08bd9bd84a3cb7950be6bd27171a294516b01"
+ resolved "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.5.tgz"
integrity sha512-+W5C/+5FHI2xBajUN9THSNc1UP6FUsA7LeLmfnaC9VMi/50/DEjjxd8OmizEXgV1Bjck7my4NVQLL1Ti39FkpA==
dependencies:
"@ethersproject/abi" "^5.1.2"
@@ -617,14 +612,14 @@
"@nomicfoundation/hardhat-network-helpers@^1.0.7":
version "1.0.7"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.7.tgz#9103be2b359899a8b7996f54df12a1b7977367e3"
+ resolved "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.7.tgz"
integrity sha512-X+3mNvn8B7BY5hpIaLO+TrfzWq12bpux+ajGGdmdcfC78NXmYmOZkAtiz1QZx1YIZGMS1LaXzPXyBExxKFpCaw==
dependencies:
ethereumjs-util "^7.1.4"
"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0":
version "0.1.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz#83a7367342bd053a76d04bbcf4f373fef07cf760"
+ resolved "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz"
integrity sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw==
"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.0":
@@ -674,7 +669,7 @@
"@nomicfoundation/solidity-analyzer@^0.1.0":
version "0.1.0"
- resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz#e5ddc43ad5c0aab96e5054520d8e16212e125f50"
+ resolved "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz"
integrity sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg==
optionalDependencies:
"@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.0"
@@ -690,12 +685,12 @@
"@nomiclabs/hardhat-ethers@^2.0.6":
version "2.2.1"
- resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.1.tgz#8057b43566a0e41abeb8142064a3c0d3f23dca86"
+ resolved "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.1.tgz"
integrity sha512-RHWYwnxryWR8hzRmU4Jm/q4gzvXpetUOJ4OPlwH2YARcDB+j79+yAYCwO0lN1SUOb4++oOTJEe6AWLEc42LIvg==
"@nomiclabs/hardhat-etherscan@^3.1.0":
version "3.1.3"
- resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.3.tgz#c9dbaa4174edfa075a464a0e9142bc8710a2c4e2"
+ resolved "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.3.tgz"
integrity sha512-UeNO97j0lwOHqX7mrH6SfQQBdXq1Ng6eFr7uJKuQOrq2UVTWGD70lE5QO4fAFVPz9ao+xlNpMyIqSR7+OaDR+Q==
dependencies:
"@ethersproject/abi" "^5.1.2"
@@ -711,17 +706,17 @@
"@rari-capital/solmate@^6.2.0":
version "6.4.0"
- resolved "https://registry.yarnpkg.com/@rari-capital/solmate/-/solmate-6.4.0.tgz#c6ee4110c8075f14b415e420b13bd8bdbbc93d9e"
+ resolved "https://registry.npmjs.org/@rari-capital/solmate/-/solmate-6.4.0.tgz"
integrity sha512-BXWIHHbG5Zbgrxi0qVYe0Zs+bfx+XgOciVUACjuIApV0KzC0kY8XdO1higusIei/ZKCC+GUKdcdQZflxYPUTKQ==
"@scure/base@~1.1.0":
version "1.1.1"
- resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
+ resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz"
integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==
"@scure/bip32@1.1.0":
version "1.1.0"
- resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b"
+ resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz"
integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==
dependencies:
"@noble/hashes" "~1.1.1"
@@ -730,7 +725,7 @@
"@scure/bip39@1.1.0":
version "1.1.0"
- resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a"
+ resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz"
integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==
dependencies:
"@noble/hashes" "~1.1.1"
@@ -738,7 +733,7 @@
"@sentry/core@5.30.0":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3"
+ resolved "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz"
integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==
dependencies:
"@sentry/hub" "5.30.0"
@@ -749,7 +744,7 @@
"@sentry/hub@5.30.0":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100"
+ resolved "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz"
integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==
dependencies:
"@sentry/types" "5.30.0"
@@ -758,7 +753,7 @@
"@sentry/minimal@5.30.0":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b"
+ resolved "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz"
integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==
dependencies:
"@sentry/hub" "5.30.0"
@@ -767,7 +762,7 @@
"@sentry/node@^5.18.1":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48"
+ resolved "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz"
integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==
dependencies:
"@sentry/core" "5.30.0"
@@ -782,7 +777,7 @@
"@sentry/tracing@5.30.0":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f"
+ resolved "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz"
integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==
dependencies:
"@sentry/hub" "5.30.0"
@@ -793,12 +788,12 @@
"@sentry/types@5.30.0":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
+ resolved "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz"
integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==
"@sentry/utils@5.30.0":
version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980"
+ resolved "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz"
integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==
dependencies:
"@sentry/types" "5.30.0"
@@ -811,7 +806,7 @@
"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1", "@solidity-parser/parser@^0.14.5":
version "0.14.5"
- resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804"
+ resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz"
integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==
dependencies:
antlr4ts "^0.5.0-alpha.4"
@@ -825,27 +820,27 @@
"@tsconfig/node10@^1.0.7":
version "1.0.9"
- resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
+ resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz"
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
"@tsconfig/node12@^1.0.7":
version "1.0.11"
- resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0":
version "1.0.3"
- resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2":
version "1.0.3"
- resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
+ resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@typechain/ethers-v5@^10.0.0":
version "10.1.1"
- resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.1.1.tgz#fdb527d8854129cea5f139d76c6c6e1c9bb040ec"
+ resolved "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.1.1.tgz"
integrity sha512-o6nffJBxwmeX1ZiZpdnP/tqGd/7M7iYvQC88ZXaFFoyAGh7eYncynzVjOJV0XmaKzAc6puqyqZrnva+gJbk4sw==
dependencies:
lodash "^4.17.15"
@@ -853,59 +848,59 @@
"@typechain/hardhat@^6.0.0":
version "6.1.4"
- resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.4.tgz#da930bf17bdae5e0996b86d37992c6c58b8a49c8"
+ resolved "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.4.tgz"
integrity sha512-S8k5d1Rjc+plwKpkorlifmh72M7Ki0XNUOVVLtdbcA/vLaEkuqZSJFdddpBgS5QxiJP+6CbRa/yO6EVTE2+fMQ==
dependencies:
fs-extra "^9.1.0"
"@types/async-eventemitter@^0.2.1":
version "0.2.1"
- resolved "https://registry.yarnpkg.com/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz#f8e6280e87e8c60b2b938624b0a3530fb3e24712"
+ resolved "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz"
integrity sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==
"@types/bn.js@^4.11.3":
version "4.11.6"
- resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
+ resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz"
integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==
dependencies:
"@types/node" "*"
"@types/bn.js@^5.1.0":
version "5.1.1"
- resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682"
+ resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz"
integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==
dependencies:
"@types/node" "*"
"@types/chai-as-promised@^7.1.3":
version "7.1.5"
- resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255"
+ resolved "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz"
integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==
dependencies:
"@types/chai" "*"
"@types/chai@*", "@types/chai@^4.3.0":
version "4.3.4"
- resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4"
+ resolved "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz"
integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==
"@types/concat-stream@^1.6.0":
version "1.6.1"
- resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74"
+ resolved "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz"
integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==
dependencies:
"@types/node" "*"
"@types/form-data@0.0.33":
version "0.0.33"
- resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8"
+ resolved "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz"
integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==
dependencies:
"@types/node" "*"
"@types/glob@^7.1.1":
version "7.2.0"
- resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
+ resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz"
integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
dependencies:
"@types/minimatch" "*"
@@ -916,78 +911,83 @@
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
+"@types/is-even@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@types/is-even/-/is-even-1.0.0.tgz#37d74a80bf5cc4a8eac81961b5efd43df7450a66"
+ integrity sha512-TMfo5j24wiKIP0MzLWTJZao3xN3TxUtx8LFy2sEsxvtc2jb2fSMpPZKzMjCoYJ380m23F6Fr6E+u+0Za24V53g==
+
"@types/json-schema@^7.0.9":
version "7.0.11"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/json5@^0.0.29":
version "0.0.29"
- resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/lru-cache@^5.1.0":
version "5.1.1"
- resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef"
+ resolved "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz"
integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==
"@types/minimatch@*":
version "5.1.2"
- resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
+ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz"
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
"@types/mocha@^10.0.1":
version "10.0.1"
- resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b"
+ resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz"
integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==
"@types/node@*", "@types/node@^18.11.11":
version "18.11.11"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.11.tgz#1d455ac0211549a8409d3cdb371cd55cc971e8dc"
+ resolved "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz"
integrity sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==
"@types/node@^10.0.3":
version "10.17.60"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
+ resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz"
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
"@types/node@^8.0.0":
version "8.10.66"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3"
+ resolved "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz"
integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==
"@types/pbkdf2@^3.0.0":
version "3.1.0"
- resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1"
+ resolved "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz"
integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==
dependencies:
"@types/node" "*"
"@types/prettier@^2.1.1":
version "2.7.1"
- resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e"
+ resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz"
integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==
"@types/qs@^6.2.31":
version "6.9.7"
- resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
+ resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
"@types/secp256k1@^4.0.1":
version "4.0.3"
- resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c"
+ resolved "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz"
integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==
dependencies:
"@types/node" "*"
"@types/semver@^7.3.12":
version "7.3.13"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
+ resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz"
integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
"@typescript-eslint/eslint-plugin@^5.9.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz#ee5b51405f6c9ee7e60e4006d68c69450d3b4536"
+ resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz"
integrity sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==
dependencies:
"@typescript-eslint/scope-manager" "5.45.1"
@@ -1002,7 +1002,7 @@
"@typescript-eslint/parser@^5.9.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.1.tgz#6440ec283fa1373a12652d4e2fef4cb6e7b7e8c6"
+ resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz"
integrity sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==
dependencies:
"@typescript-eslint/scope-manager" "5.45.1"
@@ -1012,7 +1012,7 @@
"@typescript-eslint/scope-manager@5.45.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz#5b87d025eec7035d879b99c260f03be5c247883c"
+ resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz"
integrity sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==
dependencies:
"@typescript-eslint/types" "5.45.1"
@@ -1020,7 +1020,7 @@
"@typescript-eslint/type-utils@5.45.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz#cb7d300c3c95802cea9f87c7f8be363cf8f8538c"
+ resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz"
integrity sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==
dependencies:
"@typescript-eslint/typescript-estree" "5.45.1"
@@ -1030,12 +1030,12 @@
"@typescript-eslint/types@5.45.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.1.tgz#8e1883041cee23f1bb7e1343b0139f97f6a17c14"
+ resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz"
integrity sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==
"@typescript-eslint/typescript-estree@5.45.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz#b3dc37f0c4f0fe73e09917fc735e6f96eabf9ba4"
+ resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz"
integrity sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==
dependencies:
"@typescript-eslint/types" "5.45.1"
@@ -1048,7 +1048,7 @@
"@typescript-eslint/utils@5.45.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.45.1.tgz#39610c98bde82c4792f2a858b29b7d0053448be2"
+ resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz"
integrity sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==
dependencies:
"@types/json-schema" "^7.0.9"
@@ -1062,32 +1062,27 @@
"@typescript-eslint/visitor-keys@5.45.1":
version "5.45.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz#204428430ad6a830d24c5ac87c71366a1cfe1948"
+ resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz"
integrity sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==
dependencies:
"@typescript-eslint/types" "5.45.1"
eslint-visitor-keys "^3.3.0"
-abbrev@1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
- integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
-
-abbrev@1.0.x:
+abbrev@1, abbrev@1.0.x:
version "1.0.9"
- resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
+ resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz"
integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==
abort-controller@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
+ resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"
abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741"
+ resolved "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz"
integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==
dependencies:
buffer "^6.0.3"
@@ -1100,49 +1095,49 @@ abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3:
acorn-jsx@^5.0.0, acorn-jsx@^5.3.2:
version "5.3.2"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-walk@^8.1.1:
version "8.2.0"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
+ resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^6.0.7:
version "6.4.2"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
+ resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
acorn@^8.4.1, acorn@^8.8.0:
version "8.8.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
+ resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz"
integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
address@^1.0.1:
version "1.2.1"
- resolved "https://registry.yarnpkg.com/address/-/address-1.2.1.tgz#25bb61095b7522d65b357baa11bc05492d4c8acd"
+ resolved "https://registry.npmjs.org/address/-/address-1.2.1.tgz"
integrity sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==
adm-zip@^0.4.16:
version "0.4.16"
- resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365"
+ resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz"
integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==
aes-js@3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d"
+ resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz"
integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==
agent-base@6:
version "6.0.2"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+ resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
aggregate-error@^3.0.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
+ resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz"
integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
dependencies:
clean-stack "^2.0.0"
@@ -1150,7 +1145,7 @@ aggregate-error@^3.0.0:
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1:
version "6.12.6"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
@@ -1160,7 +1155,7 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1:
ajv@^8.0.1:
version "8.11.2"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78"
+ resolved "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz"
integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==
dependencies:
fast-deep-equal "^3.1.1"
@@ -1170,88 +1165,88 @@ ajv@^8.0.1:
amdefine@>=0.0.4:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+ resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz"
integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==
ansi-colors@3.2.3:
version "3.2.3"
- resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
+ resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz"
integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==
ansi-colors@4.1.1:
version "4.1.1"
- resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
+ resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-colors@^4.1.1:
version "4.1.3"
- resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+ resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz"
integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
ansi-escapes@^3.2.0:
version "3.2.0"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
+ resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz"
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
ansi-escapes@^4.3.0:
version "4.3.2"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+ resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz"
integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
dependencies:
type-fest "^0.21.3"
ansi-regex@^3.0.0:
version "3.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1"
+ resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz"
integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==
ansi-regex@^4.1.0:
version "4.1.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
+ resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz"
integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
ansi-regex@^5.0.1:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-regex@^6.0.1:
version "6.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
+ resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz"
integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
version "3.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
ansi-styles@^6.0.0:
version "6.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+ resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
antlr4@4.7.1:
version "4.7.1"
- resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773"
+ resolved "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz"
integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ==
antlr4ts@^0.5.0-alpha.4:
version "0.5.0-alpha.4"
- resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a"
+ resolved "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz"
integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==
anymatch@~3.1.1, anymatch@~3.1.2:
version "3.1.3"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+ resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
@@ -1259,34 +1254,34 @@ anymatch@~3.1.1, anymatch@~3.1.2:
arg@^4.1.0:
version "4.1.3"
- resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7:
version "1.0.10"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
array-back@^3.0.1, array-back@^3.1.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0"
+ resolved "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz"
integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==
array-back@^4.0.1, array-back@^4.0.2:
version "4.0.2"
- resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e"
+ resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz"
integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==
array-includes@^3.1.4:
version "3.1.6"
- resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f"
+ resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz"
integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==
dependencies:
call-bind "^1.0.2"
@@ -1297,17 +1292,17 @@ array-includes@^3.1.4:
array-union@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+ resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
array-uniq@1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+ resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz"
integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==
array.prototype.flat@^1.2.5:
version "1.3.1"
- resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2"
+ resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz"
integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==
dependencies:
call-bind "^1.0.2"
@@ -1317,7 +1312,7 @@ array.prototype.flat@^1.2.5:
array.prototype.reduce@^1.0.5:
version "1.0.5"
- resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac"
+ resolved "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz"
integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==
dependencies:
call-bind "^1.0.2"
@@ -1328,44 +1323,44 @@ array.prototype.reduce@^1.0.5:
asap@~2.0.6:
version "2.0.6"
- resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+ resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
asn1@~0.2.3:
version "0.2.6"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
+ resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz"
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+ resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
assertion-error@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
+ resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz"
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
ast-parents@0.0.1:
version "0.0.1"
- resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3"
+ resolved "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz"
integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==
astral-regex@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+ resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz"
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
astral-regex@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+ resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
async-eventemitter@^0.2.4:
version "0.2.4"
- resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca"
+ resolved "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz"
integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==
dependencies:
async "^2.4.0"
@@ -1377,117 +1372,117 @@ async@1.x, async@>=2.6.4, async@^2.4.0:
asynckit@^0.4.0:
version "0.4.0"
- resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
at-least-node@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
+ resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
aws-sign2@~0.7.0:
version "0.7.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+ resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz"
integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==
aws4@^1.8.0:
version "1.11.0"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
+ resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
balanced-match@^1.0.0:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base-x@^3.0.2:
version "3.0.9"
- resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
+ resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz"
integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
dependencies:
safe-buffer "^5.0.1"
base64-js@^1.3.1:
version "1.5.1"
- resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+ resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz"
integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==
dependencies:
tweetnacl "^0.14.3"
bech32@1.1.4:
version "1.1.4"
- resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"
+ resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz"
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
bigint-crypto-utils@^3.0.23:
version "3.1.7"
- resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.1.7.tgz#c4c1b537c7c1ab7aadfaecf3edfd45416bf2c651"
+ resolved "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.7.tgz"
integrity sha512-zpCQpIE2Oy5WIQpjC9iYZf8Uh9QqoS51ZCooAcNvzv1AQ3VWdT52D0ksr1+/faeK8HVIej1bxXcP75YcqH3KPA==
dependencies:
bigint-mod-arith "^3.1.0"
bigint-mod-arith@^3.1.0:
version "3.1.2"
- resolved "https://registry.yarnpkg.com/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz#658e416bc593a463d97b59766226d0a3021a76b1"
+ resolved "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz"
integrity sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ==
bignumber.js@^9.0.1:
version "9.1.1"
- resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6"
+ resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz"
integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==
binary-extensions@^2.0.0:
version "2.2.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+ resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
blakejs@^1.1.0:
version "1.2.1"
- resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
+ resolved "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz"
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
bn.js@4.11.6:
version "4.11.6"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
+ resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz"
integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==
bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9:
version "4.12.0"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
+ resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1:
version "5.2.1"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
+ resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
brace-expansion@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
brorand@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+ resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz"
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
browser-level@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011"
+ resolved "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz"
integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==
dependencies:
abstract-level "^1.0.2"
@@ -1497,12 +1492,12 @@ browser-level@^1.0.1:
browser-stdout@1.3.1:
version "1.3.1"
- resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+ resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
browserify-aes@^1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+ resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz"
integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
dependencies:
buffer-xor "^1.0.3"
@@ -1514,14 +1509,14 @@ browserify-aes@^1.2.0:
bs58@^4.0.0:
version "4.0.1"
- resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
+ resolved "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz"
integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==
dependencies:
base-x "^3.0.2"
bs58check@^2.1.2:
version "2.1.2"
- resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
+ resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz"
integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==
dependencies:
bs58 "^4.0.0"
@@ -1530,12 +1525,12 @@ bs58check@^2.1.2:
buffer-from@^1.0.0:
version "1.1.2"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-reverse@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60"
+ resolved "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz"
integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==
buffer-xor@^1.0.3:
@@ -1553,21 +1548,21 @@ buffer@^6.0.3:
builtins@^5.0.1:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9"
+ resolved "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz"
integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==
dependencies:
semver "^7.0.0"
busboy@^1.6.0:
version "1.6.0"
- resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
+ resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
dependencies:
streamsearch "^1.1.0"
bytes@3.1.2:
version "3.1.2"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
cacheable-lookup@^7.0.0:
@@ -1575,10 +1570,10 @@ cacheable-lookup@^7.0.0:
resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27"
integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==
-cacheable-request@^10.2.1:
- version "10.2.7"
- resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.7.tgz#8bb8da66338f321b3cbbc34a71ac231178171bcc"
- integrity sha512-I4SA6mKgDxcxVbSt/UmIkb9Ny8qSkg6ReBHtAAXnZHk7KOSx5g3DTiAOaYzcHCs6oOdHn+bip9T48E6tMvK9hw==
+cacheable-request@^10.2.8:
+ version "10.2.8"
+ resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.8.tgz#899ae6c0c8c7127f263b2005ecaac07c95124079"
+ integrity sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==
dependencies:
"@types/http-cache-semantics" "^4.0.1"
get-stream "^6.0.1"
@@ -1590,7 +1585,7 @@ cacheable-request@^10.2.1:
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
+ resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
@@ -1598,60 +1593,60 @@ call-bind@^1.0.0, call-bind@^1.0.2:
caller-callsite@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+ resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz"
integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==
dependencies:
callsites "^2.0.0"
caller-path@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+ resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz"
integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==
dependencies:
caller-callsite "^2.0.0"
callsites@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+ resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz"
integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==
callsites@^3.0.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
camelcase@^6.0.0:
version "6.3.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caseless@^0.12.0, caseless@~0.12.0:
version "0.12.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+ resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz"
integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
catering@^2.1.0, catering@^2.1.1:
version "2.1.1"
- resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510"
+ resolved "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz"
integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==
cbor@^8.1.0:
version "8.1.0"
- resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5"
+ resolved "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz"
integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==
dependencies:
nofilter "^3.1.0"
chai-as-promised@^7.1.1:
version "7.1.1"
- resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0"
+ resolved "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz"
integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==
dependencies:
check-error "^1.0.2"
chai@^4.3.4:
version "4.3.7"
- resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51"
+ resolved "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz"
integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==
dependencies:
assertion-error "^1.1.0"
@@ -1664,16 +1659,16 @@ chai@^4.3.4:
chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2:
version "2.4.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^4.0.0, chalk@^4.1.0:
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
@@ -1681,22 +1676,22 @@ chalk@^4.0.0, chalk@^4.1.0:
chardet@^0.7.0:
version "0.7.0"
- resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+ resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
"charenc@>= 0.0.1":
version "0.0.2"
- resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
+ resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz"
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
check-error@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
+ resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz"
integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
chokidar@3.3.0:
version "3.3.0"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6"
+ resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz"
integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==
dependencies:
anymatch "~3.1.1"
@@ -1711,7 +1706,7 @@ chokidar@3.3.0:
chokidar@3.5.3, chokidar@^3.4.0:
version "3.5.3"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+ resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
@@ -1731,12 +1726,12 @@ chownr@^2.0.0:
ci-info@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+ resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+ resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz"
integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
dependencies:
inherits "^2.0.1"
@@ -1744,7 +1739,7 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
classic-level@^1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.2.0.tgz#2d52bdec8e7a27f534e67fdeb890abef3e643c27"
+ resolved "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz"
integrity sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==
dependencies:
abstract-level "^1.0.2"
@@ -1755,26 +1750,37 @@ classic-level@^1.2.0:
clean-stack@^2.0.0:
version "2.2.0"
- resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
+ resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+cli-barchart@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/cli-barchart/-/cli-barchart-0.2.3.tgz#6f05239897a9c0bc8fdde0b5017afa1e36778861"
+ integrity sha512-p1bzsMXYp7h/ut4IW0R7bi2eVugIAODmepo/JHvyATXiED+I1qkPLFyKS5WDVhbtbgovl5Uo8RN6MJw1hbJIHA==
+ dependencies:
+ "@types/is-even" "^1.0.0"
+ chalk "^4.1.2"
+ is-even "^1.0.0"
+ string-width "^4.2.3"
+ term-size "^2.2.1"
+
cli-cursor@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz"
integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==
dependencies:
restore-cursor "^2.0.0"
cli-cursor@^3.1.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz"
integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
dependencies:
restore-cursor "^3.1.0"
cli-table3@^0.5.0:
version "0.5.1"
- resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202"
+ resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz"
integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==
dependencies:
object-assign "^4.1.0"
@@ -1784,7 +1790,7 @@ cli-table3@^0.5.0:
cli-truncate@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+ resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz"
integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
dependencies:
slice-ansi "^3.0.0"
@@ -1792,7 +1798,7 @@ cli-truncate@^2.1.0:
cli-truncate@^3.1.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389"
+ resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz"
integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==
dependencies:
slice-ansi "^5.0.0"
@@ -1800,12 +1806,12 @@ cli-truncate@^3.1.0:
cli-width@^2.0.0:
version "2.2.1"
- resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
+ resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz"
integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
cliui@^5.0.0:
version "5.0.0"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+ resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz"
integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
dependencies:
string-width "^3.1.0"
@@ -1814,7 +1820,7 @@ cliui@^5.0.0:
cliui@^7.0.2:
version "7.0.4"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz"
integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
dependencies:
string-width "^4.2.0"
@@ -1823,53 +1829,53 @@ cliui@^7.0.2:
color-convert@^1.9.0:
version "1.9.3"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-convert@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@1.1.3:
version "1.1.3"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4:
version "1.1.4"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colorette@^2.0.19:
version "2.0.19"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz"
integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
colors@1.4.0, colors@^1.1.2:
version "1.4.0"
- resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
+ resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
command-exists@^1.2.8:
version "1.2.9"
- resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
+ resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz"
integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
command-line-args@^5.1.1:
version "5.2.1"
- resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e"
+ resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz"
integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==
dependencies:
array-back "^3.1.0"
@@ -1879,7 +1885,7 @@ command-line-args@^5.1.1:
command-line-usage@^6.1.0:
version "6.1.3"
- resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957"
+ resolved "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz"
integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==
dependencies:
array-back "^4.0.2"
@@ -1889,22 +1895,22 @@ command-line-usage@^6.1.0:
commander@2.18.0:
version "2.18.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
+ resolved "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz"
integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==
commander@3.0.2:
version "3.0.2"
- resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
+ resolved "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz"
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
commander@^9.4.1:
version "9.4.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd"
+ resolved "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz"
integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==
concat-stream@^1.6.0, concat-stream@^1.6.2:
version "1.6.2"
- resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+ resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
@@ -1914,7 +1920,7 @@ concat-stream@^1.6.0, concat-stream@^1.6.2:
cookie@^0.4.1:
version "0.4.2"
- resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
+ resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
cookiejar@>=2.1.4:
@@ -1922,19 +1928,14 @@ cookiejar@>=2.1.4:
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
-core-util-is@1.0.2:
+core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==
-core-util-is@~1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
- integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
-
cosmiconfig@^5.0.7:
version "5.2.1"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+ resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz"
integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
dependencies:
import-fresh "^2.0.0"
@@ -1944,12 +1945,12 @@ cosmiconfig@^5.0.7:
crc-32@^1.2.0:
version "1.2.2"
- resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
+ resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz"
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+ resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz"
integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
dependencies:
cipher-base "^1.0.1"
@@ -1960,7 +1961,7 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
create-hmac@^1.1.4, create-hmac@^1.1.7:
version "1.1.7"
- resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+ resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz"
integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
dependencies:
cipher-base "^1.0.3"
@@ -1972,7 +1973,7 @@ create-hmac@^1.1.4, create-hmac@^1.1.7:
create-require@^1.1.0:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@>=3.1.5:
@@ -1984,7 +1985,7 @@ cross-fetch@>=3.1.5:
cross-spawn@^6.0.5:
version "6.0.5"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+ resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies:
nice-try "^1.0.4"
@@ -1995,7 +1996,7 @@ cross-spawn@^6.0.5:
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
@@ -2004,62 +2005,62 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
"crypt@>= 0.0.1":
version "0.0.2"
- resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
+ resolved "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz"
integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
crypto-js@^3.1.9-1:
version "3.3.0"
- resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
+ resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz"
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
dashdash@^1.12.0:
version "1.14.1"
- resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz"
integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==
dependencies:
assert-plus "^1.0.0"
data-uri-to-buffer@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b"
- integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
+ integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
death@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318"
+ resolved "https://registry.npmjs.org/death/-/death-1.1.0.tgz"
integrity sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==
debug@3.2.6:
version "3.2.6"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+ resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^2.6.9:
version "2.6.9"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^3.2.7:
version "3.2.7"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+ resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
decamelize@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
+ resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
decompress-response@^6.0.0:
@@ -2071,19 +2072,19 @@ decompress-response@^6.0.0:
deep-eql@^4.0.1, deep-eql@^4.1.2:
version "4.1.3"
- resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
+ resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz"
integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
dependencies:
type-detect "^4.0.0"
deep-extend@~0.6.0:
version "0.6.0"
- resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+ resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deep-is@^0.1.3, deep-is@~0.1.3:
version "0.1.4"
- resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
defer-to-connect@^2.0.1:
@@ -2093,7 +2094,7 @@ defer-to-connect@^2.0.1:
define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4:
version "1.1.4"
- resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
+ resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz"
integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
dependencies:
has-property-descriptors "^1.0.0"
@@ -2101,17 +2102,17 @@ define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4:
delayed-stream@~1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
depd@2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
detect-port@^1.3.0:
version "1.5.1"
- resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b"
+ resolved "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz"
integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==
dependencies:
address "^1.0.1"
@@ -2119,60 +2120,60 @@ detect-port@^1.3.0:
diff@3.5.0:
version "3.5.0"
- resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+ resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diff@5.0.0:
version "5.0.0"
- resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
+ resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diff@^4.0.1:
version "4.0.2"
- resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
difflib@^0.2.4:
version "0.2.4"
- resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e"
+ resolved "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz"
integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==
dependencies:
heap ">= 0.2.0"
dir-glob@^3.0.1:
version "3.0.1"
- resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+ resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz"
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
dependencies:
path-type "^4.0.0"
doctrine@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+ resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz"
integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
dependencies:
esutils "^2.0.2"
doctrine@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz"
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
dependencies:
esutils "^2.0.2"
dotenv@^16.0.0:
version "16.0.3"
- resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
+ resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
eastasianwidth@^0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+ resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecc-jsbn@~0.1.1:
version "0.1.2"
- resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+ resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz"
integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==
dependencies:
jsbn "~0.1.0"
@@ -2193,46 +2194,46 @@ elliptic@6.5.4, elliptic@>=6.5.4, elliptic@^6.5.2, elliptic@^6.5.4:
emoji-regex@^10.2.1:
version "10.2.1"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f"
+ resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz"
integrity sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==
emoji-regex@^7.0.1:
version "7.0.3"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+ resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emoji-regex@^8.0.0:
version "8.0.0"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
emoji-regex@^9.2.2:
version "9.2.2"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+ resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
enquirer@^2.3.0:
version "2.3.6"
- resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
+ resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
dependencies:
ansi-colors "^4.1.1"
env-paths@^2.2.0:
version "2.2.1"
- resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
+ resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
error-ex@^1.3.1:
version "1.3.2"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
dependencies:
is-arrayish "^0.2.1"
es-abstract@^1.19.0, es-abstract@^1.20.4:
version "1.20.4"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861"
+ resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz"
integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==
dependencies:
call-bind "^1.0.2"
@@ -2262,19 +2263,19 @@ es-abstract@^1.19.0, es-abstract@^1.20.4:
es-array-method-boxes-properly@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
+ resolved "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz"
integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
es-shim-unscopables@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
+ resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz"
integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==
dependencies:
has "^1.0.3"
es-to-primitive@^1.2.1:
version "1.2.1"
- resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+ resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz"
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
dependencies:
is-callable "^1.1.4"
@@ -2283,22 +2284,22 @@ es-to-primitive@^1.2.1:
escalade@^3.1.1:
version "3.1.1"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
escodegen@1.8.x:
version "1.8.1"
- resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
+ resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz"
integrity sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==
dependencies:
esprima "^2.7.1"
@@ -2310,17 +2311,17 @@ escodegen@1.8.x:
eslint-config-prettier@^8.3.0:
version "8.5.0"
- resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1"
+ resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz"
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
eslint-config-standard@^17.0.0:
version "17.0.0"
- resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz#fd5b6cf1dcf6ba8d29f200c461de2e19069888cf"
+ resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz"
integrity sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==
eslint-import-resolver-node@^0.3.6:
version "0.3.6"
- resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
+ resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz"
integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
dependencies:
debug "^3.2.7"
@@ -2328,14 +2329,14 @@ eslint-import-resolver-node@^0.3.6:
eslint-module-utils@^2.7.3:
version "2.7.4"
- resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974"
+ resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz"
integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==
dependencies:
debug "^3.2.7"
eslint-plugin-es@^4.1.0:
version "4.1.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9"
+ resolved "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz"
integrity sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==
dependencies:
eslint-utils "^2.0.0"
@@ -2343,7 +2344,7 @@ eslint-plugin-es@^4.1.0:
eslint-plugin-import@^2.25.4:
version "2.26.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
+ resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz"
integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==
dependencies:
array-includes "^3.1.4"
@@ -2362,7 +2363,7 @@ eslint-plugin-import@^2.25.4:
eslint-plugin-n@^15.2.0:
version "15.6.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.6.0.tgz#cfb1d2e2e427d620eb9008f8b3b5a40de0c84120"
+ resolved "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.0.tgz"
integrity sha512-Hd/F7wz4Mj44Jp0H6Jtty13NcE69GNTY0rVlgTIj1XBnGGVI6UTdDrpE6vqu3AHo07bygq/N+7OH/lgz1emUJw==
dependencies:
builtins "^5.0.1"
@@ -2376,19 +2377,19 @@ eslint-plugin-n@^15.2.0:
eslint-plugin-prettier@^4.0.0:
version "4.2.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
+ resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz"
integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-promise@^6.0.0:
version "6.1.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816"
+ resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz"
integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==
eslint-scope@^4.0.3:
version "4.0.3"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
+ resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz"
integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
dependencies:
esrecurse "^4.1.0"
@@ -2396,7 +2397,7 @@ eslint-scope@^4.0.3:
eslint-scope@^5.1.1:
version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
dependencies:
esrecurse "^4.3.0"
@@ -2404,7 +2405,7 @@ eslint-scope@^5.1.1:
eslint-scope@^7.1.1:
version "7.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+ resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz"
integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
dependencies:
esrecurse "^4.3.0"
@@ -2412,43 +2413,43 @@ eslint-scope@^7.1.1:
eslint-utils@^1.3.1:
version "1.4.3"
- resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
+ resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz"
integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==
dependencies:
eslint-visitor-keys "^1.1.0"
eslint-utils@^2.0.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+ resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz"
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
dependencies:
eslint-visitor-keys "^1.1.0"
eslint-utils@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz"
integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
dependencies:
eslint-visitor-keys "^2.0.0"
eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
version "1.3.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+ resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz"
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
eslint-visitor-keys@^2.0.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
eslint-visitor-keys@^3.3.0:
version "3.3.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@^5.6.0:
version "5.16.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
+ resolved "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz"
integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==
dependencies:
"@babel/code-frame" "^7.0.0"
@@ -2490,7 +2491,7 @@ eslint@^5.6.0:
eslint@^8.6.0:
version "8.29.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87"
+ resolved "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz"
integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==
dependencies:
"@eslint/eslintrc" "^1.3.3"
@@ -2535,7 +2536,7 @@ eslint@^8.6.0:
espree@^5.0.1:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a"
+ resolved "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz"
integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==
dependencies:
acorn "^6.0.7"
@@ -2544,7 +2545,7 @@ espree@^5.0.1:
espree@^9.4.0:
version "9.4.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd"
+ resolved "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz"
integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==
dependencies:
acorn "^8.8.0"
@@ -2553,51 +2554,51 @@ espree@^9.4.0:
esprima@2.7.x, esprima@^2.7.1:
version "2.7.3"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+ resolved "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz"
integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==
esprima@^4.0.0:
version "4.0.1"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esquery@^1.0.1, esquery@^1.4.0:
version "1.4.0"
- resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz"
integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
dependencies:
estraverse "^5.1.0"
esrecurse@^4.1.0, esrecurse@^4.3.0:
version "4.3.0"
- resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz"
integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
dependencies:
estraverse "^5.2.0"
estraverse@^1.9.1:
version "1.9.3"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
+ resolved "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz"
integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==
estraverse@^4.1.1:
version "4.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
esutils@^2.0.2:
version "2.0.3"
- resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
eth-gas-reporter@^0.2.25:
version "0.2.25"
- resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz#546dfa946c1acee93cb1a94c2a1162292d6ff566"
+ resolved "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz"
integrity sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==
dependencies:
"@ethersproject/abi" "^5.0.0-beta.146"
@@ -2618,14 +2619,14 @@ eth-gas-reporter@^0.2.25:
ethereum-bloom-filters@^1.0.6:
version "1.0.10"
- resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a"
+ resolved "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz"
integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==
dependencies:
js-sha3 "^0.8.0"
ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3:
version "0.1.3"
- resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191"
+ resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz"
integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==
dependencies:
"@types/pbkdf2" "^3.0.0"
@@ -2646,7 +2647,7 @@ ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3:
ethereum-cryptography@^1.0.3:
version "1.1.2"
- resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d"
+ resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz"
integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==
dependencies:
"@noble/hashes" "1.1.2"
@@ -2656,7 +2657,7 @@ ethereum-cryptography@^1.0.3:
ethereumjs-abi@^0.6.8:
version "0.6.8"
- resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae"
+ resolved "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz"
integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==
dependencies:
bn.js "^4.11.8"
@@ -2664,7 +2665,7 @@ ethereumjs-abi@^0.6.8:
ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1:
version "6.2.1"
- resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69"
+ resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz"
integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==
dependencies:
"@types/bn.js" "^4.11.3"
@@ -2677,7 +2678,7 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1:
ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4:
version "7.1.5"
- resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181"
+ resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz"
integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==
dependencies:
"@types/bn.js" "^5.1.0"
@@ -2688,12 +2689,12 @@ ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4:
ethers-eip712@^0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/ethers-eip712/-/ethers-eip712-0.2.0.tgz#52973b3a9a22638f7357283bf66624994c6e91ed"
+ resolved "https://registry.npmjs.org/ethers-eip712/-/ethers-eip712-0.2.0.tgz"
integrity sha512-fgS196gCIXeiLwhsWycJJuxI9nL/AoUPGSQ+yvd+8wdWR+43G+J1n69LmWVWvAON0M6qNaf2BF4/M159U8fujQ==
ethers@^4.0.40:
version "4.0.49"
- resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894"
+ resolved "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz"
integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==
dependencies:
aes-js "3.0.0"
@@ -2708,7 +2709,7 @@ ethers@^4.0.40:
ethers@^5.5.3:
version "5.7.2"
- resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
+ resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
dependencies:
"@ethersproject/abi" "5.7.0"
@@ -2744,7 +2745,7 @@ ethers@^5.5.3:
ethjs-unit@0.1.6:
version "0.1.6"
- resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
+ resolved "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz"
integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==
dependencies:
bn.js "4.11.6"
@@ -2752,7 +2753,7 @@ ethjs-unit@0.1.6:
ethjs-util@0.1.6, ethjs-util@^0.1.6:
version "0.1.6"
- resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536"
+ resolved "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz"
integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==
dependencies:
is-hex-prefixed "1.0.0"
@@ -2760,12 +2761,12 @@ ethjs-util@0.1.6, ethjs-util@^0.1.6:
event-target-shim@^5.0.0:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
+ resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
evp_bytestokey@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+ resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz"
integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
dependencies:
md5.js "^1.3.4"
@@ -2773,7 +2774,7 @@ evp_bytestokey@^1.0.3:
execa@^6.1.0:
version "6.1.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20"
+ resolved "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz"
integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==
dependencies:
cross-spawn "^7.0.3"
@@ -2788,41 +2789,36 @@ execa@^6.1.0:
extend@~3.0.2:
version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
external-editor@^3.0.3:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+ resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz"
integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
dependencies:
chardet "^0.7.0"
iconv-lite "^0.4.24"
tmp "^0.0.33"
-extsprintf@1.3.0:
+extsprintf@1.3.0, extsprintf@^1.2.0:
version "1.3.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+ resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz"
integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==
-extsprintf@^1.2.0:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
- integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
-
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-diff@^1.1.2:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+ resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
fast-glob@^3.0.3, fast-glob@^3.2.9:
version "3.2.12"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
+ resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
@@ -2833,17 +2829,17 @@ fast-glob@^3.0.3, fast-glob@^3.2.9:
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
version "2.0.6"
- resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fastq@^1.6.0:
version "1.14.0"
- resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce"
+ resolved "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz"
integrity sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==
dependencies:
reusify "^1.0.4"
@@ -2858,49 +2854,49 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
figures@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz"
integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==
dependencies:
escape-string-regexp "^1.0.5"
file-entry-cache@^5.0.1:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
+ resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz"
integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
dependencies:
flat-cache "^2.0.1"
file-entry-cache@^6.0.1:
version "6.0.1"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
dependencies:
flat-cache "^3.0.4"
fill-range@^7.0.1:
version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
find-replace@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38"
+ resolved "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz"
integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==
dependencies:
array-back "^3.0.1"
find-up@3.0.0, find-up@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz"
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
dependencies:
locate-path "^3.0.0"
find-up@5.0.0, find-up@^5.0.0:
version "5.0.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz"
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
dependencies:
locate-path "^6.0.0"
@@ -2908,14 +2904,14 @@ find-up@5.0.0, find-up@^5.0.0:
find-up@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz"
integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==
dependencies:
locate-path "^2.0.0"
flat-cache@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
+ resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz"
integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
dependencies:
flatted "^2.0.0"
@@ -2924,7 +2920,7 @@ flat-cache@^2.0.1:
flat-cache@^3.0.4:
version "3.0.4"
- resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz"
integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
dependencies:
flatted "^3.1.0"
@@ -2937,22 +2933,22 @@ flat@>=5.0.1, flat@^4.1.0, flat@^5.0.2:
flatted@^2.0.0:
version "2.0.2"
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
+ resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz"
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
flatted@^3.1.0:
version "3.2.7"
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
+ resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz"
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
follow-redirects@^1.12.1:
version "1.15.2"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
+ resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
forever-agent@~0.6.1:
version "0.6.1"
- resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+ resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==
form-data-encoder@^2.1.2:
@@ -2960,18 +2956,9 @@ form-data-encoder@^2.1.2:
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5"
integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==
-form-data@^2.2.0:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
- integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.6"
- mime-types "^2.1.12"
-
-form-data@~2.3.2:
+form-data@^2.2.0, form-data@~2.3.2:
version "2.3.3"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+ resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
@@ -2985,19 +2972,14 @@ formdata-polyfill@^4.0.10:
dependencies:
fetch-blob "^3.1.2"
-fp-ts@1.19.3:
+fp-ts@1.19.3, fp-ts@^1.0.0:
version "1.19.3"
- resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f"
+ resolved "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz"
integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==
-fp-ts@^1.0.0:
- version "1.19.5"
- resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a"
- integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A==
-
fs-extra@^0.30.0:
version "0.30.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
+ resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz"
integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==
dependencies:
graceful-fs "^4.1.2"
@@ -3008,7 +2990,7 @@ fs-extra@^0.30.0:
fs-extra@^7.0.0, fs-extra@^7.0.1:
version "7.0.1"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+ resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz"
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
dependencies:
graceful-fs "^4.1.2"
@@ -3017,7 +2999,7 @@ fs-extra@^7.0.0, fs-extra@^7.0.1:
fs-extra@^8.1.0:
version "8.1.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+ resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
@@ -3026,7 +3008,7 @@ fs-extra@^8.1.0:
fs-extra@^9.1.0:
version "9.1.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
+ resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz"
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
dependencies:
at-least-node "^1.0.0"
@@ -3043,32 +3025,32 @@ fs-minipass@^2.0.0:
fs-readdir-recursive@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
+ resolved "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz"
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
fs.realpath@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.1.1:
version "2.1.3"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
+ resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz"
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
fsevents@~2.3.2:
version "2.3.2"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
function.prototype.name@^1.1.5:
version "1.1.5"
- resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
+ resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz"
integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
dependencies:
call-bind "^1.0.2"
@@ -3078,27 +3060,27 @@ function.prototype.name@^1.1.5:
functional-red-black-tree@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz"
integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
functions-have-names@^1.2.2:
version "1.2.3"
- resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+ resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
- resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-func-name@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
+ resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz"
integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3:
version "1.1.3"
- resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
+ resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz"
integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
dependencies:
function-bind "^1.1.1"
@@ -3107,17 +3089,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@
get-port@^3.1.0:
version "3.2.0"
- resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
+ resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz"
integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==
get-stream@^6.0.1:
version "6.0.1"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
get-symbol-description@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
+ resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz"
integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
dependencies:
call-bind "^1.0.2"
@@ -3125,14 +3107,14 @@ get-symbol-description@^1.0.0:
getpass@^0.1.1:
version "0.1.7"
- resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz"
integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==
dependencies:
assert-plus "^1.0.0"
ghost-testrpc@^0.0.2:
version "0.0.2"
- resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92"
+ resolved "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz"
integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==
dependencies:
chalk "^2.4.2"
@@ -3140,21 +3122,21 @@ ghost-testrpc@^0.0.2:
glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2:
version "5.1.2"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob-parent@^6.0.2:
version "6.0.2"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz"
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
dependencies:
is-glob "^4.0.3"
glob@7.1.3:
version "7.1.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+ resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
dependencies:
fs.realpath "^1.0.0"
@@ -3166,7 +3148,7 @@ glob@7.1.3:
glob@7.1.7:
version "7.1.7"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
+ resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
dependencies:
fs.realpath "^1.0.0"
@@ -3176,9 +3158,9 @@ glob@7.1.7:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@7.2.0:
+glob@7.2.0, glob@^7.0.0, glob@^7.1.2, glob@^7.1.3:
version "7.2.0"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
dependencies:
fs.realpath "^1.0.0"
@@ -3190,7 +3172,7 @@ glob@7.2.0:
glob@^5.0.15:
version "5.0.15"
- resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+ resolved "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz"
integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==
dependencies:
inflight "^1.0.4"
@@ -3199,28 +3181,16 @@ glob@^5.0.15:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.0.0, glob@^7.1.2, glob@^7.1.3:
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
- integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.1.1"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
global-modules@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
+ resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz"
integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
dependencies:
global-prefix "^3.0.0"
global-prefix@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+ resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz"
integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
dependencies:
ini "^1.3.5"
@@ -3229,19 +3199,19 @@ global-prefix@^3.0.0:
globals@^11.7.0:
version "11.12.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^13.15.0:
version "13.18.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc"
+ resolved "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz"
integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==
dependencies:
type-fest "^0.20.2"
globby@^10.0.1:
version "10.0.2"
- resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
+ resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz"
integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==
dependencies:
"@types/glob" "^7.1.1"
@@ -3255,7 +3225,7 @@ globby@^10.0.1:
globby@^11.1.0:
version "11.1.0"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
@@ -3266,14 +3236,14 @@ globby@^11.1.0:
slash "^3.0.0"
got@>=11.8.5:
- version "12.5.3"
- resolved "https://registry.yarnpkg.com/got/-/got-12.5.3.tgz#82bdca2dd61258a02e24d668ea6e7abb70ac3598"
- integrity sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==
+ version "12.6.0"
+ resolved "https://registry.yarnpkg.com/got/-/got-12.6.0.tgz#8d382ee5de4432c086e83c133efdd474484f6ac7"
+ integrity sha512-WTcaQ963xV97MN3x0/CbAriXFZcXCfgxVp91I+Ze6pawQOa7SgzwSx2zIJJsX+kTajMnVs0xcFD1TxZKFqhdnQ==
dependencies:
"@sindresorhus/is" "^5.2.0"
"@szmarczak/http-timer" "^5.0.1"
cacheable-lookup "^7.0.0"
- cacheable-request "^10.2.1"
+ cacheable-request "^10.2.8"
decompress-response "^6.0.0"
form-data-encoder "^2.1.2"
get-stream "^6.0.1"
@@ -3284,22 +3254,22 @@ got@>=11.8.5:
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0:
version "4.2.10"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
grapheme-splitter@^1.0.4:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
+ resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
growl@1.10.5:
version "1.10.5"
- resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
+ resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
handlebars@^4.0.1:
version "4.7.7"
- resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
+ resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
@@ -3311,12 +3281,12 @@ handlebars@^4.0.1:
har-schema@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+ resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz"
integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==
har-validator@~5.1.3:
version "5.1.5"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
+ resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz"
integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
dependencies:
ajv "^6.12.3"
@@ -3324,7 +3294,7 @@ har-validator@~5.1.3:
hardhat-gas-reporter@^1.0.7:
version "1.0.9"
- resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz#9a2afb354bc3b6346aab55b1c02ca556d0e16450"
+ resolved "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz"
integrity sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==
dependencies:
array-uniq "1.0.3"
@@ -3389,53 +3359,53 @@ hardhat@^2.12.1-ir.0:
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
+ resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz"
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
has-flag@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+ resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz"
integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==
has-flag@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-flag@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-property-descriptors@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
+ resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz"
integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
dependencies:
get-intrinsic "^1.1.1"
has-symbols@^1.0.0, has-symbols@^1.0.2, has-symbols@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has-tostringtag@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
+ resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz"
integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
dependencies:
has-symbols "^1.0.2"
has@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
hash-base@^3.0.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
+ resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz"
integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==
dependencies:
inherits "^2.0.4"
@@ -3444,7 +3414,7 @@ hash-base@^3.0.0:
hash.js@1.1.3:
version "1.1.3"
- resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846"
+ resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz"
integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==
dependencies:
inherits "^2.0.3"
@@ -3452,7 +3422,7 @@ hash.js@1.1.3:
hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7:
version "1.1.7"
- resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+ resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
@@ -3460,17 +3430,17 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7:
he@1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+ resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
"heap@>= 0.2.0":
version "0.2.7"
- resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
+ resolved "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz"
integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
hmac-drbg@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+ resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz"
integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==
dependencies:
hash.js "^1.0.3"
@@ -3479,7 +3449,7 @@ hmac-drbg@^1.0.1:
http-basic@^8.1.1:
version "8.1.3"
- resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf"
+ resolved "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz"
integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==
dependencies:
caseless "^0.12.0"
@@ -3494,7 +3464,7 @@ http-cache-semantics@^4.1.1:
http-errors@2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
@@ -3505,14 +3475,14 @@ http-errors@2.0.0:
http-response-object@^3.0.1:
version "3.0.2"
- resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810"
+ resolved "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz"
integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==
dependencies:
"@types/node" "^10.0.3"
http-signature@~1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+ resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz"
integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==
dependencies:
assert-plus "^1.0.0"
@@ -3529,7 +3499,7 @@ http2-wrapper@^2.1.10:
https-proxy-agent@^5.0.0:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
dependencies:
agent-base "6"
@@ -3537,44 +3507,44 @@ https-proxy-agent@^5.0.0:
human-signals@^3.0.1:
version "3.0.1"
- resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5"
+ resolved "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz"
integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==
husky@>=6:
version "8.0.2"
- resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.2.tgz#5816a60db02650f1f22c8b69b928fd6bcd77a236"
+ resolved "https://registry.npmjs.org/husky/-/husky-8.0.2.tgz"
integrity sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
ieee754@^1.2.1:
version "1.2.1"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore@^4.0.6:
version "4.0.6"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+ resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
ignore@^5.1.1, ignore@^5.2.0:
version "5.2.1"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c"
+ resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz"
integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==
immutable@^4.0.0-rc.12:
version "4.1.0"
- resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
+ resolved "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz"
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
import-fresh@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+ resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz"
integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==
dependencies:
caller-path "^2.0.0"
@@ -3582,7 +3552,7 @@ import-fresh@^2.0.0:
import-fresh@^3.0.0, import-fresh@^3.2.1:
version "3.3.0"
- resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz"
integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
dependencies:
parent-module "^1.0.0"
@@ -3590,17 +3560,17 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
imurmurhash@^0.1.4:
version "0.1.4"
- resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
indent-string@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+ resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
inflight@^1.0.4:
version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
@@ -3608,17 +3578,17 @@ inflight@^1.0.4:
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
version "2.0.4"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@^1.3.5:
version "1.3.8"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
inquirer@^6.2.2:
version "6.5.2"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca"
+ resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz"
integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==
dependencies:
ansi-escapes "^3.2.0"
@@ -3637,7 +3607,7 @@ inquirer@^6.2.2:
internal-slot@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
+ resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz"
integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==
dependencies:
get-intrinsic "^1.1.0"
@@ -3646,134 +3616,160 @@ internal-slot@^1.0.3:
interpret@^1.0.0:
version "1.4.0"
- resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
+ resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
io-ts@1.10.4:
version "1.10.4"
- resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2"
+ resolved "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz"
integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==
dependencies:
fp-ts "^1.0.0"
is-arrayish@^0.2.1:
version "0.2.1"
- resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
is-bigint@^1.0.1:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
+ resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz"
integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
dependencies:
has-bigints "^1.0.1"
is-binary-path@~2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+ resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-boolean-object@^1.1.0:
version "1.1.2"
- resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
+ resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz"
integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
dependencies:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
+is-buffer@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
is-buffer@^2.0.5:
version "2.0.5"
- resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
+ resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
is-callable@^1.1.4, is-callable@^1.2.7:
version "1.2.7"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+ resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz"
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
is-core-module@^2.11.0, is-core-module@^2.8.1, is-core-module@^2.9.0:
version "2.11.0"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
+ resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz"
integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==
dependencies:
has "^1.0.3"
is-date-object@^1.0.1:
version "1.0.5"
- resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
+ resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz"
integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
dependencies:
has-tostringtag "^1.0.0"
is-directory@^0.3.1:
version "0.3.1"
- resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+ resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz"
integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==
+is-even@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-even/-/is-even-1.0.0.tgz#76b5055fbad8d294a86b6a949015e1c97b717c06"
+ integrity sha512-LEhnkAdJqic4Dbqn58A0y52IXoHWlsueqQkKfMfdEnIYG8A1sm/GHidKkS6yvXlMoRrkM34csHnXQtOqcb+Jzg==
+ dependencies:
+ is-odd "^0.1.2"
+
is-extglob@^2.1.1:
version "2.1.1"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz"
integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-fullwidth-code-point@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88"
+ resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz"
integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-hex-prefixed@1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
+ resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz"
integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==
is-negative-zero@^2.0.2:
version "2.0.2"
- resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
+ resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz"
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
is-number-object@^1.0.4:
version "1.0.7"
- resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
+ resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz"
integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
dependencies:
has-tostringtag "^1.0.0"
+is-number@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==
+ dependencies:
+ kind-of "^3.0.2"
+
is-number@^7.0.0:
version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+is-odd@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7"
+ integrity sha512-Ri7C2K7o5IrUU9UEI8losXJCCD/UtsaIrkR5sxIcFg4xQ9cRJXlWA5DQvTE0yDc0krvSNLsRGXN11UPS6KyfBw==
+ dependencies:
+ is-number "^3.0.0"
+
is-path-inside@^3.0.3:
version "3.0.3"
- resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+ resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-plain-obj@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+ resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
is-regex@^1.1.4:
version "1.1.4"
- resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+ resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz"
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
dependencies:
call-bind "^1.0.2"
@@ -3781,85 +3777,85 @@ is-regex@^1.1.4:
is-shared-array-buffer@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
+ resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz"
integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
dependencies:
call-bind "^1.0.2"
is-stream@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
+ resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz"
integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
is-string@^1.0.5, is-string@^1.0.7:
version "1.0.7"
- resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+ resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
dependencies:
has-tostringtag "^1.0.0"
is-symbol@^1.0.2, is-symbol@^1.0.3:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
+ resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz"
integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
dependencies:
has-symbols "^1.0.2"
is-typedarray@~1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+ resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
is-unicode-supported@^0.1.0:
version "0.1.0"
- resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+ resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
is-weakref@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
+ resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz"
integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
dependencies:
call-bind "^1.0.2"
isarray@~1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
isexe@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isstream@~0.1.2:
version "0.1.2"
- resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+ resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz"
integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
js-sdsl@^4.1.4:
version "4.2.0"
- resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0"
+ resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz"
integrity sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==
js-sha3@0.5.7:
version "0.5.7"
- resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
+ resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz"
integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==
js-sha3@0.8.0, js-sha3@^0.8.0:
version "0.8.0"
- resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
+ resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
js-tokens@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@3.13.1:
version "3.13.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
dependencies:
argparse "^1.0.7"
@@ -3867,7 +3863,7 @@ js-yaml@3.13.1:
js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1:
version "3.14.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz"
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
dependencies:
argparse "^1.0.7"
@@ -3875,14 +3871,14 @@ js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1:
js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
jsbn@~0.1.0:
version "0.1.1"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+ resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz"
integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
json-buffer@3.0.1:
@@ -3892,17 +3888,17 @@ json-buffer@3.0.1:
json-parse-better-errors@^1.0.1:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+ resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
json-schema-traverse@^0.4.1:
version "0.4.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
json-schema@0.4.0, json-schema@>=0.4.0:
@@ -3912,12 +3908,12 @@ json-schema@0.4.0, json-schema@>=0.4.0:
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
json-stringify-safe@~5.0.1:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
json5@>=1.0.2, json5@^1.0.1:
@@ -3927,21 +3923,21 @@ json5@>=1.0.2, json5@^1.0.1:
jsonfile@^2.1.0:
version "2.4.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
+ resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz"
integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz"
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^6.0.1:
version "6.1.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+ resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
@@ -3950,12 +3946,12 @@ jsonfile@^6.0.1:
jsonschema@^1.2.4:
version "1.4.1"
- resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab"
+ resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz"
integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==
jsprim@^1.2.2:
version "1.4.2"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
+ resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz"
integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==
dependencies:
assert-plus "1.0.0"
@@ -3965,7 +3961,7 @@ jsprim@^1.2.2:
keccak@^3.0.0, keccak@^3.0.2:
version "3.0.2"
- resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0"
+ resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz"
integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==
dependencies:
node-addon-api "^2.0.0"
@@ -3979,26 +3975,33 @@ keyv@^4.5.2:
dependencies:
json-buffer "3.0.1"
+kind-of@^3.0.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==
+ dependencies:
+ is-buffer "^1.1.5"
+
kind-of@^6.0.2:
version "6.0.3"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
klaw@^1.0.0:
version "1.3.1"
- resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
+ resolved "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz"
integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==
optionalDependencies:
graceful-fs "^4.1.9"
level-supports@^4.0.0:
version "4.0.1"
- resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a"
+ resolved "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz"
integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==
level-transcoder@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c"
+ resolved "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz"
integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==
dependencies:
buffer "^6.0.3"
@@ -4006,7 +4009,7 @@ level-transcoder@^1.0.1:
level@^8.0.0:
version "8.0.0"
- resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394"
+ resolved "https://registry.npmjs.org/level/-/level-8.0.0.tgz"
integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==
dependencies:
browser-level "^1.0.1"
@@ -4014,7 +4017,7 @@ level@^8.0.0:
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
- resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz"
integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==
dependencies:
prelude-ls "~1.1.2"
@@ -4022,7 +4025,7 @@ levn@^0.3.0, levn@~0.3.0:
levn@^0.4.1:
version "0.4.1"
- resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz"
integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
dependencies:
prelude-ls "^1.2.1"
@@ -4030,12 +4033,12 @@ levn@^0.4.1:
lilconfig@2.0.6:
version "2.0.6"
- resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4"
+ resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz"
integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==
lint-staged@>=10:
version "13.1.0"
- resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.1.0.tgz#d4c61aec939e789e489fa51987ec5207b50fd37e"
+ resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz"
integrity sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==
dependencies:
cli-truncate "^3.1.0"
@@ -4054,7 +4057,7 @@ lint-staged@>=10:
listr2@^5.0.5:
version "5.0.6"
- resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.6.tgz#3c61153383869ffaad08a8908d63edfde481dff8"
+ resolved "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz"
integrity sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==
dependencies:
cli-truncate "^2.1.0"
@@ -4068,7 +4071,7 @@ listr2@^5.0.5:
locate-path@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz"
integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==
dependencies:
p-locate "^2.0.0"
@@ -4076,7 +4079,7 @@ locate-path@^2.0.0:
locate-path@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz"
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
dependencies:
p-locate "^3.0.0"
@@ -4084,24 +4087,24 @@ locate-path@^3.0.0:
locate-path@^6.0.0:
version "6.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz"
integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
dependencies:
p-locate "^5.0.0"
lodash.camelcase@^4.3.0:
version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+ resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz"
integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
lodash.merge@^4.6.2:
version "4.6.2"
- resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.truncate@^4.4.2:
version "4.4.2"
- resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
+ resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz"
integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
lodash@>=4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21:
@@ -4111,14 +4114,14 @@ lodash@>=4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.1
log-symbols@3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
+ resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz"
integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==
dependencies:
chalk "^2.4.2"
log-symbols@4.1.0:
version "4.1.0"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+ resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz"
integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
dependencies:
chalk "^4.1.0"
@@ -4126,7 +4129,7 @@ log-symbols@4.1.0:
log-update@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+ resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz"
integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
dependencies:
ansi-escapes "^4.3.0"
@@ -4136,7 +4139,7 @@ log-update@^4.0.0:
loupe@^2.3.1:
version "2.3.6"
- resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53"
+ resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz"
integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==
dependencies:
get-func-name "^2.0.0"
@@ -4148,41 +4151,41 @@ lowercase-keys@^3.0.0:
lru-cache@^5.1.1:
version "5.1.1"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+ resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz"
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
dependencies:
yallist "^3.0.2"
lru-cache@^6.0.0:
version "6.0.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
lru_map@^0.3.3:
version "0.3.3"
- resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
+ resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz"
integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==
make-error@^1.1.1:
version "1.3.6"
- resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+ resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
markdown-table@^1.1.3:
version "1.1.3"
- resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60"
+ resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz"
integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==
mcl-wasm@^0.7.1:
version "0.7.9"
- resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f"
+ resolved "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz"
integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==
md5.js@^1.3.4:
version "1.3.5"
- resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+ resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz"
integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
dependencies:
hash-base "^3.0.0"
@@ -4191,7 +4194,7 @@ md5.js@^1.3.4:
memory-level@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692"
+ resolved "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz"
integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==
dependencies:
abstract-level "^1.0.0"
@@ -4200,22 +4203,22 @@ memory-level@^1.0.0:
memorystream@^0.3.1:
version "0.3.1"
- resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2"
+ resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz"
integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==
merge-stream@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
- resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
merkletreejs@^0.3.9:
version "0.3.9"
- resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.3.9.tgz#cdb364a3b974a44f4eff3446522d7066e0cf95de"
+ resolved "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.3.9.tgz"
integrity sha512-NjlATjJr4NEn9s8v/VEHhgwRWaE1eA/Une07d9SEqKzULJi1Wsh0Y3svwJdP2bYLMmgSBHzOrNydMWM1NN9VeQ==
dependencies:
bignumber.js "^9.0.1"
@@ -4226,7 +4229,7 @@ merkletreejs@^0.3.9:
micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.2"
@@ -4234,29 +4237,29 @@ micromatch@^4.0.4, micromatch@^4.0.5:
mime-db@1.52.0:
version "1.52.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.35"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mimic-fn@^1.0.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+ resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz"
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
mimic-fn@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-fn@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
+ resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz"
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
mimic-response@^3.1.0:
@@ -4271,25 +4274,25 @@ mimic-response@^4.0.0:
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+ resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+ resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz"
integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
-"minimatch@2 || 3", minimatch@3.0.4, minimatch@5.0.1, minimatch@>=3.0.5, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.1.tgz#6c9dffcf9927ff2a31e74b5af11adf8b9604b022"
- integrity sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==
+"minimatch@2 || 3", minimatch@3.0.4, minimatch@5.0.1, minimatch@>=3.0.5, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2:
+ version "7.4.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f"
+ integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==
dependencies:
brace-expansion "^2.0.1"
minimist@>=1.2.6, minimist@^1.2.5, minimist@^1.2.6:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
- integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
minipass@^3.0.0:
version "3.3.6"
@@ -4298,6 +4301,11 @@ minipass@^3.0.0:
dependencies:
yallist "^4.0.0"
+minipass@^4.0.0:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06"
+ integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==
+
minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
@@ -4308,14 +4316,14 @@ minizlib@^2.1.1:
mkdirp@0.5.5:
version "0.5.5"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+ resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
mkdirp@0.5.x, mkdirp@^0.5.1:
version "0.5.6"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
+ resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
dependencies:
minimist "^1.2.6"
@@ -4327,14 +4335,14 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
mnemonist@^0.38.0:
version "0.38.5"
- resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade"
+ resolved "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz"
integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==
dependencies:
obliterator "^2.0.0"
mocha@7.1.2:
version "7.1.2"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6"
+ resolved "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz"
integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==
dependencies:
ansi-colors "3.2.3"
@@ -4364,7 +4372,7 @@ mocha@7.1.2:
mocha@^10.0.0:
version "10.1.0"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.1.0.tgz#dbf1114b7c3f9d0ca5de3133906aea3dfc89ef7a"
+ resolved "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz"
integrity sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==
dependencies:
ansi-colors "4.1.1"
@@ -4391,7 +4399,7 @@ mocha@^10.0.0:
mocha@^7.1.1:
version "7.2.0"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604"
+ resolved "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz"
integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==
dependencies:
ansi-colors "3.2.3"
@@ -4421,67 +4429,67 @@ mocha@^7.1.1:
module-error@^1.0.1, module-error@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86"
+ resolved "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz"
integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==
ms@2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.1:
version "2.1.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-ms@2.1.2:
+ms@2.1.2, ms@^2.1.1:
version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-ms@2.1.3, ms@^2.1.1:
+ms@2.1.3:
version "2.1.3"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
mute-stream@0.0.7:
version "0.0.7"
- resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+ resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz"
integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==
nanoid@3.3.3:
version "3.3.3"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
+ resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz"
integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
napi-macros@~2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b"
+ resolved "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz"
integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==
natural-compare-lite@^1.4.0:
version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
+ resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz"
integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
natural-compare@^1.4.0:
version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
neo-async@^2.6.0:
version "2.6.2"
- resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
nice-try@^1.0.4:
version "1.0.5"
- resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+ resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
node-addon-api@^2.0.0:
version "2.0.2"
- resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
+ resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-domexception@^1.0.0:
@@ -4491,14 +4499,14 @@ node-domexception@^1.0.0:
node-emoji@^1.10.0:
version "1.11.0"
- resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c"
+ resolved "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz"
integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==
dependencies:
lodash "^4.17.21"
node-environment-flags@1.0.6:
version "1.0.6"
- resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
+ resolved "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz"
integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==
dependencies:
object.getownpropertydescriptors "^2.0.3"
@@ -4515,24 +4523,24 @@ node-fetch@2.6.7, node-fetch@>=2.6.7:
node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
version "4.5.0"
- resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
+ resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz"
integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==
nofilter@^3.1.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66"
+ resolved "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz"
integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==
nopt@3.x:
version "3.0.6"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+ resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz"
integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==
dependencies:
abbrev "1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-url@>=4.5.1, normalize-url@^8.0.0:
@@ -4542,14 +4550,14 @@ normalize-url@>=4.5.1, normalize-url@^8.0.0:
npm-run-path@^5.1.0:
version "5.1.0"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00"
+ resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz"
integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==
dependencies:
path-key "^4.0.0"
number-to-bn@1.7.0:
version "1.7.0"
- resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0"
+ resolved "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz"
integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==
dependencies:
bn.js "4.11.6"
@@ -4557,27 +4565,27 @@ number-to-bn@1.7.0:
oauth-sign@~0.9.0:
version "0.9.0"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+ resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-assign@^4.1.0:
version "4.1.1"
- resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.12.2, object-inspect@^1.9.0:
version "1.12.2"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
+ resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz"
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
object-keys@^1.0.11, object-keys@^1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@4.1.0:
version "4.1.0"
- resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+ resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz"
integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
dependencies:
define-properties "^1.1.2"
@@ -4587,7 +4595,7 @@ object.assign@4.1.0:
object.assign@^4.1.4:
version "4.1.4"
- resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
+ resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz"
integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
dependencies:
call-bind "^1.0.2"
@@ -4597,7 +4605,7 @@ object.assign@^4.1.4:
object.getownpropertydescriptors@^2.0.3:
version "2.1.5"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3"
+ resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz"
integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==
dependencies:
array.prototype.reduce "^1.0.5"
@@ -4607,7 +4615,7 @@ object.getownpropertydescriptors@^2.0.3:
object.values@^1.1.5:
version "1.1.6"
- resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d"
+ resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz"
integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==
dependencies:
call-bind "^1.0.2"
@@ -4616,7 +4624,7 @@ object.values@^1.1.5:
obliterator@^2.0.0:
version "2.0.4"
- resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816"
+ resolved "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz"
integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==
once@1.x, once@^1.3.0, once@^1.3.1:
@@ -4628,28 +4636,28 @@ once@1.x, once@^1.3.0, once@^1.3.1:
onetime@^2.0.0:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz"
integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==
dependencies:
mimic-fn "^1.0.0"
onetime@^5.1.0:
version "5.1.2"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
onetime@^6.0.0:
version "6.0.0"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
+ resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz"
integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
dependencies:
mimic-fn "^4.0.0"
optionator@^0.8.1, optionator@^0.8.2:
version "0.8.3"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
+ resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz"
integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
dependencies:
deep-is "~0.1.3"
@@ -4661,7 +4669,7 @@ optionator@^0.8.1, optionator@^0.8.2:
optionator@^0.9.1:
version "0.9.1"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz"
integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
dependencies:
deep-is "^0.1.3"
@@ -4673,12 +4681,12 @@ optionator@^0.9.1:
ordinal@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d"
+ resolved "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz"
integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==
os-tmpdir@~1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz"
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
p-cancelable@^3.0.0:
@@ -4688,78 +4696,78 @@ p-cancelable@^3.0.0:
p-limit@^1.1.0:
version "1.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+ resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz"
integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
dependencies:
p-try "^1.0.0"
p-limit@^2.0.0:
version "2.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-limit@^3.0.2:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
p-locate@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz"
integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==
dependencies:
p-limit "^1.1.0"
p-locate@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz"
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
dependencies:
p-limit "^2.0.0"
p-locate@^5.0.0:
version "5.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz"
integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
dependencies:
p-limit "^3.0.2"
p-map@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
+ resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz"
integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
dependencies:
aggregate-error "^3.0.0"
p-try@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+ resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz"
integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==
p-try@^2.0.0:
version "2.2.0"
- resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
parent-module@^1.0.0:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
dependencies:
callsites "^3.0.0"
parse-cache-control@^1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e"
+ resolved "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz"
integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==
parse-json@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz"
integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==
dependencies:
error-ex "^1.3.1"
@@ -4767,37 +4775,37 @@ parse-json@^4.0.0:
path-exists@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz"
integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
path-exists@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
path-is-inside@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+ resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz"
integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==
path-key@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+ resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz"
integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
path-key@^3.1.0:
version "3.1.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-key@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
+ resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz"
integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
path-parse@>=1.0.7, path-parse@^1.0.6, path-parse@^1.0.7:
@@ -4807,17 +4815,17 @@ path-parse@>=1.0.7, path-parse@^1.0.6, path-parse@^1.0.7:
path-type@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pathval@^1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
+ resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz"
integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
pbkdf2@^3.0.17:
version "3.1.2"
- resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
+ resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz"
integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
dependencies:
create-hash "^1.1.2"
@@ -4828,44 +4836,44 @@ pbkdf2@^3.0.17:
performance-now@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+ resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pidtree@^0.6.0:
version "0.6.0"
- resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
+ resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz"
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
pify@^4.0.1:
version "4.0.1"
- resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
prelude-ls@^1.2.1:
version "1.2.1"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prelude-ls@~1.1.2:
version "1.1.2"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+ resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
prettier-linter-helpers@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz"
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
dependencies:
fast-diff "^1.1.2"
prettier-plugin-solidity@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.0.tgz#a417d104b48a43af3adbfb96b65dbce34fd21429"
+ resolved "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.0.tgz"
integrity sha512-5gq0T49ifvXH/6x1STuKyWjTUgi6ICoV65yNtKlg/vZEvocFtSpByJOJICBfqPwNsnv4vhhWIqkLGSUJmWum2w==
dependencies:
"@solidity-parser/parser" "^0.14.5"
@@ -4877,56 +4885,56 @@ prettier-plugin-solidity@^1.1.0:
prettier@^1.14.3:
version "1.19.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
+ resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
prettier@^2.3.1, prettier@^2.5.1:
version "2.8.0"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9"
+ resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz"
integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==
process-nextick-args@~2.0.0:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+ resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@^2.0.0:
version "2.0.3"
- resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
promise@^8.0.0:
version "8.3.0"
- resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
+ resolved "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz"
integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==
dependencies:
asap "~2.0.6"
psl@^1.1.28:
version "1.9.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
+ resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@^6.4.0, qs@^6.7.0:
version "6.11.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
+ resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
qs@~6.5.2:
version "6.5.3"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
+ resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz"
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
queue-microtask@^1.2.2, queue-microtask@^1.2.3:
version "1.2.3"
- resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quick-lru@^5.1.1:
@@ -4936,14 +4944,14 @@ quick-lru@^5.1.1:
randombytes@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
raw-body@^2.4.1:
version "2.5.1"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
+ resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
@@ -4953,7 +4961,7 @@ raw-body@^2.4.1:
readable-stream@^2.2.2:
version "2.3.7"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+ resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
@@ -4966,7 +4974,7 @@ readable-stream@^2.2.2:
readable-stream@^3.6.0:
version "3.6.0"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
@@ -4975,40 +4983,40 @@ readable-stream@^3.6.0:
readdirp@~3.2.0:
version "3.2.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839"
+ resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz"
integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==
dependencies:
picomatch "^2.0.4"
readdirp@~3.6.0:
version "3.6.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
rechoir@^0.6.2:
version "0.6.2"
- resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
+ resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz"
integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==
dependencies:
resolve "^1.1.6"
recursive-readdir@^2.2.2:
version "2.2.3"
- resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372"
+ resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz"
integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==
dependencies:
minimatch "^3.0.5"
reduce-flatten@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27"
+ resolved "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz"
integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==
regexp.prototype.flags@^1.4.3:
version "1.4.3"
- resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
+ resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz"
integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
dependencies:
call-bind "^1.0.2"
@@ -5017,38 +5025,38 @@ regexp.prototype.flags@^1.4.3:
regexpp@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
+ resolved "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz"
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
regexpp@^3.0.0, regexpp@^3.2.0:
version "3.2.0"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
req-cwd@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc"
+ resolved "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz"
integrity sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==
dependencies:
req-from "^2.0.0"
req-from@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/req-from/-/req-from-2.0.0.tgz#d74188e47f93796f4aa71df6ee35ae689f3e0e70"
+ resolved "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz"
integrity sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==
dependencies:
resolve-from "^3.0.0"
request-promise-core@1.1.4:
version "1.1.4"
- resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
+ resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz"
integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
dependencies:
lodash "^4.17.19"
request-promise-native@^1.0.5:
version "1.0.9"
- resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
+ resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz"
integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
dependencies:
request-promise-core "1.1.4"
@@ -5057,7 +5065,7 @@ request-promise-native@^1.0.5:
request@^2.88.0:
version "2.88.2"
- resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+ resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
dependencies:
aws-sign2 "~0.7.0"
@@ -5083,17 +5091,17 @@ request@^2.88.0:
require-directory@^2.1.1:
version "2.1.1"
- resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
require-from-string@^2.0.0, require-from-string@^2.0.2:
version "2.0.2"
- resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-main-filename@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
resolve-alpn@^1.2.0:
@@ -5103,29 +5111,29 @@ resolve-alpn@^1.2.0:
resolve-from@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz"
integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==
resolve-from@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@1.1.x:
version "1.1.7"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+ resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz"
integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==
resolve@1.17.0:
version "1.17.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
+ resolved "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies:
path-parse "^1.0.6"
resolve@^1.1.6, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1:
version "1.22.1"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+ resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.9.0"
@@ -5141,7 +5149,7 @@ responselike@^3.0.0:
restore-cursor@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz"
integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==
dependencies:
onetime "^2.0.0"
@@ -5149,7 +5157,7 @@ restore-cursor@^2.0.0:
restore-cursor@^3.1.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz"
integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
dependencies:
onetime "^5.1.0"
@@ -5157,38 +5165,38 @@ restore-cursor@^3.1.0:
reusify@^1.0.4:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rfdc@^1.3.0:
version "1.3.0"
- resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
+ resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz"
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
rimraf@2.6.3:
version "2.6.3"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz"
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
dependencies:
glob "^7.1.3"
rimraf@^2.2.8:
version "2.7.1"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
rimraf@^3.0.2:
version "3.0.2"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
- resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+ resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz"
integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
dependencies:
hash-base "^3.0.0"
@@ -5196,62 +5204,62 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
rlp@^2.2.3, rlp@^2.2.4:
version "2.2.7"
- resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf"
+ resolved "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz"
integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==
dependencies:
bn.js "^5.2.0"
run-async@^2.2.0:
version "2.4.1"
- resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
+ resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz"
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
run-parallel-limit@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba"
+ resolved "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz"
integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==
dependencies:
queue-microtask "^1.2.2"
run-parallel@^1.1.9:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
dependencies:
queue-microtask "^1.2.2"
rustbn.js@~0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca"
+ resolved "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz"
integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==
rxjs@^6.4.0:
version "6.6.7"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
+ resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz"
integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
dependencies:
tslib "^1.9.0"
rxjs@^7.5.7:
version "7.6.0"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2"
+ resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz"
integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==
dependencies:
tslib "^2.1.0"
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
version "5.2.1"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-regex-test@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295"
+ resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz"
integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==
dependencies:
call-bind "^1.0.2"
@@ -5260,12 +5268,12 @@ safe-regex-test@^1.0.0:
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
- resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sc-istanbul@^0.4.5:
version "0.4.6"
- resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839"
+ resolved "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz"
integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==
dependencies:
abbrev "1.0.x"
@@ -5285,22 +5293,22 @@ sc-istanbul@^0.4.5:
scrypt-js@2.0.4:
version "2.0.4"
- resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16"
+ resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz"
integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==
scrypt-js@3.0.1, scrypt-js@^3.0.0:
version "3.0.1"
- resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312"
+ resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz"
integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==
scuffed-abi@^1.0.4:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/scuffed-abi/-/scuffed-abi-1.0.4.tgz#bc88129877856de5026d8afaea49de9a1972b6cf"
+ resolved "https://registry.npmjs.org/scuffed-abi/-/scuffed-abi-1.0.4.tgz"
integrity sha512-1NN2L1j+TMF6+/J2jHcAnhPH8Lwaqu5dlgknZPqejEVFQ8+cvcnXYNbaHtGEXTjSNrQLBGePXicD4oFGqecOnQ==
secp256k1@^4.0.1:
version "4.0.3"
- resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303"
+ resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz"
integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==
dependencies:
elliptic "^6.5.4"
@@ -5309,51 +5317,51 @@ secp256k1@^4.0.1:
semver@^5.5.0, semver@^5.5.1, semver@^5.7.0:
version "5.7.1"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+ resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@^6.3.0:
version "6.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.0.0, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8:
version "7.3.8"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
+ resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
serialize-javascript@6.0.0:
version "6.0.0"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+ resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz"
integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
dependencies:
randombytes "^2.1.0"
set-blocking@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
setimmediate@1.0.4:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f"
+ resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz"
integrity sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==
setimmediate@^1.0.5:
version "1.0.5"
- resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+ resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz"
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
setprototypeof@1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
sha.js@^2.4.0, sha.js@^2.4.8:
version "2.4.11"
- resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+ resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz"
integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
dependencies:
inherits "^2.0.1"
@@ -5361,7 +5369,7 @@ sha.js@^2.4.0, sha.js@^2.4.8:
sha1@^1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848"
+ resolved "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz"
integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==
dependencies:
charenc ">= 0.0.1"
@@ -5369,31 +5377,31 @@ sha1@^1.1.1:
shebang-command@^1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz"
integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==
dependencies:
shebang-regex "^1.0.0"
shebang-command@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+ resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz"
integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==
shebang-regex@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shelljs@^0.8.3:
version "0.8.5"
- resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
+ resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz"
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
dependencies:
glob "^7.0.0"
@@ -5402,7 +5410,7 @@ shelljs@^0.8.3:
side-channel@^1.0.4:
version "1.0.4"
- resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+ resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz"
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
dependencies:
call-bind "^1.0.0"
@@ -5411,7 +5419,7 @@ side-channel@^1.0.4:
signal-exit@^3.0.2, signal-exit@^3.0.7:
version "3.0.7"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
simple-concat@^1.0.0:
@@ -5430,12 +5438,12 @@ simple-get@>=2.8.2:
slash@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+ resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
slice-ansi@^2.1.0:
version "2.1.0"
- resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
+ resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz"
integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
dependencies:
ansi-styles "^3.2.0"
@@ -5444,7 +5452,7 @@ slice-ansi@^2.1.0:
slice-ansi@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+ resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz"
integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
dependencies:
ansi-styles "^4.0.0"
@@ -5453,7 +5461,7 @@ slice-ansi@^3.0.0:
slice-ansi@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz"
integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
dependencies:
ansi-styles "^4.0.0"
@@ -5462,15 +5470,20 @@ slice-ansi@^4.0.0:
slice-ansi@^5.0.0:
version "5.0.0"
- resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a"
+ resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz"
integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==
dependencies:
ansi-styles "^6.0.0"
is-fullwidth-code-point "^4.0.0"
+solady@^0.0.84:
+ version "0.0.84"
+ resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.84.tgz#95476df1936ef349003e88d8a4853185eb0b7267"
+ integrity sha512-1ccuZWcMR+g8Ont5LUTqMSFqrmEC+rYAbo6DjZFzdL7AJAtLiaOzO25BKZR10h6YBZNaqO5zUOFy09R6/AzAKQ==
+
solc@0.7.3:
version "0.7.3"
- resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a"
+ resolved "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz"
integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==
dependencies:
command-exists "^1.2.8"
@@ -5485,7 +5498,7 @@ solc@0.7.3:
solhint@^3.3.6:
version "3.3.7"
- resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.7.tgz#b5da4fedf7a0fee954cb613b6c55a5a2b0063aa7"
+ resolved "https://registry.npmjs.org/solhint/-/solhint-3.3.7.tgz"
integrity sha512-NjjjVmXI3ehKkb3aNtRJWw55SUVJ8HMKKodwe0HnejA+k0d2kmhw7jvpa+MCTbcEgt8IWSwx0Hu6aCo/iYOZzQ==
dependencies:
"@solidity-parser/parser" "^0.14.1"
@@ -5507,12 +5520,12 @@ solhint@^3.3.6:
solidity-comments-extractor@^0.0.7:
version "0.0.7"
- resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19"
+ resolved "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz"
integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==
solidity-coverage@^0.8.2:
version "0.8.2"
- resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.2.tgz#bc39604ab7ce0a3fa7767b126b44191830c07813"
+ resolved "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz"
integrity sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==
dependencies:
"@ethersproject/abi" "^5.0.9"
@@ -5538,7 +5551,7 @@ solidity-coverage@^0.8.2:
source-map-support@^0.5.13:
version "0.5.21"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
@@ -5546,24 +5559,24 @@ source-map-support@^0.5.13:
source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@~0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
+ resolved "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz"
integrity sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==
dependencies:
amdefine ">=0.0.4"
sprintf-js@~1.0.2:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
sshpk@^1.7.0:
version "1.17.0"
- resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
+ resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz"
integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==
dependencies:
asn1 "~0.2.3"
@@ -5578,39 +5591,39 @@ sshpk@^1.7.0:
stacktrace-parser@^0.1.10:
version "0.1.10"
- resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a"
+ resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz"
integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==
dependencies:
type-fest "^0.7.1"
statuses@2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
stealthy-require@^1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+ resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz"
integrity sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==
streamsearch@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
+ resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
string-argv@^0.3.1:
version "0.3.1"
- resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
+ resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz"
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
string-format@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b"
+ resolved "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz"
integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==
"string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
@@ -5618,7 +5631,7 @@ string-format@^2.0.0:
string-width@^3.0.0, string-width@^3.1.0:
version "3.1.0"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+ resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz"
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
dependencies:
emoji-regex "^7.0.1"
@@ -5627,7 +5640,7 @@ string-width@^3.0.0, string-width@^3.1.0:
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
@@ -5636,7 +5649,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
string-width@^5.0.0:
version "5.1.2"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+ resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz"
integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
dependencies:
eastasianwidth "^0.2.0"
@@ -5645,7 +5658,7 @@ string-width@^5.0.0:
string.prototype.trimend@^1.0.5:
version "1.0.6"
- resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
+ resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz"
integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==
dependencies:
call-bind "^1.0.2"
@@ -5654,7 +5667,7 @@ string.prototype.trimend@^1.0.5:
string.prototype.trimstart@^1.0.5:
version "1.0.6"
- resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4"
+ resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz"
integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==
dependencies:
call-bind "^1.0.2"
@@ -5663,116 +5676,116 @@ string.prototype.trimstart@^1.0.5:
string_decoder@^1.1.1:
version "1.3.0"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz"
integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==
dependencies:
ansi-regex "^3.0.0"
strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
version "5.2.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+ resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz"
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
+ resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz"
integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
dependencies:
ansi-regex "^6.0.1"
strip-bom@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz"
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
strip-final-newline@^3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
+ resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz"
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
strip-hex-prefix@1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f"
+ resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz"
integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==
dependencies:
is-hex-prefixed "1.0.0"
strip-json-comments@2.0.1, strip-json-comments@^2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+ resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz"
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
- resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
supports-color@6.0.0:
version "6.0.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz"
integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==
dependencies:
has-flag "^3.0.0"
supports-color@8.1.1:
version "8.1.1"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
supports-color@^3.1.0:
version "3.2.3"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz"
integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==
dependencies:
has-flag "^1.0.0"
supports-color@^5.3.0:
version "5.5.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0:
version "7.2.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
sync-request@^6.0.0:
version "6.1.0"
- resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68"
+ resolved "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz"
integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==
dependencies:
http-response-object "^3.0.1"
@@ -5781,14 +5794,14 @@ sync-request@^6.0.0:
sync-rpc@^1.2.1:
version "1.3.6"
- resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7"
+ resolved "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz"
integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==
dependencies:
get-port "^3.1.0"
table-layout@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04"
+ resolved "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz"
integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==
dependencies:
array-back "^4.0.1"
@@ -5798,7 +5811,7 @@ table-layout@^1.0.2:
table@^5.2.3:
version "5.4.6"
- resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
+ resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz"
integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
dependencies:
ajv "^6.10.2"
@@ -5808,7 +5821,7 @@ table@^5.2.3:
table@^6.8.0:
version "6.8.1"
- resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf"
+ resolved "https://registry.npmjs.org/table/-/table-6.8.1.tgz"
integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==
dependencies:
ajv "^8.0.1"
@@ -5818,25 +5831,30 @@ table@^6.8.0:
strip-ansi "^6.0.1"
tar@>=4.4.18:
- version "6.1.12"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6"
- integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==
+ version "6.1.13"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b"
+ integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
- minipass "^3.0.0"
+ minipass "^4.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
+term-size@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
+ integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
+
text-table@^0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
then-request@^6.0.0:
version "6.0.2"
- resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c"
+ resolved "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz"
integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==
dependencies:
"@types/concat-stream" "^1.6.0"
@@ -5853,31 +5871,31 @@ then-request@^6.0.0:
through@^2.3.6, through@^2.3.8:
version "2.3.8"
- resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
tmp@0.0.33, tmp@^0.0.33:
version "0.0.33"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz"
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
dependencies:
os-tmpdir "~1.0.2"
to-regex-range@^5.0.1:
version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
toidentifier@1.0.1:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
tough-cookie@^2.3.3, tough-cookie@~2.5.0:
version "2.5.0"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+ resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
dependencies:
psl "^1.1.28"
@@ -5885,12 +5903,12 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0:
treeify@^1.1.0:
version "1.1.0"
- resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8"
+ resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz"
integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==
ts-command-line-args@^2.2.0:
version "2.3.1"
- resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz#b6188e42efc6cf7a8898e438a873fbb15505ddd6"
+ resolved "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz"
integrity sha512-FR3y7pLl/fuUNSmnPhfLArGqRrpojQgIEEOVzYx9DhTmfIN7C9RWSfpkJEF4J+Gk7aVx5pak8I7vWZsaN4N84g==
dependencies:
chalk "^4.1.0"
@@ -5900,12 +5918,12 @@ ts-command-line-args@^2.2.0:
ts-essentials@^7.0.1:
version "7.0.3"
- resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38"
+ resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz"
integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==
ts-node@^10.4.0:
version "10.9.1"
- resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
+ resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
@@ -5924,7 +5942,7 @@ ts-node@^10.4.0:
tsconfig-paths@^3.14.1:
version "3.14.1"
- resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
+ resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz"
integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==
dependencies:
"@types/json5" "^0.0.29"
@@ -5934,85 +5952,85 @@ tsconfig-paths@^3.14.1:
tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+ resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.1.0:
version "2.4.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
+ resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz"
integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
tsort@0.0.1:
version "0.0.1"
- resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786"
+ resolved "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz"
integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==
tsutils@^3.21.0:
version "3.21.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+ resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz"
integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
dependencies:
tslib "^1.8.1"
tunnel-agent@^0.6.0:
version "0.6.0"
- resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz"
integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
dependencies:
safe-buffer "^5.0.1"
tweetnacl-util@^0.15.1:
version "0.15.1"
- resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b"
+ resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz"
integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+ resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz"
integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==
tweetnacl@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
+ resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz"
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
- resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz"
integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
dependencies:
prelude-ls "^1.2.1"
type-check@~0.3.2:
version "0.3.2"
- resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz"
integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==
dependencies:
prelude-ls "~1.1.2"
type-detect@^4.0.0, type-detect@^4.0.5:
version "4.0.8"
- resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
+ resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@^0.20.2:
version "0.20.2"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.21.3:
version "0.21.3"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+ resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
type-fest@^0.7.1:
version "0.7.1"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
+ resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz"
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
typechain@^8.0.0:
version "8.1.1"
- resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.1.1.tgz#9c2e8012c2c4c586536fc18402dcd7034c4ff0bd"
+ resolved "https://registry.npmjs.org/typechain/-/typechain-8.1.1.tgz"
integrity sha512-uF/sUvnXTOVF2FHKhQYnxHk4su4JjZR8vr4mA2mBaRwHTbwh0jIlqARz9XJr1tA0l7afJGvEa1dTSi4zt039LQ==
dependencies:
"@types/prettier" "^2.1.1"
@@ -6028,32 +6046,32 @@ typechain@^8.0.0:
typedarray@^0.0.6:
version "0.0.6"
- resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+ resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
typescript@^4.5.4:
version "4.9.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
+ resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz"
integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==
typical@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
+ resolved "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz"
integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==
typical@^5.2.0:
version "5.2.0"
- resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"
+ resolved "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz"
integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==
uglify-js@^3.1.4:
version "3.17.4"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"
+ resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz"
integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==
unbox-primitive@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
+ resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"
integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
dependencies:
call-bind "^1.0.2"
@@ -6067,67 +6085,67 @@ underscore@>=1.12.1:
integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==
undici@>=5.8.2, undici@^5.4.0:
- version "5.19.1"
- resolved "https://registry.yarnpkg.com/undici/-/undici-5.19.1.tgz#92b1fd3ab2c089b5a6bd3e579dcda8f1934ebf6d"
- integrity sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==
+ version "5.20.0"
+ resolved "https://registry.yarnpkg.com/undici/-/undici-5.20.0.tgz#6327462f5ce1d3646bcdac99da7317f455bcc263"
+ integrity sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==
dependencies:
busboy "^1.6.0"
universalify@^0.1.0:
version "0.1.2"
- resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+ resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
+ resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unpipe@1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
uri-js@^4.2.2:
version "4.4.1"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
utf8@3.0.0:
version "3.0.0"
- resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
+ resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz"
integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
uuid@2.0.1:
version "2.0.1"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac"
+ resolved "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz"
integrity sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==
uuid@^3.3.2:
version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+ resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.3.2:
version "8.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
- resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
verror@1.10.0:
version "1.10.0"
- resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+ resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz"
integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==
dependencies:
assert-plus "^1.0.0"
@@ -6141,7 +6159,7 @@ web-streams-polyfill@^3.0.3:
web3-utils@^1.3.4, web3-utils@^1.3.6:
version "1.8.1"
- resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.8.1.tgz#f2f7ca7eb65e6feb9f3d61056d0de6bbd57125ff"
+ resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.1.tgz"
integrity sha512-LgnM9p6V7rHHUGfpMZod+NST8cRfGzJ1BTXAyNo7A9cJX9LczBfSRxJp+U/GInYe9mby40t3v22AJdlELibnsQ==
dependencies:
bn.js "^5.2.1"
@@ -6154,7 +6172,7 @@ web3-utils@^1.3.4, web3-utils@^1.3.6:
which-boxed-primitive@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
+ resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz"
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
dependencies:
is-bigint "^1.0.1"
@@ -6165,43 +6183,43 @@ which-boxed-primitive@^1.0.2:
which-module@^2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+ resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz"
integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==
which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1:
version "1.3.1"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
which@^2.0.1:
version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
wide-align@1.1.3:
version "1.1.3"
- resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+ resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz"
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
dependencies:
string-width "^1.0.2 || 2"
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
- resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
wordwrap@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+ resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
wordwrapjs@^4.0.0:
version "4.0.1"
- resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f"
+ resolved "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz"
integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==
dependencies:
reduce-flatten "^2.0.0"
@@ -6209,12 +6227,12 @@ wordwrapjs@^4.0.0:
workerpool@6.2.1:
version "6.2.1"
- resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
+ resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
wrap-ansi@^5.1.0:
version "5.1.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+ resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz"
integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
dependencies:
ansi-styles "^3.2.0"
@@ -6223,7 +6241,7 @@ wrap-ansi@^5.1.0:
wrap-ansi@^6.2.0:
version "6.2.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
@@ -6232,7 +6250,7 @@ wrap-ansi@^6.2.0:
wrap-ansi@^7.0.0:
version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
@@ -6241,50 +6259,50 @@ wrap-ansi@^7.0.0:
wrappy@1:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
write@1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
+ resolved "https://registry.npmjs.org/write/-/write-1.0.3.tgz"
integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
dependencies:
mkdirp "^0.5.1"
ws@7.4.6, ws@>=5.2.3, ws@^7.4.6:
- version "8.11.0"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
- integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
+ version "8.12.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f"
+ integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==
xmlhttprequest@1.8.0:
version "1.8.0"
- resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
+ resolved "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz"
integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==
y18n@^4.0.0:
version "4.0.3"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+ resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
y18n@^5.0.5:
version "5.0.8"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yallist@^3.0.2:
version "3.1.1"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+ resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yallist@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^2.1.3:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207"
- integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073"
+ integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==
yargs-parser@13.1.2, yargs-parser@20.2.4, yargs-parser@>=5.0.1, yargs-parser@^13.1.2, yargs-parser@^20.2.2:
version "21.1.1"
@@ -6293,7 +6311,7 @@ yargs-parser@13.1.2, yargs-parser@20.2.4, yargs-parser@>=5.0.1, yargs-parser@^13
yargs-unparser@1.6.0:
version "1.6.0"
- resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f"
+ resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz"
integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==
dependencies:
flat "^4.1.0"
@@ -6302,7 +6320,7 @@ yargs-unparser@1.6.0:
yargs-unparser@2.0.0:
version "2.0.0"
- resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
+ resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz"
integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
dependencies:
camelcase "^6.0.0"
@@ -6312,7 +6330,7 @@ yargs-unparser@2.0.0:
yargs@13.3.2, yargs@^13.3.0:
version "13.3.2"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
+ resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz"
integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
dependencies:
cliui "^5.0.0"
@@ -6328,7 +6346,7 @@ yargs@13.3.2, yargs@^13.3.0:
yargs@16.2.0:
version "16.2.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+ resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
dependencies:
cliui "^7.0.2"
@@ -6341,10 +6359,10 @@ yargs@16.2.0:
yn@3.1.1:
version "3.1.1"
- resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
yocto-queue@^0.1.0:
version "0.1.0"
- resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==